atproto pds in zig pds.zat.dev
pds atproto
zds docs space-host-migration.md
6.5 kB

space-host migration notes #

Status: exploratory. These notes describe the current proposal and local experiments; they do not define a ZDS extension or promise a migration API.

Separate the two moves #

Permissioned data has two independently hosted kinds of state:

  • A permissioned repo is one author's records in one space. It lives on that author's repo host, usually their PDS.
  • A space host serves the authority's configuration, mints space credentials, maintains the writer set, and routes write notifications.

Proposal 0016 explicitly says moving a permissioned repo works like moving a public repo, except an account can have many permissioned repos and their blob sets must be enumerated. It does not define a space-host export, import, or takeover procedure.

What is portable today #

The stable space coordinate is rooted in the authority DID. That DID may publish a dedicated #atproto_space_host endpoint and #atproto_space signing key, with fallbacks to #atproto_pds and #atproto.

Changing the host endpoint does not change the space URI. A space credential also contains the authority DID, space URI, holder P-256 DID key, and optional client ID, but not the host URL. Existing credentials therefore remain valid across an endpoint move while the authority key remains valid. Changing key custody is a separate identity operation.

The permissioned repo artifact is also portable. com.atproto.space.getRepo returns a two-root CAR containing the signed deniable commit, DAG-CBOR record index, and record blocks. Blobs are enumerated and fetched separately. The oplog is explicitly disposable and starts fresh on a new repo host.

CAR verification checks the signed (space, author, rev, ikm) context and consistency of the MAC, index, and record blocks. It does not establish record authorship: a holder of the commit can substitute records and recompute its hash and MAC without the author's signing key. Migration must establish content provenance separately, for example by fetching from the author's resolved repo host over authenticated transport. An arbitrary forwarded CAR is not sufficient evidence of what the author wrote.

Local two-way conformance evidence:

  • ZDS verifies the committed pds.js fixture generated by the official @atproto/space implementation at proposal PR 5187 commit 14e3ed0ec5219c58e33eb66a3efe049a3bf2b78f.
  • The official verifier on the current proposal branch accepted a CAR emitted by a fresh local ZDS smoke-permissioned instance.
  • The checks cover the commit signature and MAC, LtHash, canonical index, record CIDs and values, and binding to the space and author.

The local permissioned benchmark verifies a 1,001-record export in about 2.29 ms and a 10,001-record export in about 19.64 ms on the development machine. The larger run also caught and removed an internal mismatch where the generic commit-CAR reader's 10,000-block default rejected a valid full-state export containing 10,000 records plus its commit and index roots. Verification now remains bounded by the supplied CAR's byte length rather than that smaller commit-transport ceiling.

A separate two-host ZDS experiment before the October 1 credential change copied only a throwaway authority account and its signing key from host A to a fresh host B. An A-issued credential received 404 SpaceNotFound from B before the space existed. After the same simple-space coordinate and configuration were recreated on B, that unchanged credential received 200 from B. B still had no writer-registry entries or records. This demonstrates both halves of the boundary: credential continuity comes from the authority identity and key, while control state does not follow the DID endpoint automatically.

What a space host owns #

In ZDS, the authority-side state is currently spread across these concepts:

  • space type and key
  • independent read/write policies, managing apps, and app-access configuration
  • simple-space member read/write grants
  • writer registry with each repo's latest revision, hash, and space revision
  • the authority's latest sequence checkpoint
  • notification registrations
  • authority signing-key custody through the resident account

Writer record blocks and blobs are not space-host state and must not be copied as part of a host-only move. Repo hosts separately own the persistent revocation cache and pending writer-notification outbox; those follow repo-host state, not just the authority configuration.

The proposal intentionally permits custom host policy. Consequently, a generic destination cannot promise to reproduce an arbitrary source host's policy. Simple-space configuration is structurally transferable; application-specific policy is transferable only when the destination implements a compatible policy model or the application can reconstruct it.

Current ZDS gap #

ZDS can resolve a dedicated #atproto_space_host endpoint and verify a dedicated #atproto_space key, but it does not yet host a space under a standalone authority key. Space creation and credential minting are tied to a resident account and its signing key. ZDS also has no host-state export/import operation.

This means a normal resident account migration can move the fallback PDS and space-host roles together in principle, but independent space-host migration is not yet a complete ZDS workflow. Copying SQLite rows would demonstrate storage mechanics, not protocol interoperability.

Next local experiment #

Use throwaway identities and no resident production data:

  1. Create a simple space on host A and add two writers on distinct repo hosts.
  2. Sync each writer through listRepos, getRepo, listBlobs, and getBlob.
  3. Repoint the authority DID's #atproto_space_host service to host B while preserving the authority key.
  4. Reconstruct the simple-space config and members on B through an explicit, inspectable transfer artifact.
  5. Rebuild or import the writer registry, renew notification registrations, and verify that old credentials still read while new credentials mint at B.
  6. Confirm that no writer records or blobs were copied to the space host and that a dropped notification is repaired by comparing listRepos revisions.

Before implementing that experiment over HTTP, ZDS needs a clean authority-key boundary and a deliberately scoped host-state artifact. Neither should be smuggled into the public XRPC surface before cross-implementation discussion.