Reference
Audit & verification
Every authorization decision — allow or deny, on every surface — is appended to a hash-chained, tamper-evident log. Read it with GET /v1/audit and prove the chain is intact with GET /v1/audit/verify.
The audit row#
Each decision appends one row. A row records who did what, where, and how it was decided — plus the cryptographic linkage that makes the log tamper-evident.
| Field | Type | Meaning |
|---|---|---|
seq | int | Monotonic sequence number (starts at 1; the genesis row's prev_hash is 0×64). |
ts | int | Unix epoch milliseconds. |
principal | string | The acting principal id. |
action | string | The attempted action (e.g. search, ingest, read). |
resource | string | The scope / unit / route touched. |
surface | UI | CLI | REST | SDK | MCP | Which surface the request came through. |
decision | allow | deny | The enforcement outcome. |
prev_hash | string | Hash of the previous row (0×64 for genesis). |
hash | string | sha256 over this row's canonical JSON incl. prev_hash. |
How the hash chain works#
The log is a chain: each row’s hash is computed over a canonical (key-sorted) JSON encoding of the row’s fields including the previous row’s hash. Because the encoding is deterministic, anyone holding the rows can recompute every hash. Altering, dropping, or reordering any row breaks the linkage from that point forward.
row.hash = sha256(canonicalJson({ seq, ts, namespace, principal,
action, resource, surface, decision,
prev_hash }))
row[n].prev_hash == row[n-1].hash (row[0].prev_hash = 0×64 genesis)List the audit log#
/v1/auditReturns hash-chained AuditRow rows for the caller’s namespace, newest first. Requires read or admin. Supports limit and a cursor (echo back next_cursor to page).
curl -s "https://superchargedb.krisch1218.workers.dev/v1/audit?limit=50" -H "Authorization: Bearer aegis_sk_alice"{
"namespace": "acme-corp",
"rows": [
{
"seq": 2,
"ts": 1783343567981,
"namespace": "acme-corp",
"principal": "bob@acme",
"action": "read",
"resource": "u-call-02",
"surface": "MCP",
"decision": "deny",
"prev_hash": "b1946ac92492d2347c6235b4d2611184e2b6f0d9f1c3a2...",
"hash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822..."
},
{
"seq": 1,
"ts": 1783343567016,
"namespace": "acme-corp",
"principal": "alice@acme",
"action": "search",
"resource": "acme/alpha/backend",
"surface": "REST",
"decision": "allow",
"prev_hash": "0000000000000000000000000000000000000000000000000000000000000000",
"hash": "b1946ac92492d2347c6235b4d2611184e2b6f0d9f1c3a2..."
}
],
"count": 2,
"next_cursor": null
}Verify the chain#
/v1/audit/verifyRecomputes the chain server-side and reports whether it is intact. This lets any reader — an auditor, a compliance job, or your own CI — independently confirm the log has not been reordered, truncated, or altered.
curl -s "https://superchargedb.krisch1218.workers.dev/v1/audit/verify" -H "Authorization: Bearer aegis_sk_alice"{
"namespace": "acme-corp",
"intact": true,
"checked": 128,
"head_seq": 128,
"head_hash": "3fa1c0e2…",
"break_at": null,
"reason": null
}If a row had been tampered with, the response pinpoints the first broken link:
{
"namespace": "acme-corp",
"intact": false,
"checked": 57,
"head_seq": 56,
"head_hash": "9c2b…",
"break_at": 57,
"reason": "prev_hash does not link to the prior row's hash"
}| Field | Type | Meaning |
|---|---|---|
intact | boolean | True when every recomputed hash matches and links to its predecessor. |
checked | int | Number of rows verified before stopping. |
head_seq | int | Sequence number of the last valid row. |
head_hash | string | Hash of the last valid row (0×64 for an empty chain). |
break_at | int | null | The seq of the first broken row, or null when intact. |
reason | string | null | Human-readable failure reason, or null when intact. |
WORM sealing
The chain head is CAS-committed and periodically sealed into object-lock (WORM) storage, so the log is append-only in storage as well as by construction. See the Security model for how audit fits into the two enforcement layers.
Verified against the shipped contract
{ namespace, rows: AuditRow[], count, next_cursor } and the verify response is { namespace, intact, checked, head_seq, head_hash, break_at?, reason? }, matching the shared @superchargedb/sdk types and the deployed Worker.