Concepts
Everything in Writ is built from three objects. Understand these and the rest of the product is just operations on them.
Principal
A principal is who is acting, and on whose behalf: a human sponsor, an
agent, a task, and a purpose — bound together. It is not a shared service
account. svc-support-admin can do anything; a principal can only do the
thing its sponsor approved, for the purpose stated.
Two credentials represent the two halves:
The API key says which tenant is asking. The sponsor token says a human
approved this. The sponsor token is shown once at tenant creation and never
recoverable — if it leaks, the tenant’s human side is compromised. It can
also come from the WRIT_SPONSOR_TOKEN environment variable.
Decision
A decision is Writ’s answer at the moment of the write: ALLOW, DENY, or
STEP_UP. It is made by POST /v1/check, which matches the request against
the tenant’s policy and any live grants on five fields — sponsor, agent,
verb, target, purpose. All five must match. Anything else is DENY.
- ALLOW mints a 90-second auth token bound to the exact write: this
verb, this target, this purpose, this policy version. The agent performs
the write, then verifies the token against what it actually did with
POST /v1/tokens/verify. The grant is single-use semantics: it cannot be reused for a different write. - DENY means the write must not run. A receipt is still written — the refusal itself is auditable.
- STEP_UP means a human sponsor must approve. The agent stops and tells
the user. The sponsor mints a one-time grant (dashboard or
POST /v1/grantswith the sponsor token), and the agent callsPOST /v1/checkagain.
Receipt
A receipt is the audit record of a decision — ALLOW, DENY, STEP_UP, REVOKE,
all of them. Receipts are hash-chained (prevHash / chainHash,
HMAC-SHA256 keyed by the deploy secret), so altering a stored receipt breaks
the chain detectably. They are tenant-isolated: one tenant’s receipts
are never visible to another.
The receipt is the product’s answer to the auditor. Instead of “send us the CloudTrail,” the compliance owner opens the receipt: who, what, which purpose, which policy version, when. See Audit log for streaming, SIEM ingestion, and chain verification.
Policy modes
Each verb in a tenant has a mode, set with ./writ policy --set <verb> <mode> or the dashboard policy editor:
Start strict (deny or step_up) on consequential verbs, relax as the
agent earns trust. Policy changes take effect immediately.
Grants
A grant is a sponsor’s one-time approval for a specific write: sponsor,
agent, verb, target, purpose, 90-second lifetime. Minted in the dashboard or
with ./writ grant --sponsor-token .... The agent’s next POST /v1/check
with matching fields returns ALLOW. Grants are how STEP_UP resolves — the
human stays in the loop without becoming the bottleneck for every write.
Kill switch
A sponsor can revoke a principal at any time (POST /v1/revoke with the
sponsor token, or the dashboard). Every later check for that sponsor and
agent is DENY — even under an allow policy, even with a live grant. The
revocation itself writes a receipt. POST /v1/reinstate undoes it.
Use it when an agent misbehaves, a credential leaks, or a purpose drifts. Revocation is per-tenant: one tenant’s sponsor can never touch another tenant’s principals.
Tenants and teams
One tenant = one isolation boundary: its own keys, policy, receipts, and
chain. A tenant starts with one member (whoever created it). Members are
added with invite codes (./writ invite), each getting their own API key;
the sponsor token stays with the tenant creator. Members share the tenant’s
policy and receipts.