acdp-rs
Reference Rust library for the Agent Context Distribution Protocol —
ACDP v0.1.0 Final plus the v0.2.0 Trust & Hardening layer and the 0.3/0.4
drafts (ACDP_VERSION is 0.2.0).
ACDP lets agents publish immutable, producer-signed context descriptors,
retrieve and verify them locally, discover them by keyword, and follow signed
acdp:// references across registries. The Trust & Hardening layer adds
registry receipts, offline did:key verification, a transparency log,
witness cosigning, key revocation, and lifecycle/retraction events.
Spec: agentcontextdistributionprotocol/spec — RFC-ACDP-0001 through 0015 (0009 reserved). This crate implements 0001–0008 (core + retrieval/lineage/search), 0010 (registry receipts), 0011 (lineage-head receipts), 0012 (transparency log), 0013 (lifecycle/retraction), 0014 (key revocation), and 0015 (witness cosigning).
This is a Cargo workspace: the umbrella acdp crate is a thin facade that
re-exports a fine-grained set of crates under crates/
(acdp-primitives → acdp-jcs/acdp-safe-http → acdp-did → acdp-crypto →
acdp-types → acdp-validation → acdp-verify → acdp-producer →
acdp-client/acdp-server → acdp-cli), preserving the historical
acdp::{error, types, crypto, did, validation, verify, producer, client, registry, …} paths. Language bindings live under bindings/
(Python, Node.js, and a browser/edge WebAssembly verifier core published to npm).
Documentation
Guides that complement the rustdoc live in docs/:
- Getting Started — install, features, first publish/verify.
- Architecture — the hash/sign/state layering and module map.
- Producing contexts · Consuming & verifying · Errors & retries
- Security model — the SSRF/HTTPS/size defenses applied by default.
- Implementing a registry · CLI reference · Language bindings · Conformance & testing
These docs are additive to the specification and cite the relevant RFC sections rather than restating them.
Install
Conformance
This crate implements the acdp-consumer profile (RFC-ACDP-0001 §9.1):
- Verifies producer signatures end-to-end on every retrieved context.
- Resolves cross-registry
acdp://references with cycle detection, depth caps, SSRF defenses, and registry-DID web-binding verification. - Applies visibility rules client-side and tolerates unknown fields for forward compatibility.
The library also ships the building blocks (PublishValidator,
SsrfPolicy, validate_publish_request, compute_embedded_hash) that
registry implementers compose into acdp-registry-core /
acdp-registry-discovery / acdp-registry-federated services. See
acdp::profile for the typed profile vocabulary.
Glossary
- Body — the immutable JSON object representing a context.
- ProducerContent — the Body with the §5.7 exclusion set removed
(everything except the producer-controlled fields). The producer
signs ProducerContent; the SHA-256 of its JCS-canonicalized bytes
is the body's
content_hash. - RegistryState — the mutable, registry-derived state (
status, and in 0.2.0 the optional registry receipt) returned alongside the Body on retrieval. - Lineage — a chain of contexts representing successive versions of
the same logical work, identified by a stable
lineage_idderived from the v1 ctx_id. - JCS — JSON Canonicalization Scheme (RFC 8785). The deterministic
serialization used as the SHA-256 input for
content_hash. - DID — Decentralized Identifier (W3C).
did:webproducers resolve their keys over HTTPS; 0.2.0 adds offlinedid:key(Ed25519 + P-256, no network) resolved purely from the identifier. - Registry receipt — a registry's signed attestation that it stored a context (RFC-ACDP-0010), verified by recomputing the ProducerContent hash rather than trusting the echoed value.
Features
| Feature | Default | Description |
|---|---|---|
client |
✓ | RegistryClient, VerifiedContext, WebResolver, CrossRegistryResolver |
server |
✗ | PublishValidator, RegistryServer, InMemoryStore for registry impls |
tracing |
✗ | #[instrument] spans on async ops; pulls in tracing (no subscriber) |
test-transport |
✗ | Test-only permissive HTTP transport for in-process mock registries |
The acdp binary is its own crate (cargo run -p acdp-cli -- …). Offline
did:key verification and the receipt types work under
--no-default-features (no HTTP stack).
Security defaults
The library applies these defenses out of the box (RFC-ACDP-0006 §7, RFC-ACDP-0008):
- HTTPS-only for all outbound requests; HTTP is rejected.
- IP-literal rejection in
SsrfPolicy(forces DNS resolution). - Private-range blocking: RFC 1918, loopback, link-local,
multicast, IMDS (
169.254.169.254), IPv6 equivalents. - Response-size caps: 1 MB for context retrievals, 64 KB for capabilities and DID documents.
- Redirect cap: max 3 follows, same-authority only.
- Algorithm-downgrade rejection: signatures are checked against the algorithm declared by the resolved DID verification method.
- Ed25519 mandatory (RFC-ACDP-0001 §5.10).
DNS-rebinding protection (§7.6) is active: SafeDnsResolver is wired into
every HTTP client's dns_resolver hook, so resolved IPs are filtered through
the SsrfPolicy at DNS time, before any TCP connect. See
docs/security.md.
Quick start
Producer — build and sign a request
use ;
let seed = ;
let key = from_bytes;
let producer = new;
let req = producer
.publish_request
.title
.context_type
.visibility
.build
.expect;
// req.content_hash and req.signature are computed automatically
println!;
acdp_version field
Since 0.2.0 the builder emits acdp_version explicitly by default
("0.2.0", the value of acdp::ACDP_VERSION) — the omission default is closed
for 0.2.0 builders. Consumers still treat an absent field as "0.1.0"
(RFC-ACDP-0001 §6). To reproduce the 0.1.x omitted form, opt out:
builder.omit_acdp_version // drops the field, matching the 0.1.x wire form
Note: absent and explicit forms produce different content_hash values
(the JCS byte sequences differ). Pick one and stay consistent within a lineage.
The sig-001 golden vector was signed without the field, so its tests use
omit_acdp_version() to stay byte-stable.
Consumer — retrieve and verify
#
# async
Server — verify and publish a context (RFC-ACDP-0003 §2.1)
#
# async
RegistryServer::publish_unverified_for_testsis provided for unit tests that cannot run a live DID resolver. It MUST NOT be used in production — it skips DID resolution and signature verification, which is a protocol violation (RFC-ACDP-0003 §2.1).
Cryptographic design
The library implements three protocol-critical operations exactly:
| Operation | Spec reference | Rust impl |
|---|---|---|
| JCS canonicalization | RFC 8785 | crates/acdp-jcs/src/lib.rs (inline, handles -0.0) |
content_hash |
RFC-ACDP-0001 §5.7 | crates/acdp-crypto/src/hash.rs |
| Ed25519 / P-256 sign/verify | RFC-ACDP-0001 §5.8/11 | crates/acdp-crypto/src/{sign,verify}.rs |
The signature input is the ASCII bytes of the full "sha256:<hex>" string —
not the raw 32-byte digest. See crates/acdp-crypto/src/sign.rs for details.
Examples
Testing
The suite includes:
- Spec golden vectors (
tests/golden_vector.rs—sig-001,can-001). - The fixture-driven conformance suite (
tests/conformance.rs, plus the TLS-backedtests/tls_conformance.rsand the receipt/lifecycle/ revocation/witness suites). PointACDP_SPEC_DIRat a spec checkout to run it (seedocs/conformance.md). - The RFC-ACDP-0001 §5.11 verification algorithm, covered per-step
(
tests/verify_algorithm.rs+ the offline module inacdp-verify). - Property tests for JCS canonicalization (
proptest) andcargo-fuzztargets underfuzz/. - HTTP-mocked tests for
RegistryClientandWebResolver(wiremock). - Unit tests in every crate.
# Conformance against a spec checkout
ACDP_SPEC_DIR=../agentcontextdistributionprotocol
Building docs
RUSTDOCFLAGS="--cfg docsrs -D warnings"
Dependencies
| Crate | Purpose |
|---|---|
ed25519-dalek |
Ed25519 signing and verification |
sha2 |
SHA-256 |
serde/serde_json |
JSON |
reqwest/rustls |
HTTPS (client feature, no OpenSSL) |
zeroize |
zeroes signing-key bytes on drop |
Contributing
See CONTRIBUTING.md for the dev workflow and quality bars. Security issues should follow SECURITY.md.
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.