Seal AI and agent logs with JAdES
Make agent sessions, model outputs, and decision records provable: detached ETSI-standard JSON seals, hash-only by default, with optional post-quantum protection.
Seal AI and agent logs with JAdES
AI systems produce records that increasingly need to be provable: what the model was asked, what it answered, which tools an agent called, what decision was recorded. Those records are JSON — and JAdES (ETSI TS 119 182-1) is the ETSI signature format built for JSON. This guide seals a JSONL agent log; the same flow applies to any JSON artifact.
The flow
from sigill_sdk import SigillClient
client = SigillClient(api_key="sigill_...")
log = open("agent-session.jsonl", "rb").read()
jades = client.seal_jades(log, certificate_id=CERT_ID,
label="agent-session.jsonl",
content_type="application/json")
open("agent-session.jsonl.jades.json", "wb").write(jades)
Only the SHA-256 digest of the log is transmitted — the log itself stays with you. The returned artifact is a detached JWS: itself JSON, so it travels through your existing pipelines like any other payload. Store it next to the log and treat the pair as immutable.
.NET is the same shape: await client.SealJadesAsync(log, certId, contentType: "application/json").
Sealing exact bytes — what that means for logs
The seal covers the log's exact bytes. If you later re-serialize, reformat, or re-export the JSON, the seal will not match — by design. Practical consequences:
- Seal the log after it is final (end of session, end of day, on rotation).
- Archive the sealed bytes, not a database row you re-serialize on demand.
- For append-only JSONL streams, seal on rotation boundaries so each sealed segment is immutable.
Verify
result = client.verify_jades(log, jades)
assert result.is_valid
print(result.signer) # who sealed it
print(result.gen_time) # when, per the embedded RFC 3161 timestamp
Verification is also hash-only — digests plus the artifact, never the log content. Anyone holding the log and the seal can verify at sigill.ai/verify without an account.
Long-lived evidence: add the post-quantum signature
AI-related records are exactly the kind of evidence the "harvest now, decrypt later" threat targets — a decision log sealed today may need to hold up in fifteen years. Pass pqc=True and the artifact carries a second, quantum-resistant ML-DSA-87 signature (NIST FIPS 204, RFC 9964) alongside the classical one:
jades = client.seal_jades(log, certificate_id=CERT_ID, pqc=True)
result = client.verify_jades(log, jades)
print(result.post_quantum.algorithm) # "ml-dsa-87"
print(result.post_quantum.content_bound) # "yes"
The classical signature remains the legal instrument; the ML-DSA signature is additive protection, reported separately on verification.
Sealing evidence envelopes
If you use the SDKs' AI evidence envelopes — structured records of individual AI calls — you can combine both mechanisms: the envelope's RFC 3161 proof establishes when, and a JAdES seal over the envelope's canonical bytes establishes who stands behind it:
from sigill_sdk import canonicalize
sealed = client.seal(envelope, external_payloads=payloads)
jades = client.seal_jades(canonicalize(sealed), certificate_id=CERT_ID,
content_type="application/json")
Notes
- JAdES sealing requires a Business or Scale plan; qualified timestamps (
qualified=True) are available on all seals. - For non-JSON logs (or when your consumers expect PKCS#7), the identical flow with
seal_cadesproduces a detached.p7sinstead — see Choosing a seal format.