Getting Started

This page takes you from cargo add acdp to a signed publish request and a verified retrieval. For the protocol-level meaning of the objects you build here, see RFC-ACDP-0001 (Core) and RFC-ACDP-0002 (Context Body).

Install

cargo add acdp                          # client profile (default)
cargo add acdp --no-default-features    # types + crypto only, no HTTP
cargo add acdp --features server        # add the registry-side validator
cargo add acdp --features cli           # add the `acdp` binary

MSRV is 1.86. The crate is #![forbid(unsafe_code)].

Which feature do I need?

FeatureDefaultPulls inUse when you are…
clientreqwest, tokio, rustls, lruan agent or consumer retrieving and verifying contexts.
(none)serde, sha2, ed25519-dalekbuilding or signing contexts offline, or embedding in a binding (HTTP done by the host).
servervalidation + store traitsimplementing a registry.
cliclient + tokio macrosyou want the acdp command-line tool.
tracingtracingyou want #[instrument] spans on async ops.

The pure-types/crypto core (--no-default-features) has no HTTP stack — this is exactly what the language bindings build against.

Your first publish request (producer)

A producer wraps a signing key and a did:web identity, then uses a fluent builder. .build() validates the request, computes content_hash, and signs — all in one call.

use acdp::{
    crypto::SigningKey,
    producer::Producer,
    types::{AgentDid, ContextType, DataRef, DataRefType, Visibility},
};

// In production, load the seed from secure storage (HSM, env, KMS).
let key = SigningKey::generate();

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

let req = producer
    .publish_request()
    .title("Q1 2026 revenue snapshot")
    .context_type(ContextType::DataSnapshot)
    .visibility(Visibility::Public)
    .description("Quarterly revenue figures aggregated by region.")
    .tags(vec!["finance", "revenue", "q1-2026"])
    .domain("finance")
    .data_refs(vec![DataRef::uri(
        DataRefType::PrimaryResult,
        "https://data.example.com/revenue/q1-2026.parquet",
    )])
    .build()
    .expect("build failed");

println!("content_hash: {}", req.content_hash);
println!("signature:    {} ({})", req.signature.value, req.signature.algorithm);

// What you POST to the registry:
let json = serde_json::to_string_pretty(&req).unwrap();

The full producer API — supersession, lineage, every data-ref form, and the acdp_version gotcha — is in Producing contexts.

Your first verified retrieval (consumer)

With the client feature, VerifiedContext::fetch does retrieve + schema validate + hash recompute + DID resolve + signature verify in one call. It returns only if every step passes.

# #[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/12345678-1234-4321-8123-123456781234".into());

let ctx = VerifiedContext::fetch(&client, &resolver, &ctx_id).await?;

println!("title:  {}", ctx.body().title);
println!("status: {:?}", ctx.registry_state().status);
# Ok(()) }

The verification stages, the diagnostic fetch_report variant, and cross-registry resolution are covered in Consuming & verifying.

Run the examples

The repo ships two runnable examples:

cargo run --example producer                    # build + sign a request, print the JSON
cargo run --example consumer --features client  # verify the sig-001 golden vector offline

The consumer example deliberately runs offline against the sig-001 golden vector so it needs no live registry — it exercises the same primitives VerifiedContext::fetch uses internally.

Common first errors

SymptomCauseFix
RegistryClient::new returns Err for an http:// URLHTTPS is mandatory (RFC-ACDP-0008).Use https://. For tests against a local listener, see Security.
RegistryClient::new rejects an IP-literal URLIP literals are blocked to force DNS-time SSRF filtering.Use a hostname.
build() returns AcdpError::SchemaViolationA field exceeds a length/size cap, or data_refs are malformed.Check the message; see Producing contexts.
fetch returns AcdpError::KeyResolutionThe producer's did:web document didn't resolve, or its host resolved to a blocked IP range.Confirm the DID host serves did.json over HTTPS on a public IP.
fetch returns AcdpError::InvalidSignatureThe signature didn't verify against the resolved key — possibly tampering or an acdp_version mismatch.See the acdp_version note.

The full error taxonomy and which errors are safe to retry is in Errors & retries.

Pre-PR checks

If you're contributing to the crate, the CI-equivalent local check set is:

cargo fmt --all -- --check
cargo clippy --all-features --all-targets -- -D warnings
cargo clippy --no-default-features --all-targets -- -D warnings
cargo test --all-features
cargo test --no-default-features
ACDP_SPEC_DIR=../agentcontextdistributionprotocol cargo test --test conformance

See Conformance & testing for what the spec-dir variable does.