atproto pds in zig pds.zat.dev
pds atproto
zds docs attested-payments-and-write-delegation.md
8.4 kB

attested payments and the broker write-delegation gap #

Notes from a 2026-08-07 survey of the permissioned-data ("spaces") interop landscape, the attested.network payment proposal, and what a payment broker would need from ZDS that no PDS implementation currently provides. This is a design note, not a plan of record. Nothing here is implemented and nothing should be implemented unilaterally — see "recommendation" at the end.

context #

Interop testing with Bulleted (Nick Gerakines' atpint stack) surfaced two ZDS OAuth bugs (space scopes rejected at PAR; read_self unable to authorize null-collection repo-state reads), both fixed. The larger question that testing raised: is the spaces surface a sound foundation for future financial transaction records and an expanded rights/copyright model? Answering it required surveying every known implementation.

the implementation landscape #

PDS-hosted spaces (proposal 0016 shape — multi-writer space as a set of single-writer per-member repos, LtHash set commitment, deniable commits):

  • ZDS (this repo)
  • atpint (Nick Gerakines; sandbox network, closed source; also the origin of com.atproto.space.getRepoState and community.lexicon.service.describe, neither of which any other implementation has)
  • pds.js (Chad Miller, https://tangled.org/chadtmiller.com/pds.js — packages/spaces/; scope grammar and policy model closely match ZDS)
  • garazyk (Jack Valinsky, https://github.com/jvalinsky/garazyk — spec-native, good interop docs, monotonic writer-head rollback protection)
  • rsky (blacksky; PDS endpoints plus a standalone rsky-space-host crate)

Standalone non-PDS space hosts (the "arbiter" pattern — the term itself is Zicklag's, from the Roomy/Muni Town "Arbiter" group-management design):

  • Stratos (Evelyn Osman, https://github.com/NorthskySocial/stratos — keeps private records off the PDS entirely; clients reuse the PDS OAuth DPoP session cross-origin; the only implementation with non-deniable ECDSA commits, but no commit chain (prev: null) and the weakest history retention)
  • HappyView (Trezy, https://github.com/gamesgamesgamesgamesgames/happyview — AppView that is itself the space host; commit "signature" is pure HMAC; best-in-class credential revocation: member removal actively revokes outstanding credentials, checked per request)

Properties that matter for financial-grade records, across all of the above:

  • Deniable commits are the norm and the intent. Signatures cover (space, author, rev, ikm) context, never content. A space commit is useless as third-party evidence of what was written. This is a privacy feature of proposal 0016, not a bug.
  • No implementation has a two-party acknowledgment or counter-signature pattern. None.
  • The host holds the signing key everywhere, so a host can forge or silently drop records absent an external observer.
  • The oplog is contractually droppable (ZDS docs say so explicitly; Stratos returns OplogTruncated; garazyk prunes to last-N). It is a sync shortcut, not an audit log.
  • Credential revocation leaks ~2h everywhere except HappyView.

Conclusion from the survey: spaces provide confidentiality, not evidence. Financial truth cannot rest on space commits in any current implementation.

attested.network resolves the evidence problem at a different layer #

attested.network (https://attested.network, draft spec, discussion at the atprotocol community forum) is a proof-of-payment spec built on badge.blue attestations. Three-party model:

  • payment record (network.attested.payment.oneTime|recurring|scheduled) in the payer's repo, carrying a signatures array of strongRefs
  • network.attested.payment.proof records in the recipient's and broker's repos, attesting via CID
  • entitlements linked by strongRef to any lexicon; trust models (strict / creator-trusted / federated) are app policy

badge.blue (https://badge.blue) mechanics: strip signatures, inject $sig metadata containing the repository DID, DAG-CBOR canonicalize, SHA-256, CIDv1; ECDSA (P-256/K-256, low-S normalized) over the CID bytes. Repository binding makes cross-repo replay fail verification structurally. Remote proofs are unsigned — their authority is residence in the attestor's authenticated repo. Key discovery is deliberately open (any DID verification method, not pinned to #atproto). Reference implementation: atproto-attestation in https://tangled.org/ngerakines.me/atproto-crates (v0.15.0-rc.2 at time of writing; known gaps: no P-384; remote-proof verification does not check the strongRef's own cid field against the fetched proof).

Consequence: integrity and non-repudiation ride on the attestation layer, not on space commits. Private payments per the spec use identical attestation mechanics with the payment record living in a permissioned space. Spaces do the one thing they are good at (confidentiality); badge.blue does the evidence. The deniable-commit objection dissolves.

the actual gap: broker write delegation #

The spec requires the broker to:

  1. write payment records into the payer's repository ("via inter-service auth" — undesigned in the spec)
  2. for private payments, create and manage a permissioned space in the payer's repository and grant read access to recipient and broker
  3. for recurring/scheduled payments, keep writing proofs and records monthly, indefinitely

What exists today, nowhere sufficient:

  • ZDS: com.atproto.server.getServiceAuth mints user-signed lxm-scoped JWTs capped at 1 hour (60s method-less). The only write endpoint accepting inbound service auth is uploadBlob. All space and simplespace writes are bearer/DPoP-only with repo == authenticated user.
  • Nick's workspace (atproto-crates): space service auth is 60s notifyWrite-style with jti replay tracking; the 0016 delegation-token → space-credential flow is read delegation; the word "broker" appears nowhere.
  • All five surveyed implementations enforce self-repo-only space writes.
  • Tranquil (Lewis, https://tangled.org/tranquil.farm/tranquil-pds) has the closest prior art, for accounts rather than spaces: durable "controller" delegation grants with scope intersection, an audit log, TOTP-gated consent, and revocation (crates/tranquil-pds/src/delegation/).

three candidate models #

  1. Ephemeral user-minted service tokens (extend the existing primitive). Payer's client mints an lxm+collection-scoped service JWT at payment-initiate time; the PDS accepts service auth on createRecord and space writes, narrowly scoped. Cheap, no new consent surface, single-use jti for replay safety. Covers one-time payments only: recurring dies at the 1h cap and minting requires the payer online.
  2. Durable PDS-side delegation grants (Tranquil-controller-shaped). A stored, revocable grant: grantee DID, method set, collection set, optional space, expiry, audit log. The broker authenticates as itself; the PDS checks the grant. The only model that honestly covers recurring payments and broker-managed spaces. Cost: genuinely new protocol surface needing a lexicon and cross-implementation agreement.
  3. Write-capable space credentials (stay inside the 0016 idiom). Extend the credential exchange so credentials carry collection-scoped write actions. Elegant for the private-payment case but inherits model 1's liveness problem (delegation tokens are 60s and user-minted) unless refresh semantics are added — at which point it converges on model 2.

recommendation #

Model 2 is where this converges, and it must not be invented unilaterally: it is a consent/trust primitive that only matters if brokers and multiple PDS implementations agree. The attested.network spec hand-waves exactly this step, meaning the gap is undesigned upstream. Next action when this is picked up: write the gap analysis (the three models, the Tranquil precedent, the recurring-payment forcing function) into the attested.network forum thread and to Nick directly, before any ZDS code.

Related reading order for whoever picks this up: this file, then permissioned-data.md, then permissioned-data-proposal-94.md.