···138138 credential IDs, public keys, counters, names, and last-use timestamps.
139139- `/security` is the local account-security page for passkey and app-password
140140 management.
141141-- `com.atproto.space.*` permissioned-data routes are experimental and operator
142142- gated with `ZDS_PERMISSIONED_DATA`. ZDS implements the storage and credential
143143- substrate, while reader/group semantics are left to applications.
141141+- `com.atproto.space.*` permissioned-data routes are an experimental prototype
142142+ and operator gated with `ZDS_PERMISSIONED_DATA`. ZDS implements enough storage
143143+ and credential substrate for local experiments, but the upstream proposal is
144144+ still moving and this surface is not a stable compatibility contract.
144145145146## references
146147
+300
docs/account-takedown-runbook.md
···11+# account takedown runbook
22+33+This runbook is for operator action against accounts hosted on this ZDS
44+instance. It covers test-account cleanup, emergency suspension, and long-term
55+host takedown. It is not a global identity takedown mechanism.
66+77+## authority boundary
88+99+A PDS operator controls local hosting for accounts on that PDS:
1010+1111+- whether this PDS reports a hosted account as active
1212+- whether this PDS serves the account's repo, records, and blobs
1313+- whether this PDS accepts authenticated writes for the account
1414+- whether this PDS emits account-status events on its local firehose
1515+- whether this PDS retains or purges local account data according to policy
1616+1717+A PDS operator does not, by this action alone, erase all downstream copies,
1818+control every relay or appview, or tombstone a PLC identity. Downstream services
1919+are expected to respect account-status changes, but propagation is hop-by-hop
2020+and can lag or diverge.
2121+2222+References:
2323+2424+- Account lifecycle guide:
2525+ <https://atproto.com/guides/account-lifecycle>
2626+- Account hosting status spec:
2727+ <https://atproto.com/specs/account>
2828+- Sync and `#account` events:
2929+ <https://atproto.com/specs/sync>
3030+3131+## status vocabulary
3232+3333+Atproto distinguishes several inactive hosting states:
3434+3535+- `deactivated`: user or host temporarily pauses the account. Content should not
3636+ be displayed or redistributed, but infrastructure does not need to delete
3737+ local copies.
3838+- `suspended`: host temporarily pauses the account.
3939+- `takendown`: host or service removes the repository long-term for policy or
4040+ terms reasons.
4141+- `deleted`: account deletion. Downstream services should stop serving content
4242+ and may delete data according to policy.
4343+4444+ZDS stores account hosting state in `accounts.account_status`. The currently
4545+implemented operator states are:
4646+4747+- `active`
4848+- `deactivated`
4949+- `takendown`
5050+5151+`suspended` and `deleted` are reserved vocabulary from the protocol, but ZDS
5252+does not yet expose operator workflows for them.
5353+5454+Current behavior:
5555+5656+- `com.atproto.server.deactivateAccount` sets `account_status:
5757+ "deactivated"`.
5858+- `com.atproto.server.activateAccount` returns the account to `active` only
5959+ from `deactivated`.
6060+- `com.atproto.admin.updateSubjectStatus` can apply or clear `takendown` for a
6161+ repo subject.
6262+- applying `takendown` revokes local session tokens and OAuth token families.
6363+- ZDS emits a `#account` event with `active: false` and the precise inactive
6464+ status.
6565+- `com.atproto.sync.getRepoStatus` reports `active: false` and the precise
6666+ inactive status, and omits `rev` while inactive.
6767+- public repo/blob/read XRPCs and authenticated repo writes are denied while the
6868+ account is inactive.
6969+7070+## response levels
7171+7272+### test account cleanup
7373+7474+Use this when the operator owns the test account and wants to remove clutter.
7575+7676+Default action:
7777+7878+1. Verify the account DID and handle.
7979+2. Export or snapshot enough metadata to undo mistakes.
8080+3. Deactivate first.
8181+4. Leave physical purge as a separate, reviewed step.
8282+8383+### temporary safety pause
8484+8585+Use this when an account may be compromised, abusive, or causing operational
8686+harm, but the final policy decision is not ready.
8787+8888+Default action:
8989+9090+1. Capture evidence and timestamps.
9191+2. Deactivate the account.
9292+3. Revoke active local sessions and OAuth grants if the compromise involves
9393+ credentials.
9494+4. Verify `getRepoStatus` reports inactive.
9595+5. Watch relay/appview behavior and logs.
9696+6. Decide later whether to reactivate, keep suspended, or perform long-term
9797+ takedown.
9898+9999+### long-term host takedown
100100+101101+Use this when the PDS will no longer host the account.
102102+103103+ZDS has a first-class `takendown` state for hosted repo subjects. It stops local
104104+hosting without deleting rows or blob bytes. Treat takedown as the normal
105105+long-term operator action; treat physical purge as a separate retention-policy
106106+decision.
107107+108108+Behavior:
109109+110110+- durable status distinct from user deactivation
111111+- `#account` event with `active: false` and `status: "takendown"`
112112+- `getRepoStatus` returns `active: false` and `status: "takendown"`
113113+- writes, repo exports, record reads, and blob reads are denied
114114+- sessions and OAuth grants are revoked
115115+116116+Remaining gap:
117117+118118+- ZDS should add a richer admin audit row for operator status changes. Until
119119+ then, preserve external notes with actor, reason, timestamps, and affected
120120+ DID.
121121+122122+## investigation checklist
123123+124124+Before changing account state:
125125+126126+```sh
127127+fly ssh console -a zds-pds -C "sqlite3 /data/zds.sqlite3 \
128128+ \"select handle,did,account_status,datetime(created_at,'unixepoch'),\
129129+ datetime(activated_at,'unixepoch'),email \
130130+ from accounts order by created_at;\""
131131+```
132132+133133+For a candidate DID, inspect footprint before deciding:
134134+135135+```sh
136136+did='did:plc:example'
137137+fly ssh console -a zds-pds -C "sqlite3 /data/zds.sqlite3 \"
138138+select 'records', count(*) from records where did='$did'
139139+union all select 'repo_blocks', count(*) from repo_blocks where did='$did'
140140+union all select 'commits', count(*) from commits where did='$did'
141141+union all select 'blobs', count(*) from blobs where did='$did'
142142+union all select 'spaces_owned', count(*) from permissioned_spaces where owner_did='$did'
143143+union all select 'space_actor_state', count(*) from permissioned_space_actor_state where actor_did='$did'
144144+union all select 'space_records', count(*) from permissioned_space_records where repo_did='$did'
145145+union all select 'space_repos', count(*) from permissioned_space_repos where repo_did='$did'
146146+union all select 'session_tokens', count(*) from session_tokens where did='$did'
147147+union all select 'oauth_tokens', count(*) from oauth_tokens where did='$did';
148148+\""
149149+```
150150+151151+Record:
152152+153153+- handle and DID
154154+- who requested action
155155+- why action is justified
156156+- whether the account is operator-owned test data or another user's account
157157+- current record/blob/space footprint
158158+- whether a backup is needed before action
159159+- planned status and retention policy
160160+161161+## current ZDS actions
162162+163163+For user-initiated deactivation, use the existing XRPC with the user's own
164164+bearer token.
165165+166166+For operator-driven takedown, use the admin XRPC with `ZDS_ADMIN_TOKEN`:
167167+168168+```sh
169169+curl -fsS -X POST "https://pds.zat.dev/xrpc/com.atproto.admin.updateSubjectStatus" \
170170+ -H "authorization: Bearer $ZDS_ADMIN_TOKEN" \
171171+ -H "content-type: application/json" \
172172+ --data-binary @- <<JSON | jq .
173173+{
174174+ "subject": {
175175+ "$type": "com.atproto.admin.defs#repoRef",
176176+ "did": "$did"
177177+ },
178178+ "takedown": {
179179+ "applied": true,
180180+ "ref": "operator-note-or-ticket"
181181+ }
182182+}
183183+JSON
184184+```
185185+186186+To clear an operator takedown:
187187+188188+```sh
189189+curl -fsS -X POST "https://pds.zat.dev/xrpc/com.atproto.admin.updateSubjectStatus" \
190190+ -H "authorization: Bearer $ZDS_ADMIN_TOKEN" \
191191+ -H "content-type: application/json" \
192192+ --data-binary @- <<JSON | jq .
193193+{
194194+ "subject": {
195195+ "$type": "com.atproto.admin.defs#repoRef",
196196+ "did": "$did"
197197+ },
198198+ "takedown": {
199199+ "applied": false
200200+ }
201201+}
202202+JSON
203203+```
204204+205205+If direct SQL is unavoidable in an emergency, the minimum semantic behavior is:
206206+207207+1. Set `account_status`.
208208+2. Insert a `#account` event with `active: false` and the precise status.
209209+3. Notify crawlers.
210210+4. Revoke tokens if credential compromise is part of the incident.
211211+212212+Do not perform only step 1 and call it done; downstream services learn about
213213+status through repo-status reads and firehose account events.
214214+215215+## verification
216216+217217+After deactivation or takedown:
218218+219219+```sh
220220+curl -fsS "https://pds.zat.dev/xrpc/com.atproto.sync.getRepoStatus?did=$did" | jq .
221221+```
222222+223223+Expected for ZDS takedown:
224224+225225+```json
226226+{
227227+ "did": "did:plc:...",
228228+ "active": false,
229229+ "status": "takendown"
230230+}
231231+```
232232+233233+Also verify:
234234+235235+- the account does not appear as active on the landing page
236236+- repo export, record reads, and blob reads do not continue to make unavailable
237237+ content public
238238+- the firehose emitted a recent `#account` event
239239+- relays/appviews eventually stop serving or redistributing the account's
240240+ content
241241+242242+## physical purge
243243+244244+Physical purge is different from account status.
245245+246246+Before purging rows or blob bytes:
247247+248248+- confirm this is operator-owned test data, or that policy/terms require purge
249249+- take a database backup or Fly volume snapshot if rollback matters
250250+- understand foreign-key cascades for the affected tables
251251+- decide whether global blob/block tables may contain shared data
252252+- record what was deleted
253253+254254+For non-test accounts, prefer a two-step process:
255255+256256+1. deactivate or takedown now
257257+2. purge after a retention window
258258+259259+This keeps the emergency action reversible while still stopping local hosting.
260260+261261+## current known test account
262262+263263+As of 2026-06-24, the likely disposable private-media test account on
264264+`pds.zat.dev` is:
265265+266266+- handle: `plyr-priv-test.pds.zat.dev`
267267+- DID: `did:plc:dm3jejwohil763nmotc6cu2m`
268268+- email: `plyr-priv-test@example.com`
269269+270270+Recent read-only footprint:
271271+272272+```text
273273+records: 0
274274+repo_blocks: 0
275275+commits: 0
276276+blobs: 3
277277+spaces_owned: 1
278278+space_actor_state: 1
279279+space_records: 1
280280+space_repos: 1
281281+session_tokens: 6
282282+oauth_tokens: 0
283283+```
284284+285285+Treat this as a candidate for testing the runbook. Do not use it as precedent
286286+for deleting a real user's account without a first-class admin action and audit
287287+trail.
288288+289289+## implementation backlog
290290+291291+ZDS should eventually add:
292292+293293+- explicit admin account-status endpoint or CLI
294294+- distinct statuses: `deactivated`, `suspended`, `takendown`, `deleted`
295295+- admin audit log entries for status changes
296296+- token revocation tied to operator actions
297297+- dry-run account footprint command
298298+- reviewed purge tool with table-by-table output
299299+- tests for `#account` status events and `getRepoStatus` behavior for each
300300+ status
+11-6
docs/operations.md
···44configuration, containers, invite-code administration, account migration notes,
55and release procedure.
6677+For account suspension, takedown, and test-account cleanup, see the
88+[account takedown runbook](account-takedown-runbook.md). ZDS supports temporary
99+account deactivation and operator takedown for hosted repo subjects; `suspended`
1010+and `deleted` remain protocol vocabulary without local operator workflows.
1111+712## local checks
813914Run these before committing:
···106111 creation. `describeServer` reflects this value.
107112- `ZDS_PERMISSIONED_DATA`: set to `true` to opt into experimental
108113 `com.atproto.space.*` permissioned-data routes. Default: disabled. The
109109- current implementation is intended to track the upstream experimental
110110- permissioned-data work while following the newer direction that removes
111111- protocol member lists. ZDS provides space lifecycle, private records, grants,
112112- credentials, writer repo state/oplogs, ranged blob reads, and notify routes.
113113- Reader/group semantics are application policy. Use `just smoke-permissioned`
114114- for the experimental smoke lane.
114114+ current implementation is a prototype, not a stable protocol contract. It
115115+ provides space lifecycle, private records, grants, credentials, writer repo
116116+ state/oplogs, ranged blob reads, and notify routes for local experiments.
117117+ Reader/group semantics remain application policy, while the newer proposal's
118118+ `com.atproto.simplespace.*` baseline is tracked as future alignment work.
119119+ Use `just smoke-permissioned` for the experimental smoke lane.
115120- Passkeys do not require a separate deployment secret. WebAuthn RP ID is
116121 derived from the public URL host.
117122
+210
docs/permissioned-data-proposal-94.md
···11+# permissioned data proposal 94 notes
22+33+These notes track Bluesky proposal PR #94 from the perspective of ZDS. They are
44+not implementation instructions and should not be treated as a stable protocol
55+contract. The proposal is a draft; use this page to reduce wrong-way risk while
66+keeping ZDS's current permissioned-data work clearly experimental.
77+88+PR: <https://github.com/bluesky-social/proposals/pull/94>
99+1010+## working posture
1111+1212+ZDS is a sandcastle for this surface. It is acceptable to make subtractive
1313+changes while there are no external production users, but avoid deepening older
1414+experimental shapes when the proposal has already moved away from them.
1515+1616+Prefer small alignment work that preserves optionality:
1717+1818+- keep the API reference and operator docs explicit that this is experimental
1919+- isolate prototype code under the existing `space` and permissioned-data
2020+ modules
2121+- add tests around durable semantics that are likely to survive
2222+- avoid adding new member-list or grant behavior in the old vocabulary
2323+2424+ZDS's immediate goal is modest: get ahead of the likely direction enough to
2525+store private data on the user's PDS, while keeping the implementation easy to
2626+reshape. Do not write code or docs as if the draft is settled.
2727+2828+## recent design pressure
2929+3030+On 2026-06-24, Richard Barnes pushed back publicly on the proposal's broad
3131+scope. His useful objection is that "permissioned data" appears to combine
3232+several product shapes that can have different needs:
3333+3434+- personal data: bookmarks, mutes, drafts
3535+- gated content: paid newsletters, subscriber-only posts
3636+- socially shared data: private posts and stories
3737+- groups: private forums, communities, group chats
3838+3939+He also questioned the cost of creating a parallel data universe and called out
4040+URI-scheme complexity around `ats://`. This is worth tracking even if the
4141+thread did not fully engage Daniel's earlier long-form diaries. Richard has
4242+done foundational protocol/security work, so treat the critique as meaningful
4343+design pressure rather than as implementation guidance.
4444+4545+Eli Mallon pointed back to the diary motivation: for permissioned data,
4646+"rebroadcastability" is an antipattern. That is the core distinction that makes
4747+this more than ordinary public repo records with app-level filtering. If a
4848+reader can freely rebroadcast the same bytes into the public sync graph, the
4949+privacy boundary has already failed.
5050+5151+Daniel's reply clarified the current bet: the motivating use case is groups,
5252+and the proposal tries not to be dogmatic about one protocol per product
5353+category. If the group-shaped primitive is expressive enough for the simpler
5454+cases without becoming overwrought, that is a reasonable design win. He also
5555+said he is still wrestling with URI scheme choice and is leaning back toward
5656+keeping `at`.
5757+5858+ZDS implications:
5959+6060+- keep our docs careful about `ats://`; do not invest in naming churn until the
6161+ proposal lands
6262+- keep private personal data as the motivating local use case, not a claim that
6363+ ZDS has solved groups
6464+- do not encode a universal group model into ZDS while the proposal is still
6565+ deliberately pushing group semantics up to applications/space hosts
6666+- preserve the ability to store private records and blobs now, but keep the
6767+ protocol surface clearly experimental
6868+6969+## broad matches
7070+7171+The current ZDS implementation broadly matches these proposal instincts:
7272+7373+- permissioned data is separate from public repo data
7474+- permissioned records use an `ats://` address that names space authority,
7575+ space type, space key, writer repo, collection, and record key
7676+- one writer has one permissioned repo per space
7777+- record sets are represented with LtHash-style set commitment state
7878+- general reader/group semantics should not be encoded as a universal protocol
7979+ member list
8080+- apps that receive access to a space should expect whole-space read semantics,
8181+ not just access to the authorizing user's own writer repo
8282+8383+## proposal deltas to track
8484+8585+These are the main known gaps between ZDS's current prototype and PR #94:
8686+8787+- Space declarations are lexicon definitions, not records. That appears to make
8888+ a space type behave like a collection/schema authority: one NSID-shaped path
8989+ declares a reusable space type that many space repos, accounts, spaces, and
9090+ apps can share. This trades away the direct repo-authority feel of records in
9191+ favor of namespace authority, fallback behavior, and a single path segment.
9292+ App designers or project maintainers may publish the initial declaration
9393+ using DNS and lexicon-publishing authority, but the declaration becomes a
9494+ reusable commons resource after that. Lexicon unpublishing also should not
9595+ break existing spaces that depend on the declaration, which makes lexicon
9696+ resolution override behavior appropriate in a way that would be surprising
9797+ for normal AT records.
9898+- `com.atproto.space.getMemberGrant` is older vocabulary. The proposal uses
9999+ `com.atproto.space.getDelegationToken` as a PDS-issued OAuth query and then
100100+ exchanges that with the space host through `getSpaceCredential`.
101101+- `getRepoOplog` is older vocabulary. The proposal uses `listRepoOps`.
102102+- The proposal adds `com.atproto.space.listRepos` so a space host can list known
103103+ writer repos in a space without enumerating readers.
104104+- The proposal adds `registerNotify` as an explicit syncer registration method.
105105+ ZDS currently records credential recipients and supports write/deletion
106106+ notifications, but does not expose this method shape.
107107+- Space credentials and delegation tokens have more specific JWT `typ`, `sub`,
108108+ `aud`, `client_id`, lifetime, and verification expectations than ZDS's
109109+ prototype helper names imply.
110110+- The proposal introduces optional client attestation for app-bound space
111111+ credentials.
112112+- Space-authority DID documents are expected to publish `#atproto_space` and
113113+ `#atproto_space_host` material. ZDS currently does not model this separately
114114+ from ordinary account/PDS signing material.
115115+- Commit state is more than an LtHash value. The draft includes random commit
116116+ `ikm`, a signature over commit context, and a MAC over the record-set hash.
117117+ This is a deliberate deniability design: do not accidentally make a
118118+ permissioned commit into a durable public proof of private content.
119119+- OAuth `space:` scopes distinguish `read` from `read_self` and separate
120120+ lifecycle/admin capability with `manage=`.
121121+- Permission-set records use proposal vocabulary such as `spaceType`; older ZDS
122122+ permission-set handling should be audited before relying on it.
123123+- Space deletion semantics need review. The draft distinguishes space-authority
124124+ deletion from member repo ownership and does not imply arbitrary erasure of
125125+ every writer's local data.
126126+127127+## implementation traps
128128+129129+These are low-level details to keep visible before attempting parity work:
130130+131131+- LtHash live-record elements must stay unique. The draft shape uses
132132+ `{collection}/{rkey}/{record_cid}` so the same live element is not added more
133133+ than once.
134134+- Byte order matters. LtHash lanes are little-endian, while the
135135+ TLS-style length-prefixed context used around signed commit material is
136136+ big-endian.
137137+- The operation log is a sync shortcut, not permanent history. A host may
138138+ compact or drop it; syncers must be able to compare set hashes and recover by
139139+ listing records.
140140+- Permissioned sync is relay-less and pull-based. Real-time behavior comes from
141141+ write notifications through the space authority, but correctness cannot rely
142142+ on every notification arriving.
143143+- Space types should be particular. Avoid designing one universal community
144144+ space that gives every app access to every modality; apps can group many
145145+ narrower spaces under a community authority at the application layer.
146146+147147+## export and backup pressure
148148+149149+The draft does not currently look like it wants a `getSpaceRepo` CAR equivalent
150150+for permissioned repos. That may be intentional: a permissioned repo is not
151151+necessarily the public MST repo shape, and backfill can be expressed through
152152+space enumeration plus `listRecords`.
153153+154154+For ZDS, avoid inventing a `getSpaceRepo` endpoint unless the proposal moves
155155+that way. The safer design pressure is exportability:
156156+157157+- residents should be able to enumerate their own permissioned spaces
158158+- residents should be able to enumerate their own writer repos and records
159159+ inside those spaces
160160+- backup/export can be a higher-level artifact, such as a manifest plus records
161161+ grouped by space
162162+- blobs should probably be referenced or streamed separately from record export
163163+ instead of assuming one giant blob archive is always practical
164164+- any future export path should be resumable or easy to retry for large media
165165+ sets
166166+167167+This is separate from sync. Sync methods need protocol parity; backup/export can
168168+be an operator or resident affordance built from stable lower-level primitives.
169169+170170+## simplespace surprise
171171+172172+PR #94 removes the protocol-level member list as a universal authorization
173173+substrate, but it also sketches a required PDS baseline called
174174+`com.atproto.simplespace.*`.
175175+176176+That baseline currently includes:
177177+178178+- `createSpace`
179179+- `updateSpace`
180180+- `deleteSpace`
181181+- `addMember`
182182+- `removeMember`
183183+- `listMembers`
184184+185185+It also introduces simple policies such as `public`, `member-list`, and
186186+`managing-app`, plus a managing-app access check hook.
187187+188188+This is not the same as reviving the older generic member-list experiment. If
189189+the proposal keeps `simplespace`, ZDS should treat it as a small baseline space
190190+management API while still leaving richer application/group semantics to apps.
191191+192192+Some commentary describes the boring access model as a list of DIDs. In ZDS
193193+terms, do not read that as permission to restore the old protocol member-list
194194+sync surface. Treat it as space-management policy, especially for
195195+`com.atproto.simplespace.*` and managing-app credential minting.
196196+197197+## useful tests before more surface
198198+199199+Before adding more endpoints, prefer tests that exercise durable semantics:
200200+201201+- per-space and per-writer repo isolation
202202+- `ats://` parsing and formatting
203203+- OAuth scope gating for owner reads, self reads, writes, and management
204204+- ranged blob reads through permissioned-data auth
205205+- write oplog ordering and cursor behavior
206206+- record-set commitment changes across create, update, and delete
207207+- deletion behavior for owner spaces versus writer repos
208208+209209+These tests should stay separate from public-repo conformance tests so the
210210+experimental surface can move without muddying stable PDS behavior.
+40-17
docs/permissioned-data.md
···11# permissioned data
2233-Permissioned data support is experimental and operator gated. ZDS documents the
44-`com.atproto.space.*` surface as experimental, and only enables the handlers
55-when explicitly configured:
33+Permissioned data support is experimental and operator gated. Treat the current
44+ZDS surface as a prototype for local experiments, not as a claim of parity with
55+the evolving upstream permissioned-data proposal. ZDS documents the
66+`com.atproto.space.*` routes as experimental, and only enables the handlers when
77+explicitly configured:
6879```sh
810ZDS_PERMISSIONED_DATA=true
···2426 <https://github.com/bluesky-social/atproto/tree/permissioned-data/lexicons/com/atproto/space>
2527- Reference implementation path:
2628 `packages/pds/src/actor-store/space` on the permissioned-data branch
2929+- Bluesky proposal PR:
3030+ <https://github.com/bluesky-social/proposals/pull/94>
3131+- Local alignment notes:
3232+ [permissioned-data proposal 94](permissioned-data-proposal-94.md)
27332834The upstream design is still moving. The May branch included protocol-level
2929-member lists. The later discussion is moving toward making space credentials
3030-the protocol substrate and leaving reader/group semantics to applications or
3131-space-host policy. ZDS follows that newer direction because this deployment has
3232-no external production users, so subtractive experimental changes are cheaper
3333-than carrying compatibility for a shape we no longer want.
3535+member lists. The later discussion moved toward making space credentials the
3636+protocol substrate and leaving reader/group semantics to applications or
3737+space-host policy. Proposal PR #94 keeps that broad direction, but also sketches
3838+a baseline `com.atproto.simplespace.*` management surface for PDS-managed
3939+spaces. ZDS has not implemented that newer proposal shape yet.
34403541Feedback should respect the research behind the sketch and avoid overfitting to
3642ZDS or plyr.fm. Name concrete implementation pressure, but frame it as input for
3743the broader protocol shape.
38443939-## current surface
4545+The current local motivation is private data storage on a resident's PDS. That
4646+is narrower than proving every group/community use case. Recent proposal
4747+discussion reinforces the same posture: groups are the hard case that may
4848+justify the primitive, but ZDS should not bake group semantics into the PDS just
4949+to get private records and blobs working early.
5050+5151+## current prototype surface
40524141-ZDS currently keeps the permissioned-data substrate:
5353+ZDS currently keeps a pre-proposal-94 permissioned-data substrate:
42544355- space lifecycle: `createSpace`, `getSpace`, `listSpaces`,
4456 `updateSpaceConfig`, `deleteSpace`
···4860- credentials and deletion fanout: `getMemberGrant`, `getSpaceCredential`,
4961 `notifySpaceDeleted`
50625151-ZDS deliberately does not expose protocol member-list routes:
6363+The proposal draft has since moved some names and responsibilities. In
6464+particular, `getMemberGrant` maps only loosely to the newer
6565+`getDelegationToken`, and `getRepoOplog` maps only loosely to `listRepoOps`.
6666+Keep that vocabulary difference visible when changing this area.
6767+6868+ZDS deliberately does not expose the older protocol member-list routes:
52695370- `addMember`
5471- `removeMember`
···6481ZDS treats permissioned data as a space credential and writer-repo substrate,
6582not as a universal group-membership system.
66836767-Applications own reader/group semantics: label rosters, supporter access,
8484+Applications own rich reader/group semantics: label rosters, supporter access,
6885personal private libraries, community roles, follower-only access, and similar
6969-policy all belong above the PDS substrate. If an application needs portable
7070-access state, it should model that state as application records in a
7171-permissioned space rather than relying on a PDS-wide member list.
8686+policy all belong above the low-level PDS substrate. If an application needs
8787+portable access state, it should model that state as application records in a
8888+permissioned space rather than relying on a universal PDS-wide member list.
8989+9090+The newer proposal draft may still require a small PDS-managed
9191+`com.atproto.simplespace.*` baseline for simple spaces. That is a compatibility
9292+target to evaluate, not permission to rebuild the older generic member-list
9393+experiment.
72947395For private spaces, ZDS currently mints space credentials only to the space
7496owner DID unless the space is public. That is the conservative default until
···93115- `permissioned_space_repos`: writer repo state and current record-set hash
94116- `permissioned_space_record_oplog`: incremental record changes by
95117 `(space, repo_did, rev, idx)`
9696-- `permissioned_space_credentials`: short-lived grants and credentials
118118+- `permissioned_space_credentials`: short-lived prototype grants and
119119+ credentials
97120- `permissioned_space_credential_recipients`: services to notify for writes and
98121 space deletion
99122···117140118141- `SpaceUri` parsing/formatting
119142- LtHash and set commitment primitives
120120-- member-grant and space-credential JWT helpers
143143+- proposal-shaped delegation-token and space-credential JWT helpers
121144122145Do not edit or patch the sibling `zat` repo from ZDS without stopping and
123146making the proposed Zat change explicit.
+3
docs/references.md
···185185 state, not merely JWT formatting concerns.
186186- Delegation-ready code should keep subject, actor, and controller identities
187187 distinct even before user-facing account delegation exists.
188188+- Account-status actions are local hosting decisions. For operator action,
189189+ distinguish temporary deactivation from host takedown, deletion, and physical
190190+ purge; see [account takedown runbook](account-takedown-runbook.md).
188191189192## active comparison work
190193