# 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)](https://github.com/agentcontextdistributionprotocol/agentcontextdistributionprotocol/blob/main/rfcs/RFC-ACDP-0001-core.md)
and [RFC-ACDP-0002 (Context Body)](https://github.com/agentcontextdistributionprotocol/agentcontextdistributionprotocol/blob/main/rfcs/RFC-ACDP-0002-context-body.md).
## Install
```bash
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?
| `client` | ✓ | `reqwest`, `tokio`, `rustls`, `lru` | an **agent or consumer** retrieving and verifying contexts. |
| *(none)* | — | `serde`, `sha2`, `ed25519-dalek` | building or signing contexts **offline**, or embedding in a binding (HTTP done by the host). |
| `server` | ✗ | validation + store traits | implementing a **registry**. |
| `cli` | ✗ | `client` + tokio macros | you want the `acdp` command-line tool. |
| `tracing` | ✗ | `tracing` | you 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](bindings.md) 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.
```rust
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](producing.md).
## 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.
```rust,no_run
# #[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](consuming.md).
## Run the examples
The repo ships two runnable examples:
```bash
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
| `RegistryClient::new` returns `Err` for an `http://` URL | HTTPS is mandatory (RFC-ACDP-0008). | Use `https://`. For tests against a local listener, see [Security](security.md#testing-against-localhost). |
| `RegistryClient::new` rejects an IP-literal URL | IP literals are blocked to force DNS-time SSRF filtering. | Use a hostname. |
| `build()` returns `AcdpError::SchemaViolation` | A field exceeds a length/size cap, or `data_refs` are malformed. | Check the message; see [Producing contexts](producing.md). |
| `fetch` returns `AcdpError::KeyResolution` | The 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::InvalidSignature` | The signature didn't verify against the resolved key — possibly tampering or an `acdp_version` mismatch. | See the [`acdp_version` note](producing.md#the-acdp_version-field). |
The full error taxonomy and which errors are safe to retry is in
[Errors & retries](errors.md).
## Pre-PR checks
If you're contributing to the crate, the CI-equivalent local check set is:
```bash
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](conformance.md) for what the spec-dir variable does.