Releases: agentcontextdistributionprotocol/acdp-rs
v0.2.1
v0.2.0
Added
- (registry) add publish_verified_did_key_in_tenant
- [breaking] ACDP 0.2.0 trust & hardening — registry receipts, did:key, divergence diagnostics
Fixed
- (registry) scope the §7 no-degraded-mode check to newly inserted contexts
Other
- add unit coverage for core types and crypto error paths
- add supersession + end-to-end examples and negative-input tests
- add library usage guides under docs/
Implements the four workstreams of
plans/acdp-0.2-trust-hardening-2026-06-12.md, verified against the
spec's published 0.2.0 Draft (RFC-ACDP-0010 + the RFC-ACDP-0001
amendments) and its conformance pack: sig-003, fp-001, rcpt-001
(arithmetic golden vectors, reproduced byte-for-byte), can-012
(divergence corpus), dk-001..004, and rcpt-002..004 are all
executed by tests/conformance.rs; rot-001 and fed-009 behaviors
are covered by tests/receipts.rs. ACDP_VERSION is now 0.2.0 and
the builder default emits it explicitly (RFC-ACDP-0001 §6: the
omission default is closed for 0.2.0 builders). The receipt object is
a CLOSED schema per RFC-ACDP-0010 §4; receipt verification hashes the
raw wire JSON (never a re-serialized struct) and binds lineage_id /
origin_registry / created_at to the accompanying body per §8
step 3. acdp-registry-receipts requires capabilities
acdp_version >= 0.2.0, and publish_unverified_for_tests is
unavailable on receipts-advertising registries (§7: no degraded
mode).
Added — did:key (WS-C)
did::key: pure offlinedid:keyresolution (Ed25519 + P-256
compressed, multicodec-checked), encoding helpers, and the
did:key:z<mb>#z<mb>key-URL convention. No network, no SSRF
surface; verification outlives the producer's infrastructure.Producer::new_did_key/Producer::new_did_key_p256— identity
derived from the key, no domain or DID hosting required.crypto::verify_body_offline/
verify_publish_request_signature_offline/
verify_did_key_envelope— full did:key verification with
--no-default-features.RegistryServer::publish_verified_did_key— RFC-conformant publish
without theclientfeature; gated onsupported_did_methods
advertising"did:key"(rejected withkey_resolution_failed
otherwise).
Added — registry receipts (WS-A, RFC-ACDP-0010 draft)
types::receipt::{RegistryReceipt, ReceiptSigner}— registry-signed
attestation bindingctx_id/lineage_id/origin_registry/
created_at/key_fingerprintto the producercontent_hash.
Preimage construction is identical to the producer signature
(JCS minussignature, sign the ASCII"sha256:<hex>"string).crypto::fingerprint— pinnedkey_fingerprintencoding (SHA-256
over raw Ed25519 / SEC1-compressed P-256 public key bytes).RegistryServer::with_receipt_signer— mints receipts atomically
with persistence (via the newPublishCommit::receipt_minterhook),
returns them inPublishResponse::registry_receipt, and advertises
theacdp-registry-receiptsprofile.- Client verification:
client::verify_receipt_value, the
ReceiptPolicy(Ignore/VerifyIfPresentdefault /Require)
policy axis, andVerifiedContext::{verified_receipt, key_status}. - New wire error code
invalid_receipt(AcdpError::InvalidReceipt,
permanent).
Added — historical key validity (WS-B)
HistoricalKeyPolicy::AcceptWithReceipt(default): a producer key
rotated out ofassertionMethodbut retained in
verificationMethodverifies as
KeyAuthorization::HistoricallyAuthorizedonly when a verified
receipt attests its fingerprint; fails closed otherwise.
Changed — sharp edges (WS-D)
- BREAKING (hash-visible):
RequestBuildernow emits
acdp_versionexplicitly by default. The omitted and explicit forms
are distinct JCS preimages; requests built with default settings
hash differently than under 0.1.x. Use
RequestBuilder::omit_acdp_version()to reproduce omitted-form
hashes (e.g. thesig-001golden vector). - BREAKING (API):
VerificationPolicy::verify_registry_receipt
(bool) replaced by thereceipts: ReceiptPolicy/
historical_keys: HistoricalKeyPolicyfields;
PublishCommitgainsreceipt_minter;PublishResponsegains
registry_receipt. crypto::{canonical_preimage, explain_hash_mismatch}— divergence
diagnostics that name the known cross-implementation hash pitfalls
(acdp_version toggle, null-vs-absent, sub-ms timestamps).- Lineage anchoring and idempotency atomicity contracts documented on
RegistryStore(theInMemoryStorealready implements both).
v0.1.0
First public release. Full conformance with the ACDP v0.1.0 Final
specification (promoted to Final on 2026-05-19): the complete spec
conformance fixture suite, the sig-001 / can-001 / lin-001 golden
vectors, and the acdp-consumer profile.
Added — repository hygiene
- Project metadata: repository, homepage, documentation, README, exclude rules,
[package.metadata.docs.rs]for all-features doc builds. - GitHub Actions CI: rustfmt, clippy (default + no-default-features),
cross-platform tests (Linux/macOS/Windows + beta), MSRV check (1.86),
doc build with-D warnings, cargo-deny, cargo-audit, llvm-cov coverage. release-plzworkflow for automated crates.io publishing.- Dependabot configuration for cargo and github-actions.
rustfmt.toml,deny.toml, and.gitignore.- HTTP-mocked tests for
RegistryClientandWebResolver(wiremock). - Property-based tests for JCS canonicalization (proptest).
CONTRIBUTING.md,SECURITY.md.
Changed — wire-shape conformance (Phase 0)
- BREAKING:
PublishResponseno longer carriescontent_hash; gains
version: u32andstatus: Statusper
acdp-publish-response.schema.json(fixture pub-007). - BREAKING:
SearchResponse.resultsrenamed tomatchesper
acdp-search-response.schema.json(fixture vis-003); back-compat
accessorresults()provided. - BREAKING:
SearchResult(thematch_summaryprojection) gains
summary: Option<String>, typescontext_typeasContextType, drops
tagsanddescription. - BREAKING:
DataRefrewritten —ref_type: DataRefTypeis required
(closed enum);description,size_bytes,schema_versionadded;
location: Option<Location>(URI or structured locator); typed
constructors (uri,uri_verified,structured,
embedded_{json,utf8,base64}). - BREAKING:
DataPeriod.startand.endare now required (was
Option). - BREAKING:
Statusis now an open enum withOther(String)for
forward-compat (e.g.retractedper RFC-ACDP-0009 §2.1); helper methods
is_active,is_superseded,is_expired,as_other. summary: Option<String>added toBody,PublishRequest, and
RequestBuilder; included in the ProducerContent hash preimage.RequestBuilder::versionsetter required for v2+ supersession;
Producer::supersede_body(&Body)propagatesversion + 1and
expected_lineage_id. v1+supersedes and v2+ without version both
rejected.assign_identifiersnow takesfirst_version_ctx_id; derives
lineage_idfrom the v1 ctx_id on supersession (was incorrectly
derived from the new ctx_id).RegistryClientapplies RFC-ACDP-0006 §7.4 timeouts (5s connect,
30s total).- Producer-side
expires_atanddata_periodsetters truncate to
millisecond precision per RFC-ACDP-0001 §5.3.
Added — validation (Phase 1)
- New
validationmodule providingvalidate_publish_request,
validate_body,validate_data_ref,validate_metadata,
validate_identifiers,compute_embedded_hash,verify_embedded_hash. RequestBuilder::build()runs full schema validation before emission.- Runtime checks: public-no-audience, array uniqueness/size, string
length,data_period.start ≤ end,DataRefoneOf + URI credential
rejection + structured-scheme pattern + embedded ≤ 64 KB +
utf8/base64content must be string, metadata depth ≤ 8 / JCS size
≤ 64 KB / ≤ 100 properties,did:webenforcement, signature length
fored25519/ecdsa-p256, embeddedcontent_hashsemantics
(json→ JCS,utf8→ raw bytes,base64→ decoded bytes).
Added — error taxonomy and typed IDs (Phase 2)
- 13 new
AcdpErrorvariants matching RFC-ACDP-0007 §5 wire codes:
NotFound,NotAuthorized,RateLimited,PayloadTooLarge,
EmbeddedTooLarge,SupersededTarget { reason, message },
UnsupportedAlgorithm,NotImplemented,CursorExpired,
InvalidCursor,DuplicatePublish,CrossRegistryResolutionFailed,
RegistryInternal. NewSupersessionReasonenum. RegistryClient::parse_successnow mapsWireError.codeto typed
variants viaAcdpError::from_wire_error.CtxId::parse,LineageId::parse,ContentHash::parse,
AgentDid::parse,AgentDid::parse_webperform full pattern
validation peracdp-common.schema.json.CtxId::uuid()extracts the
v4 UUID component.
Added — builder and API (Phase 3)
RequestBuilder::expected_lineage_idfor v2+ self-verification (v1
publications reject this field per RFC-ACDP-0003 §2.2).PublishRequest.lineage_id: Option<LineageId>.SearchParamsgainsdata_period_start_after,
data_period_end_before,expires_after,expires_beforefilters
(RFC-ACDP-0005 §2.1). NewSearchParamsBuilderaccepting
DateTime<Utc>.PublishValidator::for_authorityrejects cross-registry supersession
withSupersededTarget { CrossRegistrySupersessionUnsupported }.CapabilitiesDocument.extensions: Map<String, Value>(#[serde(flatten)])
preserves unknown forward-compat capability flags.ContextTypedeserializer rejects strings that are neither standard
values nor namespaced custom types matching
^[a-z][a-z0-9_]*:[a-z][a-z0-9_-]*$.
Added — protocol completeness (Phase 4)
CrossRegistryResolver(RFC-ACDP-0006 §4.1): seven-step algorithm
withwalk_derived_from, cycle detection, configurablemax_depth
(default 10), and optional authority allowlist.registry::safe_http::SsrfPolicy(RFC-ACDP-0006 §7): URL filtering
for HTTPS-only, IP-literal rejection, RFC 1918 / loopback /
link-local / multicast / IMDS (169.254.169.254) and IPv6
equivalents (::1,fc00::/7,fe80::/10, IPv4-mapped); same-
authority redirect check; constantsMAX_CONTEXT_BYTES(1 MB),
MAX_METADATA_BYTES(64 KB),MAX_REDIRECTS(3).tests/conformance.rsvalidates all 16 spec conformance fixtures
parse, plus deserialization checks for every example under
examples/**/*.json. The harness locates the spec via
ACDP_SPEC_DIR(with a sibling-path fallback) and skips gracefully
when neither is available.
Added — quality of life (Phase 5)
FullContext.registry_receipt: Option<Value>reserved for
RFC-ACDP-0009 §2.7.WebResolvercache backed bylru::LruCache(default capacity 1000),
withWebResolver::with_capacity(n).PublishValidator::validate_post_schemaalias with RFC-aligned
documentation.- Optional
tracingfeature:RegistryClient::{capabilities, publish, retrieve}andWebResolver::resolvecarry#[tracing::instrument]
spans when enabled.
Deferred
- IMP-09 — standalone
acdp-clicrate (sign / verify / publish /
retrieve / search). Out of scope for this revision. - IMP-05 — auto-populating
acdp_version = "0.0.1"in the builder
would change the content_hash and break thesig-001golden vector;
v0.0.1 producers MAY include it explicitly via
RequestBuilder::acdp_version.
Fixed
crypto::jcsnow compiles cleanly (missingio::Writeimport,
serde_json::Value::Stringshadowing, map indexing).producer::RequestBuilder::buildno longer emits JSONnullfor unset
optional fields; the canonical form now matches the wire format produced by
serde withskip_serializing_if = "Option::is_none", so thecontent_hash
for a minimal request matches the spec golden vector
(sig-001-ed25519-golden).- Or-pattern bug in
Visibility::Restrictedaudience check.
Security
- Cross-registry resolution builds its per-authority
RegistryClient
withnew_pinned: the foreign authority's DNS is resolved up-front,
every resolved IP is filtered through theSsrfPolicy, and the
connection is pinned to that address — closing a DNS-rebinding /
internal-host SSRF gap (SEC-01). HttpsDataRefFetcherbuilds its HTTP client with aSafeDnsResolver
DNS hook and a same-authority redirect cap, so a producer-controlled
DataReflocation resolving into a private range is refused at DNS
time and cross-authority redirects are rejected (SEC-02).validate_origin_registryrejects uppercase, underscores, and
malformed labels by delegating to the shared DNS-authority validator
(BUG-02).