atproto pds in zig pds.zat.dev
pds atproto
zds docs simplespace-upgrade.md
3.7 kB
Markdown
at main

Simplespace independent read/write access #

The policy split follows the September 9, 2026 lexicons (upstream change). ZDS v0.4.0 also adopts the October 1 credential, notification, and spaceType changes; see the current Spaces contract. These experimental updates intentionally remove the previous request shapes.

Client changes #

  • Replace addMember({space, did}) with putMember({space, did, read, write}). Both flags are required booleans. This is an upsert: subsequent calls replace both permissions, including false values.
  • Replace policy in createSpace with readPolicy and writePolicy. Both are required and accept the same lexicon policy unions as before.
  • updateSpace accepts either policy independently; omitted fields stay unchanged. A supplied old policy field returns InvalidRequest, including when mixed with the new fields.
  • Read both policies from getSpace; the old policy output is removed.
  • Read read and write from each listMembers entry. The page default is 100, maximum 1000. The owner needs a covering read grant to enumerate members.
  • Managing apps receive checkUserAccess with access=read or access=write. Write checks omit clientId. Each policy can name a different managing app.

For example, a public-readable space with selected writers:

{
  "spaceType": "example.shared.notes",
  "readPolicy": {"$type": "com.atproto.simplespace.defs#publicPolicy"},
  "writePolicy": {"$type": "com.atproto.simplespace.defs#memberListPolicy"},
  "appAccess": {"$type": "com.atproto.simplespace.defs#open"}
}

Read policy governs credential issuance. Write policy governs which writers the space authority tracks and forwards notifications for. A resident can store records in their own repo even when the space denies write access; those writes do not register the writer or get forwarded by the authority. This also applies when both roles are hosted by ZDS.

Stored data #

Startup migrates the previous SQLite schema in one transaction:

  • The previous policy and managing app become the read policy and managing app.
  • The write policy and managing app start with the same values.
  • Every existing member starts with read and write enabled.
  • Records, memberships, app-access configuration, and deleted-space tombstones are retained. Future restarts do not reapply the access defaults.

This preserves previous access while removing compatibility code from the API. Old clients must update; no legacy route or single-policy translation remains. The policy-schema migration alone does not change credential expiry. The v0.4.0 credential cutover does: clients must reacquire credentials and replace Spaces DPoP with HTTP message signatures. OAuth DPoP is unchanged.

Before deploying, take a consistent database backup and volume snapshot, rehearse startup on a disposable copy, and compare the account, policy, membership, and record state. A binary rollback cannot safely represent independently changed permissions using the old schema.

Validation #

just test covers migration preservation and repeat execution, member upserts, and independent policy decisions. just smoke-permissioned exercises read-only, write-only, neither, and both permissions through credential issuance and actual record writes, plus old payload rejection and partial policy updates.

Use just bench space 1000 and just bench space 10000 for the storage baseline. These are single-caller storage measurements; they do not measure remote managing-app latency. Use just bench http separately for the public HTTP paths.