// AUDIT_SCHEMA · NO_LOGIN_REQUIRED
// RECORD_SCHEMA
| Field | Type | What it is for |
|---|---|---|
| seq | integer | Monotonic sequence number. Walk the file in this order to check the chain. |
| ts | RFC 3339 | When the record was written — before the hardware call it describes, not after. |
| level | string | AUDIT for compliance-relevant decisions. Policy denials are written at this level. |
| op | string | The RPC or handler name — Sign, GenerateKey, Verify. Not the wire method. |
| outcome | SUCCESS | FAILURE | Only meaningful on the return-value logging variants. See the caveat below — this is not a reliable ALLOW/DENY signal on its own. |
| file / func / line | string / string / int | Source location that emitted the record. |
| detail | string | Structured key=value text carrying request_id, key label, algorithm and the outcome marker. This is where a reviewer finds their own request. |
| duration_ms | integer (optional) | Present only on timed operations — Sign, Verify, Encrypt, Decrypt. |
| rv | hex string (optional) | PKCS#11 return code. 0x00000000 is CKR_OK. |
| prev_hmac | hex string | The preceding entry's hmac. This is the link that makes the file a chain. |
| hmac | hex string | HMAC-SHA256(prev_hmac || entry_json) — the record's own tag. |
Limit
outcome defaults to SUCCESS even for a request that was rejected on policy. What tells you what actually happened is the marker inside detail — rpc=completed versus rpc=rejected_pqc_policy. A reviewer who filters on outcome alone will miscount denials, so it is said here rather than in a footnote.// SYNTHETIC_SAMPLE
hmac values are visible placeholders rather than plausible digests, so that nobody mistakes them for something verifiable.Synthetic — illustrative only, not a run record
{"seq":41,"ts":"2026-02-11T09:14:02Z","level":"AUDIT","op":"Sign",
"outcome":"SUCCESS","file":"SigningServer.cpp","func":"Sign","line":645,
"detail":"rpc=rejected_pqc_policy request_id=<yours> key=demo-mldsa-key alg=MLDSA65 mode=FULL_PQC",
"prev_hmac":"<SYNTHETIC-prev>","hmac":"<SYNTHETIC-a>"}
{"seq":42,"ts":"2026-02-11T09:14:37Z","level":"AUDIT","op":"Sign",
"outcome":"SUCCESS","file":"SigningServer.cpp","func":"Sign","line":645,
"detail":"rpc=completed request_id=<yours> key=demo-mldsa-key alg=MLDSA65 mode=PQC_PRIMARY sig=3309B",
"duration_ms":18,"rv":"0x00000000",
"prev_hmac":"<SYNTHETIC-a>","hmac":"<SYNTHETIC-b>"}
{"seq":43,"ts":"2026-02-11T09:15:11Z","level":"AUDIT","op":"GenerateKey",
"outcome":"FAILURE","file":"KeyManager.cpp","func":"GenerateKey","line":212,
"detail":"rpc=denied reason=licence_suspended request_id=<yours> class=signing",
"rv":"0x00000000",
"prev_hmac":"<SYNTHETIC-b>","hmac":"<SYNTHETIC-c>"}Note what the first two records demonstrate together: a post-quantum request refused on the server’s operating-mode policy, then the same key and algorithm accepted under a permitted mode. The denial is a record at AUDIT level with its own place in the chain — refusals are evidence, not silence.
// CHAIN_RULE
hmac is HMAC-SHA256(prev_hmac || entry_json), and each entry carries the previous entry’s tag in prev_hmac. Both fields travel in every record of the bundle, so the structure is checkable offline without re-contacting anything.Limit
// WHAT_A_TOKEN_ADDS
A token turns this schema into your own evidence: submit a request with a request_id you choose, then find that exact id in the downloaded audit bundle. The schema tells you what to look for; only the live trail contains your record.
Everything above is checkable without contacting anyone, and is meant to be read first. The token exists because the remainder needs a provisioned environment — not because the evidence is being held back.