Skip to content

Users and roles

Sill dashboard users are humans who sign in to manage an Account. Each user belongs to exactly one Account, holds one of four roles — owner, admin, reviewer, viewer — and moves through a lifecycle of invited, active, suspended, deactivated. Authentication is magic-link only: Sill stores no passwords, and SSO is on the roadmap (Phase 4), not available today.

An Account is the top-level merchant org. A User is a human seat inside that Account.

FieldTypeNotes
user_idULIDStable identifier.
account_idULIDThe Account the user belongs to. One user, one Account.
emailstringSign-in identifier. Magic links are sent here.
namestringDisplay name.
roleowner | admin | reviewer | viewerSee Roles.
statusinvited | active | suspended | deactivatedSee Lifecycle.
mfa_enabledboolReserved; MFA is on the roadmap.
sso_subjectstring?Reserved; SSO is on the roadmap (Phase 4).
last_active_atISO-8601 UTCUpdated on session refresh.
created_atISO-8601 UTCTimestamp the user record was minted.

Secrets such as password hashes or MFA seeds are referenced by opaque *_ref columns and never stored inline.

Sign-in is magic-link only. The flow:

  1. The visitor enters an email address on the dashboard.
  2. The API issues a short-lived, single-use token and emails a verification link via SES. The token is hashed at rest; the raw value lives only in the email body and the verify URL.
  3. Clicking the link consumes the token atomically and mints a session cookie.

On a first-ever sign-in for an email, the API auto-creates an Account and an owner user with status = active in the same transaction. There is no separate signup form. The first owner can then bring on additional teammates as more invite tooling lands.

sequenceDiagram
  participant U as User (browser)
  participant D as Dashboard
  participant API as Sill API
  participant SES as Email (SES)

  U->>D: Enter email
  D->>API: POST /v1/auth/sign-in
  API->>SES: Send verify link (token hashed at rest)
  SES-->>U: Email with verify URL
  U->>API: GET /v1/auth/verify?token=...
  alt First sign-in for this email
    API->>API: Create Account + owner User (status=active)
  else Existing user
    API->>API: Look up User
  end
  API-->>U: Set-Cookie: sill_session
  U->>D: Authenticated session

Sessions are bound to a hashed cookie value scoped so the dashboard and marketing site share auth state. Sessions roll-refresh past the halfway point of their lifetime; expired or revoked sessions return null from the resolver. Only active users can resolve a session — invited, suspended, and deactivated users are rejected at the auth boundary.

Roles are coarse and code-enforced. Each route handler checks the caller’s role before performing a privileged action.

  • owner — full write access across the Account. Created automatically on first sign-in for a brand-new Account. Can change merchant-wide configuration such as buyer-detail retention, settlement rail, and the active policy.
  • admin — same write surface as owner in code today. The distinction exists for org clarity and future delegation (e.g. owner-only transfer of the Account, billing changes).
  • reviewer — read access plus the ability to triage the escalation queue and decide individual escalations (approve / reject / dismiss). Cannot mutate sites, policies, catalogs, or account settings.
  • viewer — read-only across the Account. Useful for analysts, auditors, and compliance reviewers who need visibility without write power.

The table below reflects what the shipping API enforces today. Where a row says “read only,” the user can fetch and view the resource through the API but cannot create, update, or delete it.

Capabilityowneradminreviewerviewer
View dashboard, sites, audit logyesyesyesyes
Create / modify sitesyesyesread onlyread only
Verify a domain (domain verification)yesyesread onlyread only
Publish or change the active policyyesyesread onlyread only
Manage the merchant catalogyesyesread onlyread only
Manage the agent-card profileyesyesread onlyread only
Manage settlement-rail configurationyesyesread onlyread only
Configure account-wide buyer-detail retentionyesyesread onlyread only
Triage / decide HITL escalationsyesyesyesread only
Export a signed audit bundleyesyesread onlyread only
Run red-team drillsyesyesnono
Reveal buyer-detail PII (gated, audited)yesyesnono

Forbidden actions return HTTP 403 with {"error":"forbidden","reason":"role_insufficient"}. The role is read from the session-bound Principal; clients cannot self-assert a role.

A user moves through four states. Only active users can sign in.

flowchart LR
  invited --> active
  active --> suspended
  active --> deactivated
  suspended --> active
  suspended --> deactivated
  • invited — the user record exists but the human has not yet completed first sign-in. Sessions are refused. (The schema supports this state today; broader invite tooling is rolling out.)
  • active — can sign in via magic link and exercise the permissions granted by their role.
  • suspended — temporarily blocked from sign-in. The record and history are preserved. Reversible.
  • deactivated — terminal off-state. Sessions refused; the user no longer counts as a seat. Audit history referencing the user remains intact (audit records are append-only and Merkle-chained).

The session resolver enforces this directly: any status other than active causes session resolution to fail, even if a prior cookie is still within its TTL.

  • Cookiesill_session, HttpOnly, Secure, SameSite=Lax, scoped so the dashboard and marketing site share auth state.
  • TTL — 14 days. Past the halfway point of the lifetime, the resolver issues a rolling refresh and writes the new expires_at back to the row.
  • At rest — the cookie value is hashed (SHA-256, base64url) before any DB write. The raw value lives only in the user’s browser.
  • Revocation — signing out deletes the row keyed by the hashed cookie value. A daily sweep removes expired magic-link tokens and expired sessions.

These items are part of the published roadmap, not present-day capability. They are listed here so the docs stay honest about Sill’s bounds.

  • SSO / SAML / OIDC. Not available. SSO is Phase 4. The sso_subject column exists for future use.
  • MFA / TOTP. Not enforced. The schema reserves mfa_enabled and mfa_secret_ref; the magic-link path is the only credential today.
  • Granular per-site roles. Roles are Account-scoped, not per-site. A user with viewer on the Account is viewer on every site in that Account.
  • API keys — the schema models account-scoped or site-scoped API keys with named scopes; programmatic-credential issuance from the dashboard is rolling out.

Who is created when I sign in for the first time?

Section titled “Who is created when I sign in for the first time?”

A new Account and a single owner user are created atomically on first magic-link verification. The Account name and user name default from your email; you can change both from account settings.

Can a user belong to more than one Account?

Section titled “Can a user belong to more than one Account?”

No. The model is one user, one Account (account_id is a non-null column on user). To work in two Accounts, you sign in with two different email addresses.

What happens to a deactivated user’s audit history?

Section titled “What happens to a deactivated user’s audit history?”

It stays. Audit records are append-only and Merkle-chained — deactivating a user changes who can sign in, not the record of what they did. Past mandate decisions and escalation resolutions that reference the user remain verifiable.

The raw token exists in three places only: the email body, the verify-URL query parameter the user clicks, and never anywhere else. The DB stores only a SHA-256 hash. Tokens are short-lived, single-use, and consumed in a transaction so a replay cannot mint two sessions. Rate limits apply per-email and per-IP.

Why are owner and admin enforced identically in many places?

Section titled “Why are owner and admin enforced identically in many places?”

Today most write actions accept either role. The distinction exists in the schema and in the role taxonomy so that owner-only actions (Account transfer, future billing-owner gates) can be tightened without a migration. Treat owner as “the seat that can change the seats.”

Can I assign Sill roles to a third-party AI agent?

Section titled “Can I assign Sill roles to a third-party AI agent?”

No. Roles describe human dashboard users. Non-human callers are modelled as Agents and authenticated by their signed agent card and ed25519 public key, not by a session cookie. The two are deliberately separate.