Agent Behavior Certificate (ABOM)¶
A small, content-addressed JSON release artifact that captures what behavior shipped. Same idea as a software SBOM, applied to AI agents.
What's in it¶
| Field | What |
|---|---|
cert_version |
Format version (currently 0.1). Bumped on backwards-incompatible shape changes. |
agent_id |
Stable identifier for the release, e.g. refund-agent@2.3.0. Caller-supplied. |
released_at |
ISO 8601 timestamp in UTC, second precision. |
trace_id |
Content-id of the trace's root metadata record. |
models |
All distinct model ids observed in the trace's chat_request records. |
prompt_hashes |
Content-ids of all distinct system prompts observed. |
tool_schemas |
Per-tool {name, hash} pairs. Hash is the content-id of the canonical JSON of the tool schema. |
policy_hash |
Optional. SHA-256 of the policy file contents when one was supplied at certify time. |
regression_suite |
Optional. Baseline-vs-candidate nine-axis severity rollup, when a baseline trace was supplied. |
cert_id |
Content-id of the rest of the certificate. Computed last; the field that makes the certificate self-verifying. |
Generate¶
shadow certify candidate.agentlog \
--agent-id refund-agent@2.3.0 \
--policy shadow-policy.yaml \
--baseline baseline.agentlog \
--output release.cert.json
--policy and --baseline are optional; both are recommended for a meaningful release record.
Verify¶
Recomputes the body's hash and compares to the claimed cert_id. Exits 0 when consistent, 1 on tamper, malformed payload, or unsupported cert_version. The non-zero exit makes verify-cert usable as a release-pipeline gate.
What it proves and what it doesn't¶
The certificate is a content-addressed record of the inputs to a release. Anyone with the trace, the policy, and the certificate can:
- confirm the trace they have is the trace that was certified (
trace_idmatch) - confirm the policy file they have is the one recorded (
policy_hashmatch) - read the regression-suite axis severities the candidate had against the baseline at certify time
It does NOT yet:
- prove the trace was generated by an authorised release pipeline rather than crafted by hand
- attest to anything that isn't part of the body — extra fields added later are ignored on verify (forward-compat) but also unverified
For the current release, treat the certificate as an integrity check on the inputs you already trust, not as a chain of custody.
Cosign / sigstore signing¶
Add a layer of cryptographic identity-binding on top of the content-addressing with --sign:
pip install 'shadow-diff[sign]'
shadow certify candidate.agentlog \
--agent-id refund-agent@2.3.0 \
--output release.cert.json \
--sign
This writes the JSON certificate as before AND a sidecar release.cert.json.sigstore Bundle containing the signature, the Fulcio-issued signing certificate, and a Rekor transparency-log entry. The signed payload is the canonical certificate body bytes — the same bytes cert_id hashes — so tampering invalidates both the content-id AND the signature.
Without --identity-token, sigstore uses ambient credentials. In CI, GitHub Actions automatically supplies an OIDC token when the workflow grants permissions: id-token: write. Locally, sigstore opens a browser for interactive auth.
Verify the signature alongside the content-addressing:
shadow verify-cert release.cert.json \
--verify-signature \
--cert-identity 'https://github.com/your-org/your-repo/.github/workflows/release.yml@refs/tags/v2.3.0' \
--cert-oidc-issuer 'https://token.actions.githubusercontent.com'
--cert-identity binds verification to a specific signer — a leaked Bundle signed by another identity will fail this check even if the cryptography is otherwise valid. That's the whole point of the keyless flow: the signature is meaningless without the identity assertion.
The signing layer is optional. Certificates without a .sigstore bundle still verify content-addressing via plain shadow verify-cert; the --verify-signature flag is opt-in.
MCP integration¶
When you invoke Shadow as an MCP server (shadow mcp-serve), two new tools are available to any MCP-aware client:
shadow_certify— same shape as the CLI; takes a trace path, agent_id, output path, optional baseline, optional policy_path. Returnscert_idand the full certificate body.shadow_verify_cert— takes a certificate path. Returns{ok, detail, cert_id}.
So an agentic CLI can certify and verify releases as part of its normal tool-using flow.
When to use it¶
- Release pipeline gate — your release workflow regenerates the certificate from the candidate trace and either fails (verify-cert exits 1 on a stale certificate) or attaches the certificate to the GitHub Release.
- Audit log — commit the certificate alongside the agent code at every tagged release. The git history becomes an audit trail of what behavior shipped, not just what code shipped.
- PR review aid — when paired with
shadow diff --policy, a reviewer can see the regression-suite rollup that landed in the certificate at the same time as the diff, in the same PR comment.
Format stability¶
The certificate format is small and human-readable on purpose — it's meant to live alongside the agent in a release branch. The cert_version field is bumped on backwards-incompatible shape changes; today's format is 0.1. Adding fields is always backwards-compatible (the verifier ignores unknown fields when reconstructing the canonical body).