Evidence outputs
Every output verifies offline: no account, no network, no trust in Stratl. Each carries the public key it needs and a manifest naming every file with its SHA-256.
Bundle: one record
Section titled “Bundle: one record”GET /v1/decisions/{decision_id}/bundle returns <decision_id>.stratl.zip:
| File | What |
|---|---|
decision.json |
The signed record with its integrity block |
keys/public.jwk |
The public key of the key that signed this record, even if the workspace has since rotated |
artifacts/<sha256> |
Original content the record refers to, when it was uploaded; re-hash it against the record |
checkpoint.json |
The checkpoint that covers the record, its inclusion proof and its RFC 3161 token, once one exists |
manifest.json, verification.txt |
Every file with its hash, the verification result at export time, and how to check it |
A bundle exported before its checkpoint exists has no checkpoint.json; export again after the next checkpoint to include it.
Subject pack: everything about one person
Section titled “Subject pack: everything about one person”GET /v1/subjects/{type}/{id}/pack returns one archive: a full bundle per record about the subject, oldest first (including Stratl’s workflow records about them); every notice as HTML; subject.json with the records, each bundle’s verification result, the notices with their delivery facts and the active holds; a public key per signing key that appears; the workspace’s current key; and manifest.json with manifest.sig.json, an ES256 signature over the RFC 8785 form of the manifest. Building the pack signs the manifest, which is a key use recorded in the key-usage ledger. Reading it as a compliance officer.
Period pack
Section titled “Period pack”GET /v1/reports/period?from=…&to=…&pack=…&format=zip covers a window against one framework pack: report.json (totals, coverage per control, gaps), report.html, decisions.jsonl (one record per line), the workspace key, and the same signed manifest. format=json and format=html return the report alone. Frameworks.
Verifying manifests
Section titled “Verifying manifests”import hashlib, jsonfrom jcs import canonicalize # RFC 8785from jwcrypto import jwk, jws # any ES256 implementation works
m = json.load(open("manifest.json")); sig = json.load(open("manifest.sig.json"))for path, digest in m["files"].items(): assert digest == "sha256:" + hashlib.sha256(open(path, "rb").read()).hexdigest()key = jwk.JWK(**json.load(open("keys/public.jwk")))# verify sig["signature"] (base64url r||s) over canonicalize(m) with key, ES256Notices
Section titled “Notices”A notice is not a signed artefact itself; the signed evidence is the workflow records written when it is approved, sent and withdrawn, linked to the decision. The notice’s HTML is included in subject packs, and GET /v1/notices/{id}/html renders it at any time. The lifecycle.
Bundle layout in custody storage
Section titled “Bundle layout in custody storage”Independently of any export, custody storage holds the same facts in a layout that can be audited without Stratl:
<workspace>/records/YYYY/MM/DD/<decision_id>.json the signed record<workspace>/records/YYYY/MM/DD/<decision_id>.sig the detached signature<workspace>/checkpoints/YYYY/MM/DD/<checkpoint>.json signed Merkle checkpoint with its timestamp<workspace>/artifacts/<aa>/<sha256> original content, named by its fingerprint<workspace>/keys/<key_id>.jwk every public key that has signed<workspace>/transfers/<id>.json custody transfer statements, dual-signed