How a decision becomes a signed record.
Signed so a third party can re-derive it offline, with no callback and no proprietary tools. What follows is how that record gets built, and why you can check it without trusting us.
Three-phase architecture
Each phase in detail, and the artifact it hands to the next: the sealed policy, the runtime decisions, and the receipt chain that records them.
Policy Artifact
Approve
Subject + Policy
permitted operations
policy (architecture sketch, not a shipped schema)
{ "subject": "agent-3.2.0", "tools": ["mcp.fetch", "mcp.write"] }Sign
Ed25519 (live default)
the npm library's hybrid ML-DSA-65 export signs receipts and checkpoints, not policy artifacts
signature (first 32 bytes hex)
8e2af1d9 73c4b605 a91e2f7c 18d5ebb3 f60c7a44 9d1eaa72 4f8b0c63 c1e2d098Sealed Artifact
In aga-mcp-server, a tamper-evident policy with its validity window bound into the signature
signed under an issuer key generated per process, so no key survives a restart to pin; a delegated child artifact checks only against that process's key, and a pinned-key check ships only in the private reference runtime
Launch check and measurement
Validate
In the request-access Kubernetes component, an admission webhook checks the artifact before a labeled pod starts, against the key the artifact carries; it pins no issuer key
not in the published packages
admission check (request-access component)
verify(artifact.signature) → OK · now within [effective, expiration] → OK · governance sidecar container present → OKMeasure
Architecture: sampling at the cadence the artifact specifies. The published aga-mcp-server measures on demand, over content the caller passes
scheduled classes are in the private reference runtime
measurement (aga-mcp-server, on request)
sha256(content) == subject.bytes_hash && sha256(canon(metadata)) == subject.metadata_hash ? (both supplied by the caller)Decide
Hash matches the sealed baseline?
autonomous, no human in the loop
Match
Continue, and record a signed receipt that finds no drift.
Mismatch
Record a signed drift receipt carrying the artifact-defined decision.
In 3.6.0 through 3.6.2 this receipt stays in the reference server's process; it is not in the exported bundle.
Evidence Bundle
Record
Signed receipt per recorded decision
Ed25519 · hybrid profile as a library export
receipt (excerpt of the 15 fields)
{ "timestamp": "2026-05-24T18:42:11.000Z", "tool_name": "mcp.fetch", "decision": "PERMITTED", "previous_receipt_hash": "4f9c..", "signature": "8e2a.." }Chain
Hash-linked, append-only
tamper-evident
chain link
next.previous_receipt_hash = sha256(canon(receipt))Evidence Bundle
Receipt chain + signed checkpoint + Merkle proofs in one portable file. On aga-proxy each receipt binds the policy by hash; the policy itself is not included
offline-verifiable by any third party
Three phases; what gets exported differs by package. On aga-proxy, each recorded tools/call decision is a signed receipt, chained into one exported sequence (known issue 7 on /security describes the calls refused without a receipt). aga-mcp-server 3.6.0 through 3.6.2 keeps its measurement and quarantine receipts in a separate chain that its bundle export does not include.
Worked exampleOne decision, end to end
An agent asks to fetch a URL. Follow that one decision through aga-proxy, from its policy file to the signed receipt that anyone can re-derive offline. A representative fixture in the shipped formats, not live data.
One decision, end to end
An agent asks to fetch a URL. Follow that one decision through aga-proxy, from its policy file to the signed receipt that anyone can re-derive offline. A representative fixture in the shipped formats, not live data.
{ "mode": "allowlist", "constraints": { "mcp.fetch": { "name": "mcp.fetch", "allowed": true } } }An unsigned JSON file passed with --policy. aga-proxy signs the SHA-256 of its canonical JSON into every receipt as policy_reference, which binds the policy to each decision by hash.
tools/call "mcp.fetch" → PERMITTED ("tool permitted by allowlist")aga-proxy decides per tools/call, against the policy. Scheduled hashing of an executable image or configuration is in the private reference runtime; memory sampling is architecture only.
{ "timestamp": "2026-05-24T18:42:11.000Z", "method": "tools/call", "tool_name": "mcp.fetch", "decision": "PERMITTED", "reason": "tool permitted by allowlist", "policy_reference": "3b1f..", "previous_receipt_hash": "4f9c..", "signature": "8e2a.." }An excerpt of the receipt's 15 fields, signed with Ed25519 over its canonical JSON.
next.previous_receipt_hash = sha256(canon(receipt)) = 7b3e..Append-only. Against the pinned key, reordering or deleting a receipt breaks the chain forward, and deleting the last one contradicts the signed checkpoint.
$ aga-verify evidence-bundle.json --pubkey <key> → VERIFIEDRecompute each receipt hash, walk the chain, re-check every signature. No callback to us.
One decision, three artifacts, one signed chain under one signing key, re-derivable offline from the bundle alone.
Cryptographic primitives policy
Industry-standard primitives. No proprietary algorithms.
Cryptographic primitives policy
Industry-standard primitives. No proprietary algorithms.
Both the hash and the signature algorithm are named in a single suite identifier, signed in each receipt and the checkpoint and repeated, unsigned, on the bundle (for example Ed25519-SHA256-JCS, or ML-DSA-65+Ed25519-SHA256-JCS). aga-verify, aga-proxy verify and /verify check the bundle's and the checkpoint's identifier, and aga-governance 0.3.2 the bundle's and each receipt's; each reports FAILED for a bundle whose suite it does not recognize, and /security lists which labels each leaves unchecked.
Checked against a pinned key, the suite cannot be silently downgraded: a changed receipt or checkpoint identifier breaks its signature, and an unknown bundle identifier fails closed. That keeps the architecture algorithm-agile without softening verification.
Ed25519
Policy-artifact and receipt signatures for classical deployments, and the reference baseline outside the post-quantum threat model.
ML-DSA-65
Post-quantum receipt and checkpoint signatures at NIST security category 3, inside the hybrid composite, which the npm package ships as a library export; it does not sign policy artifacts.
SHA-256
Addresses every artifact and chains each receipt to the one before it; also the leaf hash beneath the Merkle checkpoint.
JSON canonicalization
A JCS-lineage canonicalization with sorted keys, defined by the conformance vectors, so signature inputs reproduce identically across implementations.
Merkle Trees
A signed checkpoint with per-receipt inclusion proofs. The format carries a proof for each receipt; the published verifiers check the whole bundle.
Cryptographic FoundationStandard cryptography, checked by two implementations that agree
Standard cryptography, checked by two implementations that agree
AGA is built from published primitives: Ed25519 (RFC 8032), SHA-256, and the NIST post-quantum standard ML-DSA-65 (FIPS 204). The canonicalization, the Merkle tree and the composite encoding are AGA-defined and pinned by published vectors. The post-quantum primitive is checked against NIST known-answer vectors in the private reference runtime's test suite, and in the public repository two implementations in different languages are shown to agree on all 28 v2 cases. The composite is not the IETF LAMPS composite, which is still an Internet-Draft, and does not interoperate with it.
Two profiles, one construction. A signed record carries a versioned signature profile. The classical profile, live today, signs with Ed25519 (RFC 8032), with SHA-256 chaining the receipts and binding the signed checkpoint. The hybrid profile, implemented as a library export and cross-verified by two oracles in different languages (@noble in JavaScript and CIRCL in Go), signs with a composite of ML-DSA-65 (NIST FIPS 204) and Ed25519: both components cover the same bytes, and both must verify for the record to be accepted. An attacker would have to break ML-DSA-65 and Ed25519 to forge a record, so it holds as long as either scheme stands. Canonicalization, hashing, the Merkle tree, and the checkpoint are identical across profiles. Only the signature primitive changes.
Built to migrate, not to break. A verifier reads the profile from the bundle and checks the signatures accordingly. Records signed under the classical profile stay verifiable, and a verifier that understands the hybrid profile still accepts them. Post-quantum migration is additive: the profile is defined and cross-verified before any record needs to depend on it.
Supported signature modes
Classical
- Algorithm
- Ed25519-SHA256-JCS
- Security
- Ed25519, ~128-bit classical
- Status
- Default · live
Hybrid (post-quantum)
- Algorithm
- ML-DSA-65+Ed25519-SHA256-JCS
- Security
- Ed25519 (~128-bit classical) and ML-DSA-65 (NIST security category 3); both must verify
- Status
- Library export · cross-verified
| Mode | Algorithm | Security | Status |
|---|---|---|---|
| Classical | Ed25519-SHA256-JCS | Ed25519, ~128-bit classical | Default · live |
| Hybrid (post-quantum) | ML-DSA-65+Ed25519-SHA256-JCS | Ed25519 (~128-bit classical) and ML-DSA-65 (NIST security category 3); both must verify | Library export · cross-verified |
Hybrid mode produces both signatures. Verification requires both to pass. No downgrade fallback. Ed25519-only artifacts remain verifiable.
Checked by two implementations
In the private reference runtime's test suite (evaluation access), the post-quantum primitive is checked against the NIST ACVP known-answer vectors for key generation, signing, and verification, a fixed external oracle; the public repository does not run those vectors. Beyond those vectors, the implementations are exercised against the shared cross-language conformance corpus, 61 classical and 28 post-quantum cases, and the equality check is concrete: for the same input they must produce byte-identical canonical JSON, identical signature bytes, and the same Merkle root, and each must verify what the others produced. The composite encoding around it is AGA-defined, so there the evidence is two implementations in different languages agreeing on all 28 cases, with no external oracle.
Measured performance
Measured on the Go reference code (go test -bench, evaluation access); the published Node gateway was not benchmarked. Per decision, the cryptography is small next to the tool call it records. In 3.6.0 through 3.6.2 the bottleneck is elsewhere: bundle export is quadratic in the receipt count and holds governed calls while it runs. The classical Ed25519 path signs in tens of microseconds with one 64-byte allocation and verifies with none; the hybrid figure is dominated by the ML-DSA-65 component and is reported as the full per-decision sign-and-verify cost, not a best case. The full benchmark tables, key and signature sizes, and the standard each primitive implements are on the specification page.
View specification and benchmarksWhat a verified record proves, exactly
A passing verification against a key you pinned proves the signed record has not changed since it was signed, and any third party can re-derive it offline with no callback to us. It does not rule out the key holder re-signing a different history. It does not prove an agent was prevented from acting; whether an action was blocked is a property of how the system is deployed. It does not prove a recorded value is true, and says nothing about identity or legal standing. The proof is about the record, because an instrument that overstates what it measures is not an instrument.
MCP integration
Cryptographic governance for agentic tool use over the Model Context Protocol.
MCP integration
Cryptographic governance for agentic tool use over the Model Context Protocol.
Tool-call request path
each evaluated tools/call is recordedAI Agent
model + reasoning
MCP Client
tool discovery
Gateway
MCP gateway, signing boundary
MCP Tool
external resource
AI Agent
model + reasoning
MCP Client
tool discovery
Gateway
MCP gateway, signing boundary
MCP Tool
external resource
In a governed deployment, tool calls are routed through the gateway. The published aga-proxy listens on raw TCP (newline-delimited JSON-RPC) and bridges to a stdio MCP server (its --upstream-url mode forwards raw JSON-RPC over HTTP POST and does not implement the MCP Streamable HTTP transport, so a spec-conformant HTTP MCP server rejects it); a standard stdio MCP client reaches it through a stdio-to-TCP relay, which the package does not include. In the HTTP mode, a message that repeats its method member can carry a tools/call that the proxy never evaluates and that no receipt records (known issue 6 on /security); the stdio default is not affected. The gateway holds the signing keys; when it runs under its own identity, the subject holds none and cannot mint a call that passes as approved or a valid receipt. Routing is arranged by the deployment: when the subject reaches external tools only through the gateway, the chain holds a signed receipt for each tools/call the gateway evaluated (known issue 7 on /security describes the exceptions). What AGA proves is the record of calls that went through the boundary, not that no call ever evaded a misconfigured or root-compromised host.
The Model Context Protocol defines how agents discover and invoke external tools.
aga-proxy sits in the MCP tool-call path and signs a governance decision for every tools/call it evaluates (known issue 7 on /security describes the calls refused without a receipt). The policy file lists the approved tools and constraints; each call is evaluated against it, and each decision produces a signed receipt, carrying the SHA-256 of the policy's canonical JSON, that chains onto the receipt before it. That makes AGA the cryptographic decision-and-evidence record for securing autonomous agents at the protocol boundary, where an application that routes through the gateway cannot reach the tool without producing a signed receipt in the stdio default (with an HTTP upstream, see known issue 6 on /security).
ArchitectureTwo-process separation.
Two-process separation.
Core security property, when the gateway runs under its own identity: the governed subject holds no signing keys. On aga-proxy it has no tool that changes its policy. In aga-mcp-server 3.6.0 through 3.6.2 the MCP client can re-attest its own baseline through attest_subject, and the exported bundle does not record it.
Gateway (Governance Engine)
- Holds the signing key
- Parses and validates policy artifacts
- Signs each decision: PERMITTED or DENIED for tool calls (aga-proxy, whose default profile, permissive, denies nothing on policy grounds; policy denial needs --profile standard or restrictive, or a --policy file in allowlist or denylist mode); QUARANTINE on a measurement mismatch and TERMINATE on a TTL expiry found at the next measurement (aga-mcp-server, into its in-process chain, which the exported bundle does not include)
- In aga-mcp-server, a revoke_artifact call terminates the active artifact at once and adds a REVOCATION event to that chain; the exported bundle carries the revoke_artifact call (PERMITTED) and every later governed call, DENIED with GOVERNANCE_BLOCKED, until the governed client calls attest_subject. That call lifts a quarantine, revocation or termination, and the exported bundle does not record it
- Generates and signs all receipts
- Manages the receipt chain
Subject (Governed Agent)
- Holds no signing keys (when the gateway runs under its own identity)
- Cannot sign a receipt
- In aga-mcp-server 3.6.0 through 3.6.2, can re-attest its own baseline through attest_subject; the exported bundle does not record it
- Cannot make an action it takes outside the gate appear governed
- Cannot forge or reorder an exported receipt without breaking the chain against the pinned key
- In aga-mcp-server, measured on request against the subject hashes sealed into its artifact (the content hash and the metadata hash), over content and metadata the caller supplies
These hold because the subject has no key. Process isolation between the gateway and the subject is supplied by the deployment, and is stated as an assumption in the security model below.
Deployment architecture
Where the gateway exists in your stack.
The gateway runs as its own process and holds the gateway's signing key. (In 3.6.0 through 3.6.2 a key supplied through AGA_GATEWAY_KEY or AGA_GATEWAY_KEY_FILE is also inherited by a stdio upstream; see /security.) The governed agent runs as a separate process, with no key access when the gateway runs under an OS identity the agent cannot read. That two-process separation is the core security primitive. In the request-access Kubernetes component, an admission webhook refuses to start a labelled pod whose artifact signature or validity window fails; the published packages do not include it.
In the request-access Kubernetes component the gateway deploys as a sidecar alongside the agent container. The published aga-proxy runs as a co-process on the same host. For agents routed to the outside world through MCP, each tool invocation that transits the gateway is decided and recorded at the protocol layer before it leaves the boundary.
AGA operates at the process level, so its trusted computing base is the gateway process plus the host kernel and the deployment's process isolation. An operator with root on the host sits inside that boundary and can interfere with governance. Bundles already exported off the host keep their signatures through a later host compromise. Anything signed after the compromise is suspect, because the published gateway holds its key in process memory or in a file on that host. Where the host operator must sit outside the boundary, the architecture runs the gateway inside a Trusted Execution Environment (Intel SGX, ARM TrustZone, AMD SEV), which shrinks the trusted base to the enclave. No published component does this today, and none records an enclave attestation quote.
Container Sidecar
Deploys alongside agent containers in Kubernetes or Docker. This topology is the request-access component, not a published package; the published aga-proxy runs as the co-process shown next.
Edge Co-Process
Paired process on bare metal or any single host: the gateway runs on any host with Node 20 or later and listens on a TCP port (in 3.6.0 through 3.6.2 on every network interface, with no authentication, so firewall it: known issue 1 on /security). No container runtime is required, and the same standard cryptographic primitives carry into disconnected operation.
TEE-Hardened (architecture)
The gateway runs inside a hardware enclave, moving the host operator outside the trusted base while the enclave holds. In the architecture, the enclave’s attestation quote becomes one measurement input, so a verifier sees which enclave produced the record. No published component implements this today.
Ten ClassesMeasurement inputs.
Measurement inputs.
Architecture, not a shipped feature: the design evaluates runtime state through policy-defined measurement classes, each at its own cadence. The published aga-mcp-server measures on request over content the caller passes. No published package implements scheduled measurement, and memory and control-flow measurement exist in no implementation today.
Binary integrity
exe + weights hash
Configuration
runtime params
Module checksums
loaded modules
Container image
image digest
SBOM digest
supply chain
Environment
host metadata
File system
filesystem state
Network config
routes + interfaces
Memory samples
region snapshots
Control flow
execution graph
In the architecture, classes apply at different cadences: binary verification once on startup, configuration every minute, memory sampling every second. The sealed Policy Artifact sets which classes apply and how often. Memory and control-flow are sampled, not observed continuously, so the sampling interval is the security model’s detection window.
Integrity drift detection
Hash comparison against the sealed baseline, sample by sample.
Integrity drift detection
Hash comparison against the sealed baseline, sample by sample.
Hash sample stream
representative sequenceA representative measurement sequence from the architecture, not live telemetry; the published aga-mcp-server measures only when called. Each tick is one measurement. Every measurement hash is compared for exact equality against the sealed value; the first hash that does not match fires a DRIFT_DETECTED receipt. There is no tolerance band. Equality is binary.
In aga-mcp-server, each measurement is compared with the hash sealed into its artifact, and a mismatch produces a signed drift receipt, which in 3.6.0 through 3.6.2 stays in that server's process rather than the exported bundle. Governance decisions rest on deterministic hash equality: the comparison checks whether values match, with no interpretation of model outputs.
Hardware IndependenceNo hardware root of trust required.
No hardware root of trust required.
AGA needs no internet connectivity or cloud service: it runs offline on commodity hardware, and the gateway runs on any host with Node 20 or later, listening on a TCP port (in 3.6.0 through 3.6.2 on every network interface; known issue 1 on /security). In the architecture, hardware attestation becomes a measurement input when it is available; no published component records it today.
AGA architecture
Software cryptography on every platform.
The gateway signs governance decisions using standard primitives (Ed25519, SHA-256, JSON canonicalization; an ML-DSA-65 hybrid is a library export, not the live default) and is designed to run on commodity hardware, including isolated networks with no internet access. In the architecture, TPM, TEE, HSM or secure-boot attestation quotes become measurement inputs when present; no published component records them today.
Standard cryptographic primitives
Runs where TPM, TEE, or HSM are unavailable
TPM 2.0 quotes as an input (architecture)
Intel SGX, ARM TrustZone, AMD SEV as inputs (architecture)
HSM-backed key custody on the roadmap, never required
Secure-boot chain as one measurement (architecture)
Governance ModeQuarantine: a signed decision, containment wired per deployment
Quarantine: a signed decision, containment wired per deployment
Phantom execution is the quarantine mode of the governance design. What ships today is narrower. In the reference MCP server (aga-mcp-server 3.6.0 through 3.6.2) the drift finding and the QUARANTINE decision are recorded in a receipt in that server's in-process chain, which is not part of the exported bundle. During quarantine the server signals it in-band: each governed call is refused with an explicit GOVERNANCE_QUARANTINED error and recorded as a DENIED receipt carrying that reason. aga-proxy has no integrity measurement and no quarantine mode. Keeping outputs flowing into the sealed record while cutting them off downstream is the design, and an integration point wired per deployment. By design we do not claim the quarantined agent cannot detect containment.
In the design, the result is an observation window for a subject that has already drifted. In 3.6.0 through 3.6.2 the governed client can end quarantine itself by re-attesting through attest_subject, and the exported bundle does not record that. In the shipped reference server, each governed call during quarantine is recorded as a DENIED receipt with the reason GOVERNANCE_QUARANTINED, which any verifier can tell apart from normal entries; the eight-field forensic capture is implemented and tested in the demo orchestrator and is not in a published package. In the architecture, an investigator observes a drifted agent under containment that the deployment wires.
In aga-mcp-server 3.6.0 through 3.6.2 the server records QUARANTINE on a measurement mismatch, and TERMINATE on a TTL expiry found at the next measurement, in its in-process chain. A revoke_artifact call terminates it at once and records a REVOCATION event in that chain; the exported bundle carries the revoke_artifact call (PERMITTED) and every later governed call, DENIED with GOVERNANCE_BLOCKED, until the governed client calls attest_subject. That call lifts a quarantine, revocation or termination, and the exported bundle does not record it. SAFE_STATE and ALERT_ONLY are decision types in the library that the server does not reach. The handlers that effect each action are wired per deployment.
Verification without the argument payload
Arguments are committed by SHA-256 hash. Every receipt records an arguments_hash, a SHA-256 of the tool call's arguments, so anyone can verify the signatures, the hash chain, the Merkle inclusion, and the signed checkpoint without the arguments leaving the producing system. Three things do appear in clear text: tool names and, in 3.6.2 and earlier, the offending path in a path-constraint denial's signed reason and the matched pattern in a denied-pattern denial's. The hash is unsalted, so anyone holding a bundle can confirm a guessed argument set, and low-entropy arguments can be enumerated.
receipt.arguments_hash = SHA-256(canon(arguments)) // the receipt stores this hash in place of the argumentsAn auditor confirms the chain is intact, that every link hashes to the next, and that every signature verifies, all without seeing the argument contents. When a specific payload must be disclosed, the recipient re-hashes it and confirms it matches the arguments_hash the producer signed, so a withheld or swapped payload cannot pass as valid. The auditor verifies the record and its ordering, not the truth of the data inside it.
Security ModelWhat AGA assumes, and what it proves.
What AGA assumes, and what it proves.
Assumes
- 1.The gateway process is not compromised at signing time. A compromised gateway could sign an artifact or receipt of an attacker’s choosing.
- 2.In the architecture, the measurement cadence is set to catch drift before it causes harm. The right interval is a per-deployment risk decision.
- 3.The deployment environment applies process isolation between the gateway and the governed subject, so the subject cannot reach the signing keys.
- 4.The signature algorithm (Ed25519 in the published gateway; the ML-DSA-65 composite where the library profile is used) and SHA-256 stay computationally secure for the artifact’s validity period.
- 5.The host clock is roughly accurate. AGA records a self-reported time, so receipts establish ordering and integrity, not an independently attested wall-clock time.
Proves
- Without the issuer's signing key, any modification to a sealed policy artifact invalidates its signature, so a verifier checking against the issuer's pinned key rejects the altered artifact. The property holds for a verifier that pins the issuer's key, and that check ships only in the request-access Go runtime: the published aga-mcp-server signs its artifact under an issuer key generated per process, so no issuer key survives a restart to pin. It does not return that artifact; delegate_to_subagent returns a signed child artifact, which checks against the issuer_public_key that get_server_info reports for the same process only. The request-access admission webhook does not pin one: it checks the artifact against the key the artifact carries, and its validity window is outside the signed fields, so it does not stop a re-signed or re-dated artifact. Whether a rejection stops the subject from launching is a deployment property.
- Receipt ordering and contents, as the verifier reads them, are tamper-evident against the pinned key: deleting or reordering an existing entry breaks the hash chain forward, or, for a deletion at the tail, contradicts the signed checkpoint's leaf count and chain head, and a verifier detects it. A verified chain proves the entries it contains are intact and correctly ordered, not that none is missing, and not that it is the latest: an earlier genuine export presented as the current one verifies, because the verifier has no freshness input. A field name repeated in the file still verifies: the verifiers read the last copy, so read the verifier's parsed output (known issue 5 on /security).
- Evidence bundles can be verified by any party using standard cryptographic primitives, with no callback to the producing system.
- Verification never requires disclosing argument contents, so auditors verify the record without the data inside the arguments. In 3.6.0 through 3.6.2 a path-constraint denial names the offending path in its signed reason, a denied-pattern denial names the matched pattern (which shows the argument contained it), and tool names are plaintext.
- Each recorded governance decision is signed. A receipt in the chain is bound to the key that produced it, so a holder of that key cannot credibly claim a chain entry was not produced under it. This binds the key, not a legal identity, and holds while that key is uncompromised.
Indexed by adversary
If an attacker can do this, here is what AGA catches, what it does not, and what closes the gap.
| Adversary can | AGA detects | AGA does not | Residual mitigation |
|---|---|---|---|
| Tamper with the sealed policy artifact | The signature breaks; a verifier that pins the issuer key rejects the altered artifact. That check is in the request-access Go runtime; the published aga-mcp-server signs under a per-process issuer key, so there is no key to pin across restarts | Nothing beyond detection | Detection is the property |
| Delete or reorder existing receipts | The hash chain breaks forward from the changed entry; a deletion at the tail leaves the chain intact and contradicts the signed checkpoint's leaf count and chain head. Verifiers detect both | Recover deleted content | Bind the chain with a signed checkpoint |
| Withhold or truncate the receipt tail (compromised gateway) | A single receipt can be proven to belong to a checkpoint (a property of the format; the published verifiers check the whole bundle) | Prove no entry is missing from the chain alone | Compare against a copy of a signed checkpoint held outside the gateway |
| Present an earlier genuine export as the current one | Nothing: it verifies, because the verifier has no freshness input | Tell an earlier export from the latest | Compare against the latest checkpoint you obtained independently |
| Swap the binary or config at runtime | In the architecture, the next measurement catches it; the published aga-mcp-server measures only when called, over content the caller supplies | Prevent action between two measurements | In the architecture, a faster cadence at higher resource cost |
| Hold the signing key and re-sign a different history | Nothing, if the new history is internally consistent | Tell it apart from the history originally signed | Keep a copy of an earlier signed checkpoint outside the producer, and compare |
| Exfiltrate the gateway signing key | Not detected by AGA itself; later artifacts and receipts are suspect | Stop forged artifacts and receipts until rotation | Scheduled rotation, HSM storage, out-of-band key verification |
| Run an ungoverned subject beside a governed one | Out of scope | Prove anything about the ungoverned subject | Operator deployment policy |
TransparencyFailure modes and limits
Failure modes and limits
These are the scenarios where AGA offers reduced or no protection.
Gateway Compromise
An attacker who exfiltrates the gateway signing key can forge artifacts and receipts until the next rotation. Mitigations are scheduled rotation, HSM storage, and out-of-band key verification.
Measurement Cadence
In the architecture, AGA detects drift at measurement intervals; the published aga-mcp-server measures only when called. Between intervals, a compromised subject could act outside policy. Faster cadence reduces the window but increases resource cost.
Key Management
AGA specifies the cryptographic protocol but not the key management infrastructure. Deployments must implement secure key storage, rotation, and revocation appropriate to their threat model.
Deployment Governance
AGA proves the record of the governance decisions it made. It does not prevent an operator from deploying an ungoverned subject alongside a governed one; that subject simply produces no receipts. Deployment policy governance is the operator's responsibility.
Record Integrity, and Its Boundary
A verified chain proves the entries it contains are intact and correctly ordered. It does not by itself prove none is missing. The subject cannot remove a receipt from an exported bundle without failing verification against the pinned key, but receipts not yet exported live in the gateway's memory and are lost if its process ends, and a compromised gateway could withhold one or truncate the tail. The signed Merkle checkpoint binds a root, so a single receipt can be proven to belong to a checkpoint; detecting a truncated tail requires comparing against a copy of a checkpoint held outside the gateway. The same holds for an earlier genuine export presented as the current one: it verifies, because the verifier has no freshness input.
The verifier is published. Check us with it
The same Ed25519-SHA256-JCS construction the gateway signs with is published as a standalone command-line verifier on npm. It is our own package, so the point is not that we vouch for it: run it yourself. The format is specified so you can re-implement it. Version 2.2.3 was manually published without a SLSA provenance attestation. Its retained archive hash and registry signature are separate checks; inspect the release evidence.
What you get depends on the key. Without a pinned key the verifier confirms integrity only: that the bundle is internally consistent under its own key. A separately trusted expected key also checks that the record was signed under that key. Connecting the key to a person or organization requires a trusted identity mapping. A key copied from the file does not establish its issuer, because a re-signed bundle carries its own matching key. A pin does not rule out the key holder signing a different history under the same key.
The sample-bundle signing key is a published fixture that no gateway holds, and the live demo gateway key is published at that gateway’s /pubkey. A deployment’s key persists only when AGA_GATEWAY_KEY or a valid AGA_GATEWAY_KEY_FILE is set; otherwise it is per-process.
Recorded decisions, signed and chained. Verifiable offline.