Skip to content

general-liquidity/agent-disclosure-protocol

Repository files navigation

Agent Disclosure Protocol (ADP)

The wire format for verifiable agency

Agents are starting to transact with each other with no way to answer the first question of commerce: who am I dealing with, and what are they committed to? ADP is the disclosure an agent publishes before it transacts - so a counterparty can verify and decide before value moves, not after a loss.

CI npm tests conformance interop node runtime dep license types

Why · Install · Disclose & verify · The disclosure · Where it fits · Conformance · Spec · Threat model


Why

ADP is not a wallet, a rail, or an identity registry. It is the vendor-neutral disclosure layer that sits above the payment rails (x402, AP2, ACP, cards) and beside the identity protocols (ERC-8004, AIP, Visa TAP), enforcing a single loop:

An agent reveals what it is committed to before it transacts; a counterparty verifies that disclosure against its own policy and a live key-possession handshake, and decides transact or refuse - before any value moves. Asymmetric signatures mean anyone can check, with no shared secret and no registry.

This is the "pluggable behavioural-trust layer" the rest of the agentic-commerce stack openly defers. ERC-8004 anchors an agent's identity to a wallet and names a verification layer it does not itself fill. ADP is that layer: not just who is this agent, but what is it committed to, is that commitment enforced, and has it behaved - checked before the transaction clears.

The protocol has one runtime dependency (zod) and signs with node:crypto only. It is not just claimed to be language-neutral, it is proven to be: native verifiers and emitters in TypeScript, Go, Python, Rust, and C all reproduce the canonicalization byte-for-byte and cross-verify each other's ed25519 signatures, gated by a shared conformance contract on every commit (see Conformance). AgentWorth is the reference implementation that populates a disclosure from a live, enforced governance gate.

Install

npm install @general-liquidity/agent-disclosure

Verify a counterparty before you transact - the whole disclose-before-settle loop, end to end:

import { verifyCounterparty } from "@general-liquidity/agent-disclosure";

const verdict = await verifyCounterparty(fetch, "https://agent.example", {
  now: new Date().toISOString(),
  requireEnforcedConstitution: true,   // the constitution must BE the running gate, not a claim
  requireNonCustodial: true,
  minRedTeamGrade: "B",
});

if (verdict.decision === "transact") {
  // proven: valid signature, fresh, policy met, live key possession - before any value moves
} else {
  verdict.reasons;   // every failed check, for transparency
}

Emit a signed disclosure other agents can verify (serve it at /.well-known/agent-disclosure):

import { generateAgentKeyPair, signDisclosure } from "@general-liquidity/agent-disclosure";

const key = generateAgentKeyPair();          // agentId === key.publicKeyHex
const signed = signDisclosure(myDisclosure, key);   // an ed25519 envelope, verifiable with no shared secret

Build myDisclosure from your own runtime (a fluent DisclosureBuilder is exported), or use the AgentWorth builders to populate it from a live gate, mandate set, and signed audit chain.

Or from the CLI (agent-disclosure, shipped in the package):

agent-disclosure keygen                                        # mint an ed25519 identity
agent-disclosure sign --in disclosure.json --key key.hex       # sign a disclosure document
agent-disclosure verify-file signed.json --require-enforced    # verify a file against a policy
agent-disclosure verify-url https://agent.example              # fetch + verify a peer over HTTP

Disclose and verify

A verifier runs four steps. verifyCounterparty implements all of them; any failure on either the static or the live leg is a refuse, and the default posture is fail-closed.

counterparty                                          agent
    │  GET /.well-known/agent-disclosure  ─────────────►│
    │◄──────────────  signed disclosure (ed25519)       │
    │                                                   │
    │  evaluateDisclosure(disclosure, policy)           │  signature · freshness · enforced ·
    │                                                   │  grade · custody · history · anchor
    │                                                   │
    │  POST /agent-disclosure/respond  (fresh nonce) ──►│
    │◄────  signed response (nonce + live audit head)   │  live key possession + history currency
    │                                                   │
    ▼  decision: transact / refuse   (before any value moves)
Step What it proves
1 · Fetch Resolve the counterparty's commitments from a well-known URI on its own origin. No registry, no directory, no out-of-band exchange.
2 · Evaluate evaluateDisclosure runs the verifier's VerificationPolicy. Signature + freshness are on by default; every other requirement (enforced constitution, required hard constraints, red-team grade, non-custodial, attestation level, history, audit anchor) is an opt-in field set to the verifier's risk appetite.
3 · Handshake A fresh-nonce challenge proves the counterparty holds the signing key right now and that its audit head is current - which a captured static document cannot. The response signs an RFC 9421 (HTTP Message Signatures) signature base, so a standard implementation reads the exact covered set + params. Defeats identity replay.
4 · Decide transact only when both legs pass. Cheap and deterministic, so it can run before every transaction (see the economic-viability model).

The disclosure document

What an agent exposes. Each field group maps to a surface serious agent products already maintain, and each carries the threat it makes legible. SignedDisclosure wraps the document in an ed25519 envelope whose public key is the agentId. The same disclosure also serializes as a flattened JWS (EdDSA) envelope (signDisclosureJws) for JOSE-native stacks; verifyAnyDisclosureSignature accepts either shape, discriminated by structure, so both encodings share one signature path.

Field group What it is Threat it makes legible
System-prompt fingerprint sha256 of the composed system prompt injection-mediated substitution
Constitution (enforced) the hard deny-list + gate parameters; enforced = these rules ARE the running gate a promise vs an enforced gate
Tool inventory tools + permission boundaries (gated · read-only · operator-only) hidden capability / privilege
Capital envelope the mandate set - scoped, capped, expiring spend authority - and custody unbounded spend
Operator identity who deployed it, the deniability boundary, the attestation level + scheme an unaccountable operator
Deployment history a summary bound to a signed, hash-linked audit chain (chainAnchor) history forgery
Red-team attestation a grade against a public adversarial corpus self-grading on a private rubric
Model identity · provenance (optional) a declared model fingerprint; how each field was derived model swap; claim-weighting

The load-bearing field: enforced

When enforced is true, the disclosed constitution is the gate actually running, not a description of intent. In the AgentWorth reference implementation it is populated directly from the live deny-list and gate config, and enforcementEvidence names the gate. A verifier that sets requireEnforcedConstitution: true refuses any counterparty whose constitution is merely declared. This is the difference between a disclosure and a promise: the rules are not prose a model can be talked out of, they are the function that decides whether value moves.

Extending without a core edit

The attestation scheme and the document-level extensions map are open by reverse-domain id (e.g. com.visa.tap): a new attestation scheme or vendor field is a namespace publication, not a core enum edit that forces a five-way re-port. A verifier acts only on extension keys it recognizes and ignores the rest, so the wire format stays forward-compatible across stacks.

Where it fits

ADP rides above the payment rails an agent settles on and composes with the identity protocols it authenticates with. The protocol is vendor-neutral; live rail and identity adapters live in the AgentWorth reference implementation.

Payment rails it gates settlement over

Rail Relationship
  x402 HTTP-402 + stablecoin settlement - the disclosure is checked before the 402 is authorized.
  AP2 Agent Payments Protocol (Google + FIDO) - an AP2 payment mandate maps onto the disclosed capital envelope.
  Agentic Commerce Protocol ACP (OpenAI + Stripe) - verify the merchant agent before the delegated checkout settles.
  UCP ·   MPP Delegated-checkout and rail-agnostic machine-payment protocols.
  On-chain (ERC-20 / USDC) The agentId is an ed25519 key the disclosure signs with - bindable to a wallet-anchored identity.

Identity & trust protocols it composes with

Protocol Relationship
  ERC-8004 Anchors agent identity to a wallet and names a pluggable verification layer it does not fill. ADP is that layer.
  AIP (Agent Identity Protocol) ·   Visa Trusted Agent Protocol Carried in the operator attestation field; the evidence KYC-bound rails recognize, so a regulated rail can terminate at an agent endpoint.
  Self (self.xyz) ZK proof-of-personhood (src/self.ts) - recognized as an operator-attestation scheme (xyz.self). ADP does structural validation of Self's on-chain ref / off-chain proof result (including the inverted OFAC flag - isOfacValid === true means sanctioned) and delegates the full Groth16 / Celo verification to an injected seam, exactly as it treats ERC-8004.
  World ID (Worldcoin) Proof-of-unique-personhood (src/worldid.ts) - recognized as an operator-attestation scheme (org.world). ADP structurally validates the Groth16 / Semaphore proof shape (app_id / action / nullifier_hash / merkle_root / proof / verification_level) and delegates the Developer-Portal /verify or on-chain World ID Router check to an injected seam; an orb-level proof maps to registry_attested, others to signed.
  World Agent (worldcoin/agentkit) Human-backed agent (src/worldagent.ts) - recognized as an operator-attestation scheme (org.world.agent). An agent wallet registered in the on-chain AgentBook via a World ID proof signs a CAIP-122 / SIWE challenge; ADP structurally validates the message + address/signature, EIP-191-recovers the signer, and delegates the AgentBook lookupHuman(address) read to an injected resolver; a registered (human-backed) agent maps to registry_attested, a wallet-controlled-but-unregistered one to signed.
  Human Passport (Gitcoin) Humanity-reputation score (src/humanpassport.ts) - recognized as an operator-attestation scheme (tech.human.passport). ADP structurally validates the address + Unique Humanity Score and delegates the Passport-API / EAS score fetch to an injected scorer; a score banded against the threshold (default 20) maps to a level, a passing score to signed.
  Agent Client Protocol ·   MCP Editor/agent surfaces a disclosed agent is driven from. The disclosure rides the MCP initialize handshake as an opt-in capabilities.experimental entry (src/mcp.ts); a counterparty verifies the disclosure's ed25519 envelope (the trust root) and binds the advertised agentId to it before driving a single tool call.

Agent discovery & messaging

Protocol Relationship
  A2A (Agent2Agent) A2A (Linux Foundation) - the disclosure rides an Agent Card as a capabilities.extensions[] entry (src/a2a.ts). A counterparty fetches the card at discovery, verifies the disclosure's ed25519 envelope (the trust root) - the card's own RFC 7515 signatures[] JWS is treated as origin tamper-evidence - and decides transact/refuse before the A2A task runs.
SIWA (Sign-In-With-Agent) A SIWE/EIP-4361 login message whose subject is an agent account (src/siwa.ts). Its (address, agentRegistry, agentId) triple is exactly an ERC-8004 binding, so ADP mints a SIWA message from a disclosed agent and verifies one - EIP-191 recover (reusing the ERC-8004 secp256k1 path) for signed, an injected ownerOf resolver for registry_attested. verifySiwaAgainstDisclosure cross-checks the login and the disclosure describe one agent.

Discovery transport

The disclosure is served at /.well-known/agent-disclosure, a well-known URI on the agent's own origin; the live handshake sits beside it at /agent-disclosure/respond. Any verifier that can resolve a counterparty's base URL can fetch its commitments - turning the agent-discovery proposals circulating in the space into the concrete transport for the disclosure.

Conformance

Canonicalization is the interoperability crux: the signed bytes must be byte-identical across implementations, or signatures will not verify across stacks. conformance/ is a portable contract that five independent implementations (TypeScript, Go, Python, Rust, C) all pass on every commit:

Layer What it proves
Canonicalization + digest vectors (vectors.json) Every stack reproduces the canonical bytes + sha256 digests exactly.
Differential fuzzer (fuzz.json) 200 seeded-random values; all five stacks agree byte-for-byte. It already caught a real C-only divergence (embedded-NUL truncation).
Interop fixtures (interop.json) TS-minted, ed25519-signed disclosures; native verifiers reproduce every verdict + handshake, and native emitters reproduce the signatures byte-for-byte (bidirectional interop, no shared secret). Plus redaction / revocation / transparency cases.
Adversarial corpus (negative.json) A MUST-REJECT set (malformed, tampered, hostile input); every verifier rejects all of it and never crashes. It caught a real signature.algorithm check gap in two stacks.
Live cross-process A TS server serves a disclosure over a real socket; the TS, Go, Python, and Rust clients verify it against one live origin.
Schema drift guard (schema/) The committed JSON-Schema artifacts are generated from the zod source (npm run schema); a drift test (TS schema-drift + Go schema_sync_test) fails CI if an enum is edited without regenerating, keeping the schema and every port in sync.

SPEC.md is the normative protocol and docs/drafts/draft-gl-adp-disclosure-00.md is the IETF Internet-Draft; docs/ is a browsable mdBook and RELEASING.md covers tokenless (OIDC trusted-publishing) releases; the canonicalization + signed-bytes format is frozen (see the stability guarantees).

npm run conformance       # the TS conformance + fuzz + interop suite
node --import tsx scripts/conformanceReport.ts   # a pass/fail report across suites

Architecture

The vendor-neutral core has one runtime dependency (zod); the ERC-8004 on-chain and ZK range-proof modules use optional @noble / viem deps behind dynamic imports, so they never weigh on the core.

Group Modules
Core schema (document + both signed envelopes; namespaced attestation schemes + extensions), attestation (ed25519, deterministic canonicalization, the agentId-to-key binding, freshness, recursion-depth guard, the v2 flattened-JWS EdDSA envelope), versioning (schema version negotiation).
Verify verify + client (the policy language, deterministic verdict, the over-the-wire loop), cache (tiered + validity-window), guard + mutual (disclose-before-settle, both-sides verification), adapters (a verify-before-pay tool for any framework), verifierService (verify-as-a-service HTTP).
Handshake handshake (live nonce challenge-response, defeats identity replay).
Selective disclosure + ZK redaction (salted-commitment field hiding), negotiate (reveal exactly what a policy needs), zk (equality backend) + zkRange (real Pedersen + bit-decomposition range proofs over secp256k1), zkDisclosure (attach + require range proofs about hidden attributes as a disclosure feature).
Revocation + transparency revocation + revocationTransport (status list + fetch/honor over the wire), transparency + transparencyTransport + witness (CT-for-agents log, inclusion proofs, split-view monitor).
Identity (ERC-8004) erc8004 (agent-to-wallet binding), erc8004Onchain (secp256k1 EIP-191 recovery), erc8004Registry (viem registry read), erc8004Validation (read on-chain validationResponse scores), modelAttestation (declared model fingerprint).
Standards bridges did (the agentId as a did:key / did:web), vc (a disclosure as a W3C VC Data Model 2.0 with an ADP-namespaced adp-jcs-2024 Data Integrity proof - same signature, no second trust root), sdjwtvc (an SD-JWT-VC encoding that hides field names + count via decoy digests and binds a presentation to one verifier + nonce), a2a (carry/extract/verify a disclosure on an A2A Agent Card via a capabilities.extensions[] entry - the disclosure envelope is the trust root, the card's RFC 7515 signatures[] JWS is origin tamper-evidence), siwa (mint/verify a Sign-In-With-Agent (SIWE/EIP-4361) message from an ERC-8004 binding - EIP-191 recover + an injected ownerOf resolver → signed / registry_attested), self (recognize Self (self.xyz) ZK proof-of-personhood as an operator-attestation scheme - structural validation + an injected ZK/onchain verifier seam, with the inverted-OFAC semantics documented), worldid / humanpassport / worldagent (World ID / Gitcoin Passport / worldcoin-agentkit personhood schemes), enforcement (the falsifiable Proof-of-Enforcement verifier - verifyEnforcement replays a sampled gate decision to detect a gate that does not enforce what it discloses; policyHash byte-identical to the AgentWorth emitter), erc8004Validator (fill ERC-8004's named-but-unfilled validation hook), visatap (Visa Trusted Agent Protocol RFC-9421 interop), and optional library paths (@sd-jwt / jose / web-did) + referenceVerifiers (concrete impls of the personhood injected seams).
Discovery + ops discovery (.well-known fetcher + agent directory), keys (keyring, key files, and a signed key-rotation statement - the old key signs the move to the new one, chaining identity across a key change), monitor (disclosure diffing + downgrade alarm), statusList (W3C StatusList revocation), builder (a fluent disclosure builder), economics (which markets clear at verification cost C).
CLI cli - keygen / sign / verify-file / verify-url.
Native implementations Verifiers + emitters in go/ · python/ · rust/ · c/, each gated by the conformance contract.

The AgentWorth-specific half (the field builders that populate a disclosure from a live gate / mandate set / audit chain / SpendTrust run) deliberately does not lift out; it is the reference implementation. Any other agent product implements its own builders against the same schema.

Develop

tsx is bundled; the suite runs on Node ≥ 20.

npm install
npm test          # 229 TS tests across the protocol + every module
npm run conformance # the conformance vectors + fuzz + interop suite
npm run typecheck # tsc --noEmit, strict
npm run build     # tsc -> dist

The native implementations carry their own suites, all gated in CI:

( cd go && go test ./... )
( cd rust && cargo test )
( cd python && python -m pytest )
( cd c && make SODIUM_INC= SODIUM_LIB= LDLIBS=-lsodium test )   # needs libsodium

Tech stack

Technology Role
  TypeScript The whole protocol - strict, ESM, .ts imports
  Node ≥ 20 node:crypto for ed25519; no native build step
  Zod The single runtime dependency - schema validation + parse at the boundary
ed25519 Asymmetric signing - a counterparty verifies with no shared secret
  Go ·   Python ·   Rust ·   C Native verifiers + emitters, each gated by the conformance contract (crypto/ed25519 · cryptography · ed25519-dalek · libsodium)
@noble/curves · viem Optional, behind dynamic imports - secp256k1 (ERC-8004 + ZK range proofs) and the on-chain registry read
  GitHub Actions CI matrix: TypeScript · Go · Python · Rust · C · cross-process · mdBook

License

MIT © General Liquidity. A General Liquidity protocol - the wire format for Verifiable Agency, so an agent can prove what it is committed to before it transacts.


About

Agent Disclosure Protocol (ADP) is a vendor-neutral disclosure protocol for agent-to-agent commerce, the wire format for verifiable agency.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors