DocsUse cases

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_cades produces a detached .p7s instead — see Choosing a seal format.