perlsky is a Perl 5 implementation of an AT Protocol Personal Data Server.
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

Document current OAuth and conformance status

alice 91d4f552 812a63f9

+17 -9
+1 -1
README.md
··· 35 35 - The deployment guide includes a reverse-proxy layout, a sample `systemd` unit, validation commands, and a `createAccount` example for bootstrapping the first user. 36 36 - `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. 37 37 - 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. 38 - - 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. 38 + - 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. 39 39 - If `service_handle_domain` is `example.com`, submitting `handle: "alice"` to `com.atproto.server.createAccount` creates `alice.example.com`. 40 40 - If `invite_code_required` is enabled, public signup is disabled until a valid invite code is supplied. 41 41 - `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
··· 260 260 - `describeServer.availableUserDomains` matching `service_handle_domain` 261 261 - a per-handle `/.well-known/atproto-did` response returning the account DID when queried on the handle host 262 262 263 - 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`. 263 + 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`. 264 264 265 265 ## First Account 266 266
+15 -7
docs/TEST_AUDIT.md
··· 1 1 # Test Audit Status 2 2 3 - As of 2026-03-12, the focused test-correctness and reference-audit pass is complete on rewritten history through `50447c9`. 3 + As of 2026-03-12, the focused test-correctness and reference-audit pass is complete on rewritten history through `812a63f`. 4 4 5 5 That does not mean every test has been manually revalidated against every other PDS implementation line by line. It means: 6 6 ··· 13 13 The current baseline for saying "the audited suite is green" is: 14 14 15 15 - `prove -lr t` 16 - - last green result in the realigned Meridian worktree: `Files=40, Tests=2258` 16 + - last green result in the realigned Meridian worktree: `Files=41, Tests=2318` 17 17 - `prove -lv t/server-auth.t` 18 18 - `perl -c script/differential-validate` 19 19 - `PERLSKY_RUN_REFERENCE_DIFF=1 prove -lv t/reference-differential.t` ··· 45 45 - Firehose tests must not assume the smallest possible CAR diff. The reference runtime guarantees normalized behavior, not a minimal encoding. 46 46 - Label replay and cursor handling need exclusive replay semantics, proper future-cursor rejection, and forward progress across unhandled backlog events. 47 47 - `com.atproto.repo.listMissingBlobs` needed a real implementation rather than an always-empty placeholder. 48 + - ATProto OAuth `include:<nsid>` permission-set scopes need to be compiled into concrete repo/RPC permissions instead of being echoed back as inert strings. 48 49 - Deactivated accounts should still be able to establish and refresh sessions, but those responses must stay marked `active=false` with `status=deactivated`. 49 50 - 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. 51 + - 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. 50 52 - `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. 51 53 - `com.atproto.identity.resolveHandle` should reject malformed handles with `400 InvalidHandle`, not quietly treat them as misses. 52 54 - `com.atproto.sync.getBlob` should ship the same download-hardening headers as the reference PDS (`X-Content-Type-Options`, `Content-Disposition`, `Content-Security-Policy`). ··· 85 87 | `t/email-confirmation.t` | audited local regression | intentionally testing-friendly email flow | 86 88 | `t/event-stream.t` | audited local regression | wire-format, malformed frame, and event decoding coverage | 87 89 | `t/extended-api.t` | audited local regression | broad XRPC behavior including invites and moderation-adjacent flows | 88 - | `t/external-surface.t` | audited local regression | external repo/account surface including missing-blob behavior | 90 + | `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 | 89 91 | `t/firehose.t` | audited local regression | repo subscription lifecycle, cursor, and CAR behavior | 90 92 | `t/identity.t` | local correctness/infrastructure | handle and DID identity flow coverage | 91 - | `t/import-repo.t` | audited local regression | import/snapshot restore behavior | 93 + | `t/import-repo.t` | audited local regression | import/snapshot restore behavior, including perlsky's intentionally tolerant malformed-record import semantics | 92 94 | `t/invite-gating.t` | audited local regression | self-service invite flag behavior | 93 95 | `t/ipld-canonical.t` | local correctness/infrastructure | canonical IPLD encoding invariants | 94 96 | `t/ipld-codecs.t` | local correctness/infrastructure | DAG-CBOR and codec coverage | 95 97 | `t/labels.t` | audited local regression | label persistence, replay, negation, and cursor behavior | 96 98 | `t/metrics.t` | audited local regression | metrics endpoint, token-gating smoke, and instrumentation contract for local appview behavior | 97 99 | `t/moderation.t` | audited local regression | takedown visibility and moderation behavior | 98 - | `t/pds_smoke.t` | local correctness/infrastructure | broad local PDS smoke | 100 + | `t/oauth-include.t` | audited local regression | permission-set scope expansion and least-privilege enforcement from `include:<nsid>` scopes | 101 + | `t/oauth-permissions.t` | audited local regression | granular OAuth permission enforcement across repo/blob/rpc scope families | 102 + | `t/oauth-scopes.t` | audited local regression | OAuth scope parsing, normalization, and token-grant shaping | 103 + | `t/oauth.t` | audited local regression | OAuth provider metadata, PAR, PKCE, DPoP, and token lifecycle coverage | 104 + | `t/pds_smoke.t` | local correctness/infrastructure | broad local PDS smoke; still intentionally optimistic and should only carry a small number of negative assertions | 99 105 | `t/plc-identity.t` | direct reference differential | PLC mock driven by official library semantics | 100 106 | `t/reference-differential-plc.t` | direct reference differential | official runtime comparison in PLC mode | 101 107 | `t/reference-differential.t` | direct reference differential | official runtime comparison in baseline mode | 102 - | `t/remote-handle-resolution.t` | audited local regression | remote handle resolution behavior and invalid-handle rejection | 103 - | `t/repo-api.t` | audited local regression | record mutation and read semantics | 108 + | `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 | 109 + | `t/repo-api.t` | audited local regression | record mutation and read semantics, but still lighter than ideal on some negative/reference edge cases | 104 110 | `t/repo-firehose-car.t` | audited local regression | repo commit CAR shape and firehose interactions | 105 111 | `t/repo_formats.t` | audited local regression | direct repo wire-format and CAR expectations | 106 112 | `t/server-auth.t` | direct reference differential | auth/session/service-auth behavior repeatedly compared to official runtime | ··· 117 123 - every test has already been manually checked against Pegasus 118 124 - every test has already been manually checked against RSKY 119 125 - every local extension has already been split cleanly into "reference-compatible" versus "deliberate product policy" at the per-assertion level 126 + - every currently green suite has the same confidence level; a few suites remain broader or more product-specific than the core conformance tests 120 127 121 128 That fuller pass is still available as a next phase. 122 129 ··· 130 137 4. decide whether to tighten admin auth to reference semantics or document the bearer shortcut as a permanent extension 131 138 5. move the testing-friendly email confirmation path behind an explicit smoke/dev switch instead of ambient behavior 132 139 6. keep narrowing the local `ServiceProxy` surface until every locally answered `app.bsky.*` field is either authoritative or explicitly documented as a local-only extension 140 + 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 133 141 134 142 ## Practical Reading Of The Current Status 135 143