···3535- The deployment guide includes a reverse-proxy layout, a sample `systemd` unit, validation commands, and a `createAccount` example for bootstrapping the first user.
3636- `perlsky` now includes a built-in ATProto OAuth provider surface, so modern third-party clients that use the Bluesky OAuth flow can authenticate directly against your PDS without extra auth-server infrastructure.
3737- The built-in provider publishes `/.well-known/oauth-protected-resource`, `/.well-known/oauth-authorization-server`, `/oauth/jwks`, `/oauth/par`, `/oauth/authorize`, `/oauth/token`, and `/oauth/revoke` from the same host as the PDS.
3838-- OAuth scope enforcement now understands both the transition scopes (`transition:generic`, `transition:email`, `transition:chat.bsky`) and the newer granular permission families (`account:`, `identity:`, `repo:`, `blob:`, and `rpc:`), so clients that request narrower ATProto permissions can be authorized without silently getting broader access.
3838+- OAuth scope enforcement now understands both the transition scopes (`transition:generic`, `transition:email`, `transition:chat.bsky`), the newer granular permission families (`account:`, `identity:`, `repo:`, `blob:`, and `rpc:`), and `include:<nsid>` permission-set scopes, so clients that request narrower ATProto permissions can be authorized without silently getting broader access.
3939- If `service_handle_domain` is `example.com`, submitting `handle: "alice"` to `com.atproto.server.createAccount` creates `alice.example.com`.
4040- If `invite_code_required` is enabled, public signup is disabled until a valid invite code is supplied.
4141- `com.atproto.server.createInviteCode` and `com.atproto.server.createInviteCodes` are admin-only by default. Set `self_service_invite_codes` to enable self-service invite minting for authenticated full-access sessions, limited to the caller's own account.
+1-1
docs/DEPLOYMENT.md
···260260- `describeServer.availableUserDomains` matching `service_handle_domain`
261261- a per-handle `/.well-known/atproto-did` response returning the account DID when queried on the handle host
262262263263-Modern third-party ATProto OAuth clients should now be able to discover and authenticate directly against your PDS. The built-in provider enforces both the transition scopes (`transition:generic`, `transition:email`, `transition:chat.bsky`) and the granular ATProto permission families (`account:`, `identity:`, `repo:`, `blob:`, and `rpc:`). For example, a client like Tangled will start by fetching `/.well-known/oauth-protected-resource`, follow the advertised authorization-server metadata, submit a pushed authorization request, and then send the browser through `/oauth/authorize`.
263263+Modern third-party ATProto OAuth clients should now be able to discover and authenticate directly against your PDS. The built-in provider enforces both the transition scopes (`transition:generic`, `transition:email`, `transition:chat.bsky`), the granular ATProto permission families (`account:`, `identity:`, `repo:`, `blob:`, and `rpc:`), and `include:<nsid>` permission-set scopes. Permission-set scopes are resolved through lexicon records and compiled down to concrete repo/RPC permissions before tokens are issued, so apps requesting spec-compliant permission bundles still get least-privilege tokens. For example, a client like Tangled will start by fetching `/.well-known/oauth-protected-resource`, follow the advertised authorization-server metadata, submit a pushed authorization request, and then send the browser through `/oauth/authorize`.
264264265265## First Account
266266
+15-7
docs/TEST_AUDIT.md
···11# Test Audit Status
2233-As of 2026-03-12, the focused test-correctness and reference-audit pass is complete on rewritten history through `50447c9`.
33+As of 2026-03-12, the focused test-correctness and reference-audit pass is complete on rewritten history through `812a63f`.
4455That does not mean every test has been manually revalidated against every other PDS implementation line by line. It means:
66···1313The current baseline for saying "the audited suite is green" is:
14141515- `prove -lr t`
1616- - last green result in the realigned Meridian worktree: `Files=40, Tests=2258`
1616+ - last green result in the realigned Meridian worktree: `Files=41, Tests=2318`
1717- `prove -lv t/server-auth.t`
1818- `perl -c script/differential-validate`
1919- `PERLSKY_RUN_REFERENCE_DIFF=1 prove -lv t/reference-differential.t`
···4545- Firehose tests must not assume the smallest possible CAR diff. The reference runtime guarantees normalized behavior, not a minimal encoding.
4646- Label replay and cursor handling need exclusive replay semantics, proper future-cursor rejection, and forward progress across unhandled backlog events.
4747- `com.atproto.repo.listMissingBlobs` needed a real implementation rather than an always-empty placeholder.
4848+- ATProto OAuth `include:<nsid>` permission-set scopes need to be compiled into concrete repo/RPC permissions instead of being echoed back as inert strings.
4849- Deactivated accounts should still be able to establish and refresh sessions, but those responses must stay marked `active=false` with `status=deactivated`.
4950- Local `app.bsky.*` emulation must be conservative: only synthesize owner-local feed/thread data when the PDS can answer authoritatively, and proxy upstream instead of inventing partial global state.
5151+- Account email handling needs consistent normalization on write, lookup, session creation, and confirmation checks; treating email case inconsistently leaves both tests and user-facing auth behavior brittle.
5052- `app.bsky.actor.putPreferences` and `app.bsky.notification.putPreferencesV2` need shape validation; unvalidated merges are not a critical exploit here, but they are a real correctness and hardening issue.
5153- `com.atproto.identity.resolveHandle` should reject malformed handles with `400 InvalidHandle`, not quietly treat them as misses.
5254- `com.atproto.sync.getBlob` should ship the same download-hardening headers as the reference PDS (`X-Content-Type-Options`, `Content-Disposition`, `Content-Security-Policy`).
···8587| `t/email-confirmation.t` | audited local regression | intentionally testing-friendly email flow |
8688| `t/event-stream.t` | audited local regression | wire-format, malformed frame, and event decoding coverage |
8789| `t/extended-api.t` | audited local regression | broad XRPC behavior including invites and moderation-adjacent flows |
8888-| `t/external-surface.t` | audited local regression | external repo/account surface including missing-blob behavior |
9090+| `t/external-surface.t` | audited local regression | external repo/account surface including missing-blob behavior; still mixes surface inventory with conformance assertions and should stay documented that way |
8991| `t/firehose.t` | audited local regression | repo subscription lifecycle, cursor, and CAR behavior |
9092| `t/identity.t` | local correctness/infrastructure | handle and DID identity flow coverage |
9191-| `t/import-repo.t` | audited local regression | import/snapshot restore behavior |
9393+| `t/import-repo.t` | audited local regression | import/snapshot restore behavior, including perlsky's intentionally tolerant malformed-record import semantics |
9294| `t/invite-gating.t` | audited local regression | self-service invite flag behavior |
9395| `t/ipld-canonical.t` | local correctness/infrastructure | canonical IPLD encoding invariants |
9496| `t/ipld-codecs.t` | local correctness/infrastructure | DAG-CBOR and codec coverage |
9597| `t/labels.t` | audited local regression | label persistence, replay, negation, and cursor behavior |
9698| `t/metrics.t` | audited local regression | metrics endpoint, token-gating smoke, and instrumentation contract for local appview behavior |
9799| `t/moderation.t` | audited local regression | takedown visibility and moderation behavior |
9898-| `t/pds_smoke.t` | local correctness/infrastructure | broad local PDS smoke |
100100+| `t/oauth-include.t` | audited local regression | permission-set scope expansion and least-privilege enforcement from `include:<nsid>` scopes |
101101+| `t/oauth-permissions.t` | audited local regression | granular OAuth permission enforcement across repo/blob/rpc scope families |
102102+| `t/oauth-scopes.t` | audited local regression | OAuth scope parsing, normalization, and token-grant shaping |
103103+| `t/oauth.t` | audited local regression | OAuth provider metadata, PAR, PKCE, DPoP, and token lifecycle coverage |
104104+| `t/pds_smoke.t` | local correctness/infrastructure | broad local PDS smoke; still intentionally optimistic and should only carry a small number of negative assertions |
99105| `t/plc-identity.t` | direct reference differential | PLC mock driven by official library semantics |
100106| `t/reference-differential-plc.t` | direct reference differential | official runtime comparison in PLC mode |
101107| `t/reference-differential.t` | direct reference differential | official runtime comparison in baseline mode |
102102-| `t/remote-handle-resolution.t` | audited local regression | remote handle resolution behavior and invalid-handle rejection |
103103-| `t/repo-api.t` | audited local regression | record mutation and read semantics |
108108+| `t/remote-handle-resolution.t` | audited local regression | remote handle resolution behavior and invalid-handle rejection, with some malformed/upstream-failure branches still worth expanding |
109109+| `t/repo-api.t` | audited local regression | record mutation and read semantics, but still lighter than ideal on some negative/reference edge cases |
104110| `t/repo-firehose-car.t` | audited local regression | repo commit CAR shape and firehose interactions |
105111| `t/repo_formats.t` | audited local regression | direct repo wire-format and CAR expectations |
106112| `t/server-auth.t` | direct reference differential | auth/session/service-auth behavior repeatedly compared to official runtime |
···117123- every test has already been manually checked against Pegasus
118124- every test has already been manually checked against RSKY
119125- every local extension has already been split cleanly into "reference-compatible" versus "deliberate product policy" at the per-assertion level
126126+- every currently green suite has the same confidence level; a few suites remain broader or more product-specific than the core conformance tests
120127121128That fuller pass is still available as a next phase.
122129···1301374. decide whether to tighten admin auth to reference semantics or document the bearer shortcut as a permanent extension
1311385. move the testing-friendly email confirmation path behind an explicit smoke/dev switch instead of ambient behavior
1321396. keep narrowing the local `ServiceProxy` surface until every locally answered `app.bsky.*` field is either authoritative or explicitly documented as a local-only extension
140140+7. keep documenting broad suites like `t/extended-api.t`, `t/external-surface.t`, and `t/import-repo.t` as mixed conformance-plus-product coverage rather than over-claiming that every assertion is a pure reference check
133141134142## Practical Reading Of The Current Status
135143