Chris’s PDS implementation
cpds docs atrium-evaluation.md
36 kB · 567 lines
Markdown
at main

ATrium adoption evaluation #

This document records which ATrium components CPDS should compose, which responsibilities remain CPDS-owned, and the evidence required before an ATrium-backed path is considered production-ready. It is a dated evaluation of the crates published as of 2026-08-22, not a permanent endorsement of every future release.

Implementation notes were reconciled with the working tree on 2026-09-05, including public applyWrites and the shared cpds-protocol crate. Package release, dependency-count, and advisory findings below remain historical 2026-08-22 evidence; rerun the dependency acceptance gate for a current claim. For the present source layout, use the repository guide.

The objective is a smaller PDS, not merely fewer local files. CPDS should use ATrium where it implements stable AT Protocol machinery and delete equivalent custom code after parity is proven. CPDS must still own server policy, persistence, transaction boundaries, resource limits, recovery, and operational behavior. A dependency is not a substitute for validating untrusted input.

Evaluated baseline #

The evaluated packages are from the ATrium workspace:

Package Version Intended CPDS use Decision
atrium-api 0.25.8 Generated Lexicon request, response, union, and syntax types Adopt incrementally
atrium-crypto 0.1.3 AT Protocol key encoding, low-S signing, and signature verification Adopted for service keys, Spaces, and public repo commits
atrium-repo 0.1.8 Repository-v3 commits, MST operations, block-store traits, and CAR support Adopted behind CPDS storage adapters and fixture gates
atrium-identity 0.1.9 DID/handle resolver composition and bidirectional identity resolution Adopted selectively behind hardened CPDS transports
atrium-xrpc 0.12.4 Shared XRPC HTTP and error primitives used by generated types and clients Use transitively or in adapters; not as an Axum server framework
atrium-oauth 0.1.7 Profile-complete OAuth client behavior Use in the end-to-end client harness only

All evaluated packages are MIT licensed and declare Rust 1.85 as their minimum supported Rust version. CPDS declares Rust 1.88, so the current releases do not raise its MSRV. Their 2025-12 and 2026-03 releases demonstrate recent workspace maintenance, but the pre-1.0 repository, crypto, identity, and OAuth packages still require exact version pins, narrow adapters, and an explicit upgrade review.

atrium-api defaults to its agent and Bluesky namespace features. CPDS should use default-features = false unless a required generated type proves otherwise. The core com.atproto types remain available without enabling the client agent or application namespaces. This avoids making a client SDK or the full Bluesky schema surface part of the server architecture.

The dependency impact is acceptable only in stages. atrium-crypto reuses CPDS's existing RustCrypto curves and multibase stack, though ATrium's current thiserror 1 constraint coexists with CPDS's thiserror 2. Even with its own default features disabled, atrium-api 0.25.8 unavoidably enables atrium-common's Moka-based async cache graph in addition to atrium-xrpc, chrono, langtag, regex, IPLD, and trait-variant; atrium-common exposes no feature switch to remove that unused cache machinery. CPDS accepts that cost only because the generated types will replace wire code across the public repository API, not for isolated identifier validation. atrium-repo additionally needs async streams, futures, Tokio I/O compatibility, and unsigned varints. atrium-identity keeps its optional Hickory 0.24 DoH feature disabled because it centralizes queries at a configured DoH service and would add a second, older Hickory graph. CPDS instead pins Hickory Resolver 0.26.1 with system DNS and Tokio support. This adoption adds 26 locked packages, including platform-specific DNS configuration and Rand 0.10, but no second TLS stack; Hickory's Rust 1.88 floor matches CPDS. atrium-oauth adds JOSE, concurrent-map, and Rand 0.8 packages, so it belongs in the federation test harness rather than the production server; its default TLS client feature should be disabled in favor of an injected test transport. The actual resolved duplicates and enabled features must be measured in each adoption commit, not inferred from these manifests.

Package decisions #

atrium-api: canonical protocol boundary #

Adopt generated types for standard com.atproto endpoints instead of maintaining parallel Serde structs and string validators. In particular, the generated record APIs already express bounded NSIDs, DIDs/handles, record keys, CIDs, optional validation, compare-and-swap inputs, commit metadata, and Lexicon-defined error variants that the current public handlers omit.

Axum handlers remain thin CPDS adapters: deserialize an ATrium input, authorize it, convert it into a validated domain command, and convert the domain result back to the generated output. ATrium wire types must not leak into database tables or become the repository service's policy model.

Acceptance evidence:

  • JSON and query-string fixtures from the canonical Lexicons round-trip through the selected generated types;
  • existing clients continue to receive the exact standard field names, optional fields, bounds, content types, and error names;
  • default features remain disabled and the dependency tree contains no unused application namespaces or client runtime;
  • each migrated endpoint deletes its superseded local wire structs and syntax checks in the same commit when no other caller needs them.

Adoption record for 0.25.8 (reviewed 2026-08-22): the crate is pinned exactly, uses no default features, declares MIT and Rust 1.85, and comes from the active ATrium workspace. The generated getSession shape matches the canonical Lexicon at bluesky-social/atproto d6296a1cfdb40a773319cb59061300e4e8e52f26; its DID and handle parsers reject malformed or oversized identifiers, optional fields are omitted, and unknown extension fields round-trip through Object::extra_data. CPDS validates configured identifiers at startup and treats invalid persisted identifiers as storage corruption rather than emitting non-conformant output.

The generated Unknown type rejects floats at the HTTP boundary, but 0.25.8's generic JSON deserializer retains $link and $bytes objects as ordinary maps. Passing that value directly to a DAG-CBOR serializer therefore encodes a map and base64 string instead of CBOR tag 42 and a byte string. Its generic TryFromUnknown helper also contains internal unwrap calls. CPDS does not use that helper for untrusted records. A small fallible adapter recognizes only the specified single-key special objects, reuses ATrium's CidLink and typed BlobRef, enforces the blessed CIDv1/codec/SHA-256 forms and resource bounds, and passes the resulting serializable value to atrium-repo. Repository reads reuse ATrium's DataModel; startup re-encodes that native value to compare the canonical bytes, including UTF-8 byte strings that untagged Serde inference can misclassify as text. $type is still checked against the collection and rejected data becomes InvalidRequest; malformed stored records are storage corruption. Per-Lexicon schema validation remains separate: validate=true is rejected instead of falsely claiming it, while successful unvalidated writes report validationStatus: unknown.

That pure adapter now lives in cpds-protocol and is shared directly by public repositories and Space CAR exports. Neither feature imports the other feature's service implementation to encode records.

The adapter accepts every valid and rejects every invalid schema-independent fixture in the official, implementation-neutral atproto-interop-tests data-model set at commit 056e5741bb330757205d6b16db5266fffcae937b. Its byte output and record CIDs also match every canonical encoding fixture, including nested Unicode, native links, bytes, and blobs. CPDS negative tests add unsafe numeric forms, excessive nesting, and CIDs outside the blessed set. HTTP integration proves links and bytes retain the AT Protocol JSON representation, and startup verification reads their native repository blocks. The exact upstream fixtures are pinned with provenance, this adds no package, and CPDS's MSRV is unchanged.

ATrium's Tid, Did, Handle, Nsid, RecordKey, and AtIdentifier parsers match every corresponding valid and invalid syntax vector in the same official fixture commit; CPDS pins those vectors at its delegated syntax and production-identity boundaries. The sets cover maximum lengths, percent-encoded and real DID methods, punycode/onion and RFC handle corner cases, collection segment rules, the full record-key alphabet, case rules, invalid path/query/fragment forms, IP addresses, labels, methods, and lengths.

Datetime matches the official lexical and semantic vectors except that 0.25.8 accepts 0000-01-01T00:00:00+01:00, whose offset normalizes before the minimum year-zero instant. CPDS retains ATrium's parser and adds that one ordering guard. Language likewise supplies the BCP 47 grammar, but its langtag 0.3 dependency accepts case-insensitive duplicate variant and extension-singleton subtags. Its public parsed-component accessors also panic for short valid tags, so CPDS does not expose them to untrusted input; a small post-parse scan enforces only the RFC uniqueness rules. The standalone fixture commit still labels jaja and JA invalid, but the current Lexicon specification requires well-formed rather than registry-valid tags and the canonical atproto fixture now comments out those two cases. CPDS follows the current specification and canonical fixture while retaining the exact older vectors to detect the documented difference.

The upstream CID syntax set cannot be used as the same kind of semantic acceptance gate. It labels permissive multibase strings such as mBcDxtdWx0aWhhc2g+ valid at the syntax layer, while atrium-api deliberately delegates to ipld-core's semantic CID parser and rejects that value as an invalid multihash. The current AT Protocol data-model specification is stricter still for interoperable links, requiring its blessed CIDv1 forms. CPDS therefore does not replace the semantic parser with a fixture-only regular expression or weaken the record-link checks. Generic CID syntax fixture coverage remains open until the lexical-versus-semantic contract is clarified upstream. Generic URI and AT URI fixture coverage also remains open because atrium-api does not currently expose corresponding parsers. Native record-link fixtures and CPDS negative tests remain the production gate.

An OSV query of the complete lockfile found RUSTSEC-2026-0253 in lru 0.16.4, which atrium-common constrains for wasm only. CPDS does not target wasm; both the aarch64-apple-darwin and x86_64-unknown-linux-gnu graphs exclude lru, and separate OSV checks of the native graphs returned no advisories. The native graph has no packages with missing license metadata and uses permissive MIT/Apache/BSD/ISC/Zlib/Unicode/CDLA/CC0/Unlicense combinations. A future wasm target is blocked until ATrium permits patched lru >= 0.18.2. Upgrade atrium-api when a reviewed release models the current describeServer blobUploadLimit field, removes the target-only advisory, or materially reduces the mandatory atrium-common graph; re-run the full acceptance gate then.

atrium-crypto: AT Protocol signing boundary #

Adopt its P-256 and secp256k1 did:key parsing, key formatting, low-S signing, and strict verification for repository commits and AT Protocol service keys. It uses the same RustCrypto ecdsa, k256, and p256 families already used by CPDS, so it centralizes protocol-specific encoding and malleability rules rather than replacing the underlying audited primitives.

The strict verifier rejects high-S and DER-encoded signatures by default and ships the AT Protocol signature fixture set for both supported curves. The permissive verifier mode must not be used on federation boundaries. CPDS may continue using direct RustCrypto APIs for JOSE operations that atrium-crypto does not model, but should not retain duplicate multikey or repository-signature code.

Acceptance evidence:

  • CPDS runs the pinned upstream valid, high-S, and DER signature vectors in its own interoperability suite with recorded provenance;
  • generated signatures match the raw 64-byte, SHA-256, low-S AT Protocol form;
  • malformed multibase, wrong multicodec, wrong curve, short/long signature, and mutated-message tests fail closed without panics;
  • key material is not copied or logged beyond what the API requires, and the production secret-loading boundary remains CPDS-owned.

atrium-repo: repository mechanism, not persistence policy #

Adopt its repository-v3 commit encoding, MST, typed/raw record operations, block-store traits, extraction, and CAR primitives after interoperability tests prove byte-for-byte output. Implement its async block-store traits over a CPDS-owned SQLite transaction so record blocks, MST blocks, signed commit, repository head, operation rows, and outbox event succeed or roll back together.

The current crate intentionally separates signing from commit construction, which is useful for CPDS key custody. It also explicitly opens repositories without verifying the commit signature. Repository::open therefore must never be CPDS's trust decision: resolve the identity, verify the commit signature, validate version/DID/revision/previous-link invariants, and enforce resource limits before accepting imported or remote data.

The bundled CAR reader verifies SHA-256 blocks, but currently allocates lengths declared by the input, accepts unsupported block hash algorithms without verification, and treats an unexpected EOF while reading the next frame as a normal end of archive. CPDS must place byte/block/depth limits around CAR input and either harden those conditions upstream or use a bounded import adapter. Several MST internal invariant failures use panic!/unwrap; fuzz and corrupt block tests must demonstrate that attacker-controlled repository data cannot reach them before the reader is exposed publicly.

Acceptance evidence:

  • official MST, DAG-CBOR, CID, commit, and CAR fixtures match byte for byte;
  • randomized operation sequences match the reference implementation's roots and survive create/open/export/import cycles;
  • corrupt blocks, cycles, invalid key ordering, invalid hashes, truncated CARs, oversized lengths, excessive blocks, unsupported hashes, and bad signatures return bounded errors rather than panicking or exhausting resources;
  • repository mutations use a real SQLite transaction through the ATrium block-store adapter and pass rollback, compare-and-swap, concurrency, restart, and clock-rollback tests;
  • any local MST, commit, block, or CAR implementation superseded by the adapter is removed rather than retained as a second production path.

Adoption record for 0.1.8 (reviewed 2026-08-22): the crate is pinned exactly, declares MIT and Rust 1.85, and was published from ATrium commit 1762e5bcf00fb2a038fa4a94777acdd9cb6d4b70 on 2026-03-25. Its MST output matches the empty, eleven-record, twelve-record, and post-delete root CIDs in the official TypeScript reference suite at the pinned AT Protocol commit. It also matches all six official firehose commit-proof fixtures: pre/post roots for single and multi-operation merge/split cases, plus the exact proof set formed by newly written blocks that remain reachable from the final root. This proves the public Tree API is a conformant basis for atomic applyWrites even though Repository does not expose a batch builder; CPDS wraps the final root in exactly one signed commit and transaction. Its block-store traits are a useful composition boundary, but DiffBlockStore records writes after forwarding them and is not an atomic staging layer. CPDS therefore stages content-addressed writes in memory, filters a legacy backfill to ATrium's reachable export, and inserts each incremental write batch, record index, head, operation, and outbox event in one SQLite transaction. Commit-event proof membership is the intersection of staged writes and the final root's reachable blocks, excluding transient blocks from earlier operations in a batch.

The source review also found boundaries that CPDS must enforce. Repository and MST open operations explicitly trust stored structure and do not verify commit signatures, version, DID, revision, key ordering, depth, or resource bounds. Some malformed graphs can reach panic!, assert!, or unwrap invariants; record reads assert the DAG-CBOR codec. The CAR reader trusts declared lengths for allocation, treats an unexpected EOF at the next frame as clean end of input, and does not verify unsupported hash algorithms. Until a bounded validator proves those conditions safe, CPDS uses atrium-repo only to construct locally authorized repositories. Its SQLite adapter verifies block hashes before any ATrium read, and raw repository/CAR readers remain unexposed to remote input.

Full public export now composes Repository::export_into with ATrium's CAR v1 writer over CPDS's verified local block store. CPDS verifies the signed head before traversal and wraps the writer in a 64 MiB hard bound; the router applies the expensive-endpoint peer limit. The interoperability test reopens only this locally generated response with ATrium, checks the declared commit root and signature, and reads a record through the exported MST. This does not relax the remote-input quarantine above. ATrium's export currently deduplicates through a hash set, so block order is arbitrary; the current repository specification permits arbitrary order while describing commit-first pre-order traversal as a future streaming preference. Incremental since export uses CPDS's durable per-commit block membership to select revisions newer than the requested TID while keeping the current signed head as the CAR root, matching the reference reader's layered-CAR contract. It fails closed across legacy rows whose exact membership was not recorded. An interoperability test layers the incremental archive over a prior full export and reads both old and new records through ATrium.

Record-level sync similarly composes Repository::get_raw for existence and Repository::extract_raw_into for the signed MST proof. Its endpoint test confirms the proof resolves the requested record at the declared current root and omits an unrelated record block, keeping CPDS out of proof construction and preventing the public response from disclosing unrelated values.

Repository::add_raw and update_raw correctly preserve serializable native CID and byte values, but plain serde_json::Value cannot express them to a non-human-readable serializer. CPDS therefore passes only the small schema-independent adapter described under atrium-api; ATrium remains the sole repository/MST writer and CPDS does not maintain a parallel block encoder.

Block sync uses ATrium's reachable repository export as the authorization set, then reads only requested members through the CID-verifying SQLite adapter and writes them with CarStore. This is necessary because CPDS's content-addressed block table is shared across repositories, unlike the per-account store used by the reference PDS. Requests are bounded to 1,000 CIDs and the CAR has an empty roots array, matching the current reference implementation. Repeated XRPC query keys are parsed with pinned serde_html_form 0.2.8, already present and used by atrium-xrpc, so the direct dependency adds no package or parallel query stack.

Public and permissioned blob listing now share one recursive scanner built on ATrium's BlobRef parser. It accepts both the current typed representation and ATrium's read-only legacy representation, canonicalizes valid CIDs, ignores malformed lookalikes, and replaces the previous hand-written Spaces-only blob shape checks. Public listing derives only from current repository records (or paths changed after since), so it does not disclose unattached uploads. Record create and put use the same parser as a stricter write boundary: only the current typed representation is accepted, and its CID, MIME type, and size must match metadata already uploaded by that repository owner.

Repository streaming reuses atrium-api's generated subscribeRepos message, identity, account, commit, sync, operation, DID, handle, TID, datetime, CID-link, and byte-string models. ATrium does not currently provide a Rust server-side event-stream encoder, so the CPDS HTTP adapter owns only the specified transport boundary: one binary WebSocket frame containing a small {op, t} DAG-CBOR header followed by the generated payload without $type. This matches the official event-stream encoder at AT Protocol source commit d6296a1cfdb40a773319cb59061300e4e8e52f26. Axum's existing ws feature adds tokio-tungstenite 0.29, tungstenite 0.29, and SHA-1 only for the standard WebSocket upgrade; their Rust 1.63/1.71 floors are below CPDS's Rust 1.88 MSRV and their MIT/Apache licensing stays within policy. CPDS retains stream admission, backpressure, cursor, persistence, and shutdown policy because those are server responsibilities rather than AT Protocol data-model machinery. The first repository transaction persists identity, active-account, and commit events in the order recommended by the sync specification on one sequence. CPDS now includes the canonical stored account handle: local mode derives it from the validated local document, while production verifies both public directions before accepting the first write. Existing retained events remain immutable. Later user lifecycle changes reuse generated activateAccount, deactivateAccount, getSession, getRepoStatus, listRepos, and subscribeRepos shapes. ATrium supplies the evolving Lexicon wire models, while CPDS retains account policy, atomic status/outbox persistence, visibility decisions, and concurrency gates because those are PDS server responsibilities. The end-to-end test decodes status/session/list responses and retained #account events through ATrium before and after reactivation. The durable window is configurable and pruned atomically with commits. A stale cursor receives ATrium's generated #info model with the Lexicon-defined OutdatedCursor name, while incremental CAR export rejects history older than the same boundary. listRepos and listReposByCollection likewise use generated ATrium parameters, repository models, DID/CID/TID fields, and the reference PDS time/DID cursor. The collection variant applies the generated canonical NSID to CPDS's current record index rather than reimplementing repository discovery models. describeRepo and listMissingBlobs use generated ATrium output models; the latter and listBlobs query schema-v6 reference indexes populated by the same ATrium typed/legacy blob scanner. Public and Space mutations update their index in the record transaction, migrations backfill them, and startup recomputes them to fail closed on drift. A real WebSocket relay consumer opens commit CARs through ATrium, verifies the signed root, and resolves the announced MST record before accepting an event. A recovery variant forces the durable cursor outside retention, consumes the generated OutdatedCursor information event, opens a full getRepo CAR through ATrium, verifies its signed head and every current record, then resumes live and validates the next inductive commit.

The resolved graph reuses CPDS's existing ATrium API, CID, IPLD, DAG-CBOR, Serde, SHA-256, Tokio, and Tokio-util packages. Its incremental packages are atrium-repo, async-stream, the futures facade/I/O/channel features, and unsigned-varint's futures support; it introduces no second CID, CBOR, crypto, or async runtime. All resolved packages have license metadata and remain in the documented permissive license set. A complete-lockfile OSV check still finds only the documented wasm-only lru 0.16.4 advisory, while both supported native target graphs (aarch64-apple-darwin and x86_64-unknown-linux-gnu) are clean.

atrium-identity: resolution with CPDS network policy #

CPDS now uses ATrium's common resolver for did:plc, well-known resolver for HTTPS handles, generated DID/handle/document types, and XRPC HTTP trait. It keeps direct DNS TXT selection because ATrium's DNS resolver returns the first matching record rather than rejecting conflicting valid DIDs as the handle specification requires. The canonical resolveHandle Lexicon intentionally performs only the forward lookup, while CPDS startup separately applies the bidirectional trust check. The shared service also replaced the parallel remote DID fetches used by Space signing-key and service resolution.

CPDS must provide the outbound transports and cache. Those adapters own DNS and HTTP timeouts, maximum response sizes, redirect behavior, TLS, allowed schemes and ports, IP-range/SSRF rejection, PLC endpoint configuration, positive and negative cache TTLs, concurrency bounds, and metrics. Resolver success is not enough: CPDS verifies document identity and applies caller-specific trust rules, including signing-key type/controller, service identifier/URL, and bidirectional handle-DID correspondence.

The local document does not require a network resolver. CPDS constructs and validates both local and fetched DID documents with atrium-api::did_doc, uses the generated type's PDS/signing-key helpers at startup, and requires a production did:web authority to equal the public origin. This removed the parallel DID document wire structs and remote Space fetch paths while retaining CPDS's fetch policy. The production transport uses credential-free public HTTPS, deployment system DNS with private/reserved answers denied, three redirects, five-second connects, ten-second requests, 256 KiB responses, and bounded positive/negative DNS caching. Version 0.1.9's WebDidResolver interpolates the complete did:web method identifier as a hostname, so it does not replace CPDS's small path-bearing, percent-decoded did:web URL mapping. ATrium's higher-level identity resolver also treats a document with no alsoKnownAs as acceptable; CPDS explicitly requires the exact at:// backlink for its production trust decision.

Production periodically reuses this same resolver boundary. CPDS persists only confirmed invalidity as the AT Protocol special value handle.invalid, appends the generated #identity stream model through its existing durable outbox, and restores the configured handle after a confirmed recovery. Transient resolver failures retain the last confirmed state. This policy and persistence remain CPDS-owned because ATrium provides resolution and wire models, not PDS hosting lifecycle orchestration.

Acceptance evidence:

  • pure negative tests cover DNS noise/duplicates/conflicts, unsafe identity URLs, did:web root/path/port mapping, and missing/wrong/exact backlinks;
  • an injected IdentityNetwork contract proves DNS precedence, HTTPS fallback only for absence/transport failure, terminal conflicts, document-ID matching, bidirectional trust, and invalidation/recovery behavior independently of public network state;
  • generated-route and Space integration tests cover local handle resolution and reuse of the shared DID boundary;
  • a fully local fake DNS/HTTPS/PLC service must still cover both successful methods and their precedence;
  • private, loopback, link-local, redirected, oversized, slow, malformed, and mismatched responses fail according to explicit local/production policy;
  • cached results honor bounds and expiry and cannot bypass bidirectional verification;
  • superseded remote fetch and document-parsing paths are deleted; the small conflict, backlink, and did:web conformance adapters remain until upstream provides equivalent behavior with negative coverage.

atrium-xrpc and atrium-oauth: narrower roles #

atrium-xrpc defines client-side request/response/error and HTTP traits. It is useful under atrium-api, resolver transports, and test clients, but it does not provide Axum routing, server authorization, request limits, or CPDS transaction semantics. CPDS should keep direct Axum handlers rather than build a second server abstraction.

atrium-oauth is an OAuth client library. It can drive the local discovery, PAR, DPoP, authorization, token, refresh, revocation, and authenticated-XRPC end-to-end test. It does not replace the PDS authorization server. CPDS retains server metadata, client registration policy, PAR/session storage, DPoP nonce and replay state, token rotation, consent, revocation, and security headers.

Dependency acceptance gate #

Before each new ATrium dependency or version is committed:

  1. Pin the reviewed version in Cargo.toml and Cargo.lock; disable default features and enable only features exercised by CPDS.
  2. Confirm its declared MSRV is no higher than CPDS's, its license is allowed, and its release/changelog/repository show active maintenance.
  3. Review its public API and relevant source for trust assumptions, panics, unchecked allocation, permissive compatibility modes, network behavior, and secret handling.
  4. Run cargo tree -d and cargo tree -e features; avoid a second crypto, async, HTTP, CID, CBOR, or Serde stack unless the benefit is explicit.
  5. Run the repository's dependency advisory and license policy. A missing audit tool is a failed release gate, not evidence of no advisories.
  6. Add official fixture and CPDS negative tests before routing production data through the component.
  7. Put unstable ATrium APIs behind one small adapter owned by the relevant CPDS domain, then delete superseded custom code after parity is established.
  8. Record the exact upstream version, fixture commit, enabled features, known limitations, and upgrade trigger in the commit or test provenance file.

ATrium upgrades are reviewed changes, not automated version bumps. Re-run the source review, dependency tree, advisory check, official fixtures, negative tests, and federation harness before accepting one.

Planned adoption order #

  1. Complete: pinned atrium-crypto, official signature fixtures, and removal of duplicate AT Protocol multikey and low-S signing/verification code while retaining JOSE-specific RustCrypto use.
  2. Complete for the implemented standard surface: pinned atrium-api without default features; migrated com.atproto.server.getSession, com.atproto.repo.createRecord, com.atproto.repo.deleteRecord, com.atproto.repo.getRecord, com.atproto.repo.listRecords, com.atproto.repo.uploadBlob, and com.atproto.sync.getBlob, plus account configuration syntax, to generated wire and identifier types. The migrations removed CPDS's parallel blob reference structs, corrected getBlob to require a DID rather than the previously accepted handle, enforce data-model-safe record JSON, support current-record CID selection, implement reference-compatible before/after rkey pagination, and enforce deleteRecord.swapRecord atomically in SQLite. The canonical describeServer Lexicon at d6296a1cfdb40a773319cb59061300e4e8e52f26 includes blobUploadLimit, which 0.25.8 does not yet model explicitly, so that endpoint remains local pending a compatible release or a deliberately tested extension-field adapter. CPDS's remaining DID, NSID, record-key, and TID validators now delegate directly to the generated ATrium syntax types; the superseded handwritten grammar and length logic has been removed. Local and fetched DID documents plus listRepos, listReposByCollection, describeRepo, and listMissingBlobs responses use canonical ATrium models instead of CPDS-owned wire structs. Blob discovery reuses ATrium's current and legacy reference scanner and persists its result as atomically maintained, startup-verified public and Space indexes. Schema v6 durably stages object publication and uses those indexes to transactionally claim aged orphans before bounded, crash-resumable object deletion.
  3. Complete for create/update/delete and atomic batches: a CPDS SQLite staging adapter composes atrium-repo for signed repository-v3 commits, passes the pinned reference roots, returns commit metadata, enforces commit/record swaps, and atomically publishes blocks, head, index, operation, and outbox state. Ordered public applyWrites uses one final signed commit and the same transaction. Before binding, CPDS uses ATrium's MST entry stream and repository reads to verify the signed head, every reachable record block, and exact parity with its SQL record index; routine readiness remains a bounded adapter check. ATrium 0.1.8 exposes only single-operation builders that advance the commit when finalized; it has no public batch builder. CPDS composes its public MST Tree API for the ordered operations and signs the final root once. The pinned commit-proof vectors guard ATrium's root/proof behavior; CPDS service, SQL rollback, and HTTP tests guard the surrounding batch transaction.
  4. Complete for local full and incremental export: bounded ATrium CAR export, signed-head verification, and versioned historical block selection are live. Add a hardened bounded import adapter before enabling remote repository ingestion.
  5. Complete for user account lifecycle and commit/sync transport: generated ATrium subscription models, durable inductive metadata, signed CAR slices, legacy #sync recovery, and bounded binary WebSocket delivery are live. On-disk recovery is proven by replaying a committed event and receiving the next live event after restart. Configurable retained windows and generated #info/OutdatedCursor recovery are live. The initial identity/account/commit sequence is decoded from a real WebSocket through generated ATrium types before and after restart. A relay-consumer test verifies streamed CAR roots, signatures, and MST records through ATrium. A forced slow-consumer test proves bounded disconnect and durable cursor replay without adding a parallel stream implementation. The explicit relay operation likewise reuses ATrium's generated requestCrawl input while CPDS owns origin policy, deadlines, retries, logs, and process status. Relay-style gap repair through a full ATrium-opened CAR and subsequent live commit is also proven. One combined fake relay now receives the real crawl request, validates the served DID/handle pair and signing key, repairs the expired cursor, and verifies the next live commit. Durable user activation/deactivation now reports through generated status/session/list models, gates content and writes, and emits generated #account events. The CPDS-owned offline operator path reuses that atomic transition/event machinery for host states and verifies storage around the action. Add DID-document lifecycle, secure account erasure, and crawl metrics.
  6. Extend the adopted atrium-identity boundary with fake DNS/HTTPS/PLC wire services and cache/latency metrics. Periodic invalid-handle/recovery events, the deterministic resolver port, and the orchestration-policy contract are complete; retain only the reviewed conflict, backlink, and conformant did:web adapters that upstream does not provide.
  7. Use atrium-oauth in the profile-complete local client harness while the CPDS authorization-server implementation remains explicit.

This order can yield to a smaller prerequisite, but CPDS must not introduce a second long-lived implementation of the same AT Protocol mechanism.