Skip to content

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

shadow verify-cert release.cert.json

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_id match)
  • confirm the policy file they have is the one recorded (policy_hash match)
  • 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. Returns cert_id and 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).