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).