Audit log
Every decision Writ makes — ALLOW, DENY, STEP_UP, REVOKE — lands in your tenant’s audit log as a receipt. The log is the product’s answer to the auditor: instead of dumping CloudTrail and hoping, the compliance owner opens the receipt and sees who, what, which purpose, which policy version, when.
Watch it live
Three ways, same stream:
Or poll GET /v1/receipts?since=<receiptId> — it returns only newer
receipts plus a cursor.
Filter by decision
GET /v1/receipts/stream?decision=DENY streams only one decision —
ALLOW, DENY, or STEP_UP (case-insensitive; anything else is a 400).
The filter applies to the initial replay and to every frame emitted while
the connection is held.
The DENY-only stream shows every write Writ blocked before it happened, in real time. The dashboard’s Denied panel is built on it — verb, target, and reason for each stopped write, with a running count.
Streaming to your SIEM
Security teams don’t want another dashboard — they want the event stream.
GET /v1/receipts/stream is that feed: every gated write your tenant’s
agents attempt, as server-sent events, in near real time. This is the
answer to “can we pipe this into our SIEM?”
Connecting. Authenticate with Authorization: Bearer <key>, or pass
?apiKey= — browsers’ EventSource can’t set headers, which is why the
query param exists. The server holds the connection up to timeout
seconds (default 10, max 12), emitting event: receipt frames plus
: ping heartbeats, then closes it. Reconnect and resume with since
(or Last-Event-ID, which EventSource sends automatically). Omit
since on first connect to replay the most recent replay receipts
(default 20) and then go live.
What each event carries. Every frame’s data is a complete receipt:
receiptId, tenantId, decision (ALLOW, DENY, STEP_UP, REVOKE),
sponsorId, agentId, verb, target, purpose, reason,
policyVersion, createdAt, plus prevHash/chainHash for the
tamper-evident chain. No joins needed downstream — enrich or index it as
is. Use receiptId as the dedup key; the resume cursor guarantees at-least-once delivery across reconnects, never gaps.
Filtering. Append ?decision=DENY to stream only stopped writes —
the feed your SOC actually wants to watch. ALLOW and STEP_UP work
too; anything else is a 400. The filter applies to the replay and the
live hold alike.
Tenant isolation. One tenant’s stream never carries another tenant’s receipts. A receipt ID from another tenant is indistinguishable from a missing one everywhere in the API.
Polling fallback. Where SSE is unavailable, poll
GET /v1/receipts?since=<cursor> — it returns only newer receipts plus a
cursor for the next poll.
Tamper evidence
Every receipt is hash-chained: each one stores prevHash (the previous
receipt’s chainHash, or GENESIS for the first) and
chainHash = HMAC-SHA256(canonical receipt fields + prevHash), keyed by the
deploy’s WRIT_CHAIN_SECRET. The tenant’s chain head advances with a
conditional write, so concurrent checks serialize into one linear chain
instead of forking.
Anyone with raw table access who alters a stored receipt breaks the chain — and the break is detectable:
GET /v1/receipts/verify returns
{"ok": true, "checked": N, "tip": "<hash>", "tipReceipt": "<id>", "chainedFrom": "<id>", "legacyCount": M, "complete": true} when the chain
is intact, or {"ok": false, "brokenAt": "<receiptId>", "reason": "..."}
at the first receipt that fails.
Receipts written before chaining was enabled have no chain fields;
verification stops at them and reports them as legacyCount.
Deploy notes
Set WRIT_CHAIN_SECRET to a dedicated secret on your deploy (it falls back
to WRIT_TOKEN_SECRET). Whoever holds the table but not the deploy secret
cannot forge a valid link.
This is the control that supports NIST SP 800-53 AU-9 (Protection of Audit Information): audit records are protected against unauthorized modification and deletion, and tampering is detectable on demand. It also feeds AU-3 (content of audit records) — every receipt records who, what, when, and why — and AU-12 (audit generation). Writ is not itself certified; your assessor determines compliance for your deployment, but the chain gives them the tamper-evidence mechanism AU-9 asks for.
Billing note
The audit log is also the meter: every receipt is a billable event under commit + volume pricing, and because you hold the same tamper-evident log, you can verify usage yourself. See Pricing.