Skip to content
Open the portal
Developer docs

Evidence outputs

EngineersCompliance & legal7 min read

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.

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.

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.

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.

import hashlib, json
from jcs import canonicalize # RFC 8785
from 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, ES256

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.

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