acdp 0.8.0

Rust client library for the Agent Context Distribution Protocol (ACDP v0.1.0 + v0.2.0 Trust & Hardening)
Documentation

acdp-rs

CI Crates.io docs.rs License MSRV

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-primitivesacdp-jcs/acdp-safe-httpacdp-didacdp-cryptoacdp-typesacdp-validationacdp-verifyacdp-produceracdp-client/acdp-serveracdp-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/:

These docs are additive to the specification and cite the relevant RFC sections rather than restating them.

Install

cargo add acdp                          # client (default)
cargo add acdp --no-default-features    # types/crypto only, no HTTP
cargo add acdp --features server        # add the publish validator

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_id derived 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:web producers resolve their keys over HTTPS; 0.2.0 adds offline did: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 acdp::{
    crypto::SigningKey,
    producer::Producer,
    types::{AgentDid, ContextType, Visibility},
};

let seed = [/* your 32-byte key seed */ 0u8; 32];
let key  = SigningKey::from_bytes(&seed);

let producer = Producer::new(
    key,
    AgentDid::new("did:web:agents.example.com:my-agent"),
    "did:web:agents.example.com:my-agent#key-1",
);

let req = producer
    .publish_request()
    .title("Q1 2026 revenue snapshot")
    .context_type(ContextType::DataSnapshot)
    .visibility(Visibility::Public)
    .build()
    .expect("build failed");

// req.content_hash and req.signature are computed automatically
println!("content_hash: {}", req.content_hash);

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

# #[cfg(feature = "client")]
# async fn run() -> Result<(), acdp::AcdpError> {
use acdp::{
    client::{RegistryClient, VerifiedContext},
    did::WebResolver,
    types::CtxId,
};

let client   = RegistryClient::new("https://registry.example.com")?;
let resolver = WebResolver::new();
let ctx_id   = CtxId("acdp://registry.example.com/…".into());

// Fetches, recomputes hash, resolves DID, verifies signature
let ctx = VerifiedContext::fetch(&client, &resolver, &ctx_id).await?;
println!("title: {}", ctx.body().title);
println!("status: {:?}", ctx.registry_state().status);
# Ok(()) }

Server — verify and publish a context (RFC-ACDP-0003 §2.1)

# #[cfg(feature = "server")]
# async fn run(
#     server: &acdp::registry::RegistryServer<acdp::registry::InMemoryStore>,
#     resolver: &acdp::did::WebResolver,
#     req: &acdp::PublishRequest,
# ) -> Result<acdp::types::PublishResponse, acdp::AcdpError> {
// `publish_verified` runs the full §2.1 pipeline: schema validation →
// hash recomputation → DID resolution → signature verification → and
// only then assigns a `ctx_id` and persists. This is the ONLY
// conformant server path — never persist before signature verification.
let response = server.publish_verified(req, None, resolver).await?;
# Ok(response) }

RegistryServer::publish_unverified_for_tests is 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

cargo run --example producer                       # build a signed request
cargo run --example consumer --features client     # verify the golden vector
cargo run --example supersession                   # build a v2 that supersedes v1
cargo run --example end_to_end --features client,test-transport  # publish→retrieve→verify

Testing

cargo test --all-features                          # full suite
cargo test --no-default-features                   # core (no HTTP)

The suite includes:

  • Spec golden vectors (tests/golden_vector.rssig-001, can-001).
  • The fixture-driven conformance suite (tests/conformance.rs, plus the TLS-backed tests/tls_conformance.rs and the receipt/lifecycle/ revocation/witness suites). Point ACDP_SPEC_DIR at a spec checkout to run it (see docs/conformance.md).
  • The RFC-ACDP-0001 §5.11 verification algorithm, covered per-step (tests/verify_algorithm.rs + the offline module in acdp-verify).
  • Property tests for JCS canonicalization (proptest) and cargo-fuzz targets under fuzz/.
  • HTTP-mocked tests for RegistryClient and WebResolver (wiremock).
  • Unit tests in every crate.
# Conformance against a spec checkout
ACDP_SPEC_DIR=../agentcontextdistributionprotocol cargo test --test conformance

Building docs

RUSTDOCFLAGS="--cfg docsrs -D warnings" cargo +nightly doc --all-features --no-deps --open

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.