Spec & reference · v0.1 (draft) + v0.2 (draft)

What's in an ABOM, field by field.

An ABOM document is one of two things: a Composition Manifest (what an agent is) or an Action Provenance Record (what an agent did). This page documents every field, the seven component types, the enforcement-policy schema — and the v0.2 draft: per-record signatures, the gateway's event types, payload commitments, and floor + template policies.

JSON Schema · Draft 2020-12 ed25519-signed Inspired by CycloneDX ML-BOM
Two document types Composition Manifest Component types Action Provenance Signature Enforcement policy v0.2 draft
The model

Two document types

Everything in ABOM is one of two artifacts. They are linked: every Action Provenance Record carries the composition_sha256 of the manifest the agent ran under, so a runtime action can always be traced back to the exact, signed thing it ran from.

Composition Manifest

What is this agent made of?

A signed inventory produced at build / deploy time.

  • The agent's identity, owner, and risk class
  • Every component: models, tools, prompts, data, policies…
  • Declared controls (egress, human-in-the-loop, residency)
  • A composition_sha256 + ed25519 signature

Action Provenance Record

What did this agent do?

One hash-chained record per consequential action, at runtime.

  • The decision taken and the inputs it acted on
  • Model calls, tools invoked, data touched
  • Policy decisions and human approvals
  • A prev_hashhash chain back to GENESIS
Top-level fieldTypeWhat goes here
abomconst "0.1"The spec version. Bumps on breaking changes — never silent.
typeenum"CompositionManifest" or, for provenance, an event_type of "ActionProvenance".
Artifact 1

Composition Manifest

A signed inventory of everything an agent is built from.

composition-manifest.json
{
  "abom": "0.1",
  "type": "CompositionManifest",
  "agent": { "name": "loan-doc-agent", "version": "1.4.0", "risk_class": "high (Annex III)" },
  "components": [
    { "type": "model", "name": "local/qwen2.5-coder", "weights_sha256": "…", "egress": false },
    { "type": "tool", "name": "http_fetch", "scope": "egress", "allowed_endpoints": ["internal-kyc.bank"] },
    { "type": "prompt", "role": "system", "sha256": "…" },
    { "type": "dataSource", "name": "core_banking_repo", "classification": "confidential" },
    { "type": "policy", "engine": "OPA", "sha256": "…" }
  ],
  "controls": { "egress": "deny-by-default", "hitl": "required-for-consequential", "residency": "EU" },
  "composition_sha256": "…",
  "signature": { "alg": "ed25519", "value": "…" }
}

agent required

Identity and governance metadata for the agent the manifest describes.

FieldTypeWhat goes here
name reqstringThe agent's identifier, e.g. loan-doc-agent.
version reqstringSemantic or build version, e.g. 1.4.0. Forms the name@version run reference.
risk_class optstringRisk tier, free-form. Often maps to a regulation, e.g. high (Annex III) for the EU AI Act.
owner optstringAccountable team or person, e.g. risk-eng@bank.
purpose optstringOne line on what the agent is for.

controls optional

Declared, human-readable posture for the agent. These are declarations in the manifest; the machine-enforced version lives in the enforcement policy.

FieldTypeWhat goes here
egressstringe.g. deny-by-default — the agent's network posture.
hitlstringHuman-in-the-loop stance, e.g. required-for-consequential.
residencystringWhere data must stay, e.g. EU.

controls accepts additional keys — it's an open object for posture you want recorded.

Inside the manifest

Component types

Each entry in components[] has a type and type-specific fields. There are seven types:

model

An LLM or ML model the agent calls.

tool

A function or API the agent can invoke.

prompt

A system / template prompt.

dataSource

A dataset, repo, or store it reads.

policy

A policy bundle the agent ships with.

framework

The agent framework / runtime.

mcpServer

A connected MCP server.

Only type is required. The other fields are populated where they apply to that type — a model carries weights_sha256, an egress tool carries allowed_endpoints, an MCP server carries endpoints, and so on. The object is open, so a scanner can record extra detail without breaking the schema.

FieldTypeWhat goes here
type reqenumOne of the seven types above.
namestringComponent identifier — match what abom scan records.
versionstringComponent version, where applicable.
sha256stringDigest of the component's bytes (prompt text, policy bundle…).
weights_sha256stringmodel — digest of the model weights, pinning the exact model.
provenancestringWhere the component came from (registry, repo, vendor).
egressbooleanWhether the component can send data out of the boundary.
scopestringtool — e.g. egress, read.
allowed_endpointsstring[]tool — the endpoints an egress tool is permitted to reach.
rolestringprompt — e.g. system, user.
classificationstringdataSource — data sensitivity, e.g. confidential.
enginestringpolicy — the policy engine, e.g. OPA.
endpointsstring[]mcpServer — the MCP endpoints exposed.
Artifact 2

Action Provenance Record

One hash-chained record of a consequential action the agent took. Each record's prev_hash points at the previous record's hash, forming a tamper-evident chain back to "GENESIS".

FieldTypeWhat goes here
run_id reqstringAgent reference, e.g. loan-doc-agent@1.4.0.
seq reqinteger ≥ 0Monotonic position of this record in the chain.
event_type reqconstAlways "ActionProvenance".
actor optstringWho/what produced the record.
created_at reqdate-timeRFC 3339 timestamp.
prev_hash reqstringPrevious record's hash, or "GENESIS" for the first.
hash reqsha256This record's digest — the chain link.
data reqobjectThe action payload (below).

data — the action payload

What the agent actually did on this step. composition_sha256 and decision are required; the rest are recorded where relevant.

FieldTypeWhat goes here
composition_sha256 reqsha256The manifest this action ran under — the join key. A mismatch is a composition_match finding.
decision reqstringWhat the agent decided to do.
inputsobject[]The inputs the decision acted on.
model_callsobject[]Each: model, tokens, output_sha256. Checked against the allowlist + manifest.
tools_invokedobject[]Each: name, endpoint. Endpoints checked against the egress allowlist.
data_touchedobject[]Each: classification, egress. Egress of restricted data is a residency finding.
policy_decisionsobject[]Each: rule, result. A result: "required" demands an approval.
approvalobject | nullby + decision (approved/rejected). Missing approval on a required action is an approval_coverage finding.
Trust

Signature

The manifest is signed with a detached ed25519 signature over its canonical JSON form. This is what makes the trust cryptographic, not reputational — anyone with the public key can verify the document hasn't changed.

FieldTypeWhat goes here
alg reqstringed25519 in production. (An hmac-sha256 stand-in exists for the MVP only.)
value reqstringBase64 detached signature over the canonical body.
signer optstringKey identifier / signer reference.
Enforcement

The enforcement policy

Two different things called "policy". Don't confuse them:
· policy component — an entry inside a manifest's components[] ({ "type": "policy", "engine": "OPA" }). It declares that the agent ships with a policy bundle.
· enforcement policy document — a separate JSON file (below) that the gate evaluates before an action runs, and that abom verify audits after the fact. This is the one you author and hand to --policy.

The enforcement policy has its own schema, abom-policy-0.1.schema.json. Every field maps to a concrete check. Allowlists are deny-by-default where set: omit a field to leave it unconstrained, set it to [] to deny all.

policy.json
{
  "name": "finance-baseline",
  "allowed_models": ["local/qwen2.5-coder"],
  "allowed_egress_endpoints": ["internal-kyc.bank"],
  "no_egress_classifications": ["confidential", "restricted"],
  "require_approval_when": "consequential"
}
FieldTypeWhat it enforces
namestringPolicy identifier, surfaced in findings and decision cards.
allowed_modelsstring[]model_allowlist — declared and runtime models must be on the list; a runtime model absent from the manifest is composition_drift.
allowed_egress_endpointsstring[]egress_allowlist — tool endpoints not on the list are flagged.
no_egress_classificationsstring[]residency — these classifications must never egress.
egress_allowedbooleanRuntime gate: hard deny-by-default for outbound egress when false.
consequential_actionsstring[]Rules that mark an action consequential: writes_outside_workspace, touches_paths:<glob>.
approval_required_for_consequentialbooleanRuntime gate: consequential actions need human approval.
require_approval_whenenumAudit-time counterpart: consequential / never / alwaysapproval_coverage.
max_iterations_capintegerRuntime safety cap on the agent's reasoning loop.

Ready-made policies for finance, manufacturing, and healthcare live on the policy hub. Build and validate your own with the policy builder.

The gateway layer · breaking changes, never silent

v0.2 draft — signed records, event types, commitments

v0.2 is the spec of the MCP gateway: one signed record per tool call, written before the call runs. Everything below is implemented in the reference CLI, and the full draft ships with it — spec/abom-0.2-draft.md plus the decision log (docs/adr/) in the abom-cli sdist; the breaking changes are what justify the version bump. See how the pieces deploy in the interactive architecture.

Every record individually signed breaking

In v0.1 only the manifest carries a signature; the provenance chain is hash-linked but records are unsigned. In v0.2 every record MUST carry a detached ed25519 signature over its canonical form. The hash chain and the signature cross-cover: mutating a chained field breaks both. A valid v0.1 chain is not a valid v0.2 chain.

Event types

A v0.2 log is a chain of typed, signed records — the gaps are attested, not silent:

event_typeWhat it attests
ActionProvenanceOne per tools/call — allowed, denied, or would_deny (inspect mode). Written and fsync'd before forwarding; carries enforcement_mode, floor_hash, policy_template_hash, jurisdiction, detectors[], payload commitments.
ActionOutcomeThe upstream result of a forwarded call, linked to its APR by outcome_of — closes the decision≠execution gap.
LogEpochREQUIRED first record of every log file: file creation is itself attested. Carries predecessor_final_hash (rotation continuity — catches tail-truncation, file suppression, delete-and-restart) and salt_custody.
GatewayDegradedAn upstream failure or timeout as a signed event — "we degraded at 14:03, here's the proof", never a missing record.
PolicyConformanceViolationA project template refused for contradicting the org floor — the refusal itself enters the chain.

Payload commitments

Arguments never enter the log as content. Each call records a per-file salted HMAC (arguments_hmac) — call identity that is not dictionary-attackable — plus a de-identified arguments_shape (keys, types, sizes). The salt stays inside the boundary; the epoch seals its hash, so a salt disclosed in a dispute is authenticatable and verify_argument_claim settles what a call contained.

Policy v0.2 — floor + template

Additive over v0.1 (abom-policy-0.2.schema.json): allowed_tools / denied_tools globs, allowed_egress_endpoints at the gateway, and a max_calls session budget. Two documents of the same schema can be layered — an org floor and a project template — evaluated conjunctively: floor denials cannot be overridden, allowlists intersect, the budget is the minimum, and denial reasons name the layer that fired.

Default mode is inspect-only. A violation is recorded as a first-class would_deny decision and forwarded — the evidence is the deliverable; --enforce opts into blocking. The raw policy verdict is always preserved in policy_decisions[] alongside what the gateway actually did.