···4949- Passkeys are stored as account credentials: credential ID, public key,
5050 signature counter, friendly name, and timestamps. Password login remains
5151 available unless an explicit account policy changes that.
5252+- App passwords are account-owned credentials. Management requires password
5353+ auth, generated passwords are shown once, and revoking a credential revokes
5454+ active sessions created with it.
5255- Password sessions are storage-backed token families. Accept session JWTs only
5356 when their JTI is active, and keep subject/actor/controller fields distinct in
5457 account-plane audit work.
+6-4
README.md
···4455> name credit: jim (`calabro.io`) suggested `zds`.
6677-`zds` stores atproto accounts, repos, records, blobs, sessions, oauth state, and
88-identity state. it uses [`zat`](../zat) for protocol primitives: syntax, tids,
99-did resolution, jwt helpers, dag-cbor, car, mst, repo verification, and key
1010-encoding. WebAuthn support comes from
77+`zds` stores atproto accounts, repos, records, blobs, sessions, oauth state,
88+account-security state, and identity state. it uses [`zat`](../zat) for
99+protocol primitives: syntax, tids, did resolution, jwt helpers, dag-cbor, car,
1010+mst, repo verification, and key encoding. WebAuthn support comes from
1111[`webauthn`](https://tangled.org/zzstoatzz.io/webauthn).
12121313## docs
···107107 Revocation also invalidates active sessions created with that app password.
108108- Passkeys are optional account credentials for the OAuth login page. ZDS stores
109109 credential IDs, public keys, counters, names, and last-use timestamps.
110110+- `/security` is the local account-security page for passkey and app-password
111111+ management.
110112111113## references
112114
+3-1
docs/account-security.md
···4646`/security` is the local account-security surface. It signs in with the account
4747password, then lists passkeys and app passwords for that account. App passwords
4848can be created and revoked from the page; the generated password is displayed
4949-once and is not recoverable later because only the hash is stored.
4949+once and is not recoverable later because only the hash is stored. The
5050+privileged-app-password checkbox is labeled as trusted-client behavior because
5151+ordinary clients should not request it casually.
50525153## references
5254
+7-2
docs/architecture.md
···1313 helpers, DAG-CBOR, CAR, MST, repo verification, OAuth helpers, and streaming
1414 clients.
1515- `zds` owns PDS policy and persistence: accounts, repo writes, sessions,
1616- storage, server routes, sync production, account migration, and the local
1717- login/security surface.
1616+ app passwords, passkeys, storage, server routes, sync production, account
1717+ migration, and the local login/security surface.
1818- SQLite stores account, repo, commit, token, OAuth, blob metadata, and identity
1919 state.
2020- Password sessions are durable rows keyed by access and refresh JWT IDs. Refresh
···7272by the local `webauthn` dependency and stored in SQLite. Discoverable passkey
7373login is supported so an account can be chosen by the authenticator during the
7474OAuth flow.
7575+7676+`/security` is the local management surface for passkeys and app passwords. It
7777+uses account-password authentication for management actions, matching the
7878+official PDS boundary that app passwords cannot mint or revoke other app
7979+passwords.
75807681PDS-owned `app.bsky.actor` preferences are handled locally because the official
7782PDS implements those routes. Appview reads and writes remain behind the generic
+7
docs/operations.md
···129129Migration requires working public HTTPS, stable JWT secret, email delivery, blob
130130storage, and writable SQLite storage.
131131132132+## account security
133133+134134+The local security surface lives at `/security`. It is for account-owner
135135+management of passkeys and app passwords and requires the account password for
136136+management actions. App-password revocation also revokes active sessions that
137137+were created with that app password.
138138+132139## conformance
133140134141Useful external checks:
+4-3
docs/passkeys.md
···22222323If the login hint resolves to an account with saved passkeys, the OAuth page
2424offers passkey login first and keeps password login available. If no passkey is
2525-saved for the account, password login is shown directly with a link to the
2626-security page.
2525+saved for the account, password login is shown directly and points the user to
2626+`/security` after authentication.
27272828Discoverable passkey login is supported: the browser may return a credential
2929without the page first choosing an account. ZDS resolves the credential ID to
···34343535Passkeys are managed from `/security`, alongside app passwords. That page signs
3636in with the account password, lists saved passkeys, and supports add, rename,
3737-and delete.
3737+and delete. Registration uses a browser WebAuthn prompt; ZDS stores only the
3838+credential ID, public key, counter, name, and timestamps.
38393940Deleting a passkey removes it from ZDS. The browser, OS, or password manager
4041may still keep its copy, but it can no longer be used with this PDS.
+10-2
docs/references.md
···5959- benchmark design and metastore comparisons
6060- delegation groundwork
61616262+### webauthn
6363+6464+Link: [zzstoatzz.io/webauthn](https://tangled.org/zzstoatzz.io/webauthn)
6565+6666+ZDS uses this local Zig WebAuthn library for passkey ceremony parsing and
6767+verification. Keep PDS policy in ZDS, but prefer moving general WebAuthn
6868+encoding, challenge, and signature-verification fixes into this library.
6969+6270### Pegasus
63716472Link: [futur.blue/pegasus](https://tangled.org/futur.blue/pegasus)
···173181 IDs, public keys, counters, names, and timestamps, and verify WebAuthN
174182 assertions before authorizing OAuth requests.
175183- Account-plane security should be storage-backed. Sessions, refresh rotation,
176176- app passwords, OAuth grants, passkeys, and revocation are not merely JWT
177177- formatting concerns.
184184+ app passwords, OAuth grants, passkeys, and revocation are durable account
185185+ state, not merely JWT formatting concerns.
178186- Delegation-ready code should keep subject, actor, and controller identities
179187 distinct even before user-facing account delegation exists.
180188