Skip to content
Warrantv0.1

Key handling

Keys are stored as SHA-256 digests. The plaintext is shown once and is not recoverable.

An API key is stored as a SHA-256 digest alongside its prefix and last four characters — the row proves a key was issued, never what it was.

wr_test_8f0c1b4e2b1a4d599d0e3a2f7c1b9e44
└──────┘                            └──┘
 prefix                            last4

What is stored

ColumnHolds
prefixwr_test_ or wr_live_.
hashSHA-256 of the full key.
last4The final four characters, for display.
environmentsandbox or production.
scopesReserved; not currently enforced.
last_used_atUpdated on every successful resolve.
revoked_atNull while live.

The plaintext is shown once, when the key is created, and is not recoverable afterwards. Someone who reads the whole table gets a list of digests, which is the point.

Prefixes decide the environment

wr_test_...   sandbox
wr_live_...   production

Nothing else does. A wr_live_ key presented against a sandbox organisation resolves to no principal at all, which is a 401 rather than a 403 — there is nobody to refuse. That is the same answer a made-up key gets, so probing tells you nothing.

Revocation is immediate

Resolution filters on revoked_at IS NULL on every request. There is no cache and no session to expire: a revoked key stops working on the next call.

Keys are for agents, and carry no role

An API key can open and verify warrants. It cannot resolve an escalation, release, refund or partially release — those return 403 user_session_required.

If a key could approve a payment, the passkey threshold above which a person must sign would be decorative, because anyone holding a key could route around it. This is the single most important thing to understand about the key model.

Sessions for people

A person's session is an httpOnly cookie, never localStorage. The console is a backend-for-frontend: it holds the cookie on its own origin and forwards it server-side. No token reaches client JavaScript, so an XSS bug on the console cannot exfiltrate one.

Where to keep yours

Environment variables, read at process start. The SDK reads WARRANT_API_KEY and never takes a key as a literal in a call signature, which keeps it out of stack traces and out of anything that logs arguments.

Do not put a key in a NEXT_PUBLIC_ variable. That prefix inlines the value into the browser bundle at build time, permanently and publicly. This repo lint-enforces the rule for anything that looks like a secret.

Other secrets in the same family

SecretGuards
WARRANT_WORKER_SECRET/internal/*. Absent or wrong is 401.
TELEGRAM_WEBHOOK_SECRETThe bot webhook. Absent or wrong is 401.
Webhook signing secretOutbound HMAC. Shown once, not recoverable.

Both header secrets are compared with timingSafeEqual, and absent and wrong return the identical error code so a prober learns nothing from the difference.