perlsky is a Perl 5 implementation of an AT Protocol Personal Data Server.
1# Endpoint Conformance
2
3As of 2026-03-12, `perlsky` has three broad classes of external surface:
4
5- `executable reference-differenced`
6 These endpoints or behaviors are compared directly against the official `@atproto/pds` runtime through `script/differential-validate`.
7- `locally regression-tested only`
8 These endpoints are covered in `t/*.t`, but the current official runtime either does not expose the same surface or is not a useful like-for-like oracle.
9- `intentional local divergence`
10 These are deliberate product or operator choices, not accidental compatibility gaps.
11
12## Executable Reference-Differenced
13
14The current differential harness directly compares:
15
16- core account/session flows
17 `describeServer`, `createAccount`, `createSession`, `getSession`, `refreshSession`, `deleteSession`, `getAccountInviteCodes`, app-password lifecycle, password-boundary behavior, and email/account-delete flows
18- identity flows
19 `resolveHandle`, `resolveDid`, `resolveIdentity`, PLC credential/signature/update flows, and handle-conflict semantics
20- repo and sync flows
21 `createRecord`, `putRecord`, `deleteRecord`, `applyWrites`, `listRecords`, `getRecord`, `getLatestCommit`, `getHead`, `getBlocks`, `getRepo`, `getCheckout`, `listBlobs`, `getBlob`, `listRepos`, `listMissingBlobs`, and `importRepo`
22- moderation/admin flows
23 `updateSubjectStatus`, `getSubjectStatus`, `getAccountInfo`, `getAccountInfos`, `sendEmail`, `updateAccountEmail`, `updateAccountHandle`, `updateAccountPassword`, `deleteAccount`, `disableAccountInvites`, `enableAccountInvites`, `disableInviteCodes`, and admin invite-code listing semantics
24- eventing and crawler behavior
25 `subscribeRepos`, firehose cursor behavior, and outbound crawler notification semantics after local repo activity
26
27For the exact current comparison set, use:
28
29```sh
30script/differential-validate
31PERLSKY_DIFF_ACCOUNT_DID_METHOD=did:plc script/differential-validate
32```
33
34## Locally Regression-Tested Only
35
36These surfaces are covered locally but are not currently executable-differenced because the official runtime does not expose the same endpoint or does not provide a like-for-like comparison surface:
37
38- `com.atproto.sync.requestCrawl`
39 The reference runtime still drives crawler notices internally, but the current official build does not expose the endpoint directly as a comparable external surface.
40- `com.atproto.sync.notifyOfUpdate`
41 Locally covered in `t/crawler-status-surfaces.t`; not wired as a comparable public surface in the current official runtime.
42- `com.atproto.admin.updateAccountSigningKey`
43 Locally covered in `t/admin-account-surfaces.t` and aligned to lexicon-void response semantics; no matching official external endpoint wiring was found in the current reference build.
44- `com.atproto.admin.searchAccounts`
45 Implemented and tested locally in `t/admin-account-surfaces.t`; not exposed by the current official runtime.
46- `com.atproto.sync.listReposByCollection`
47 Present in the published lexicon and covered locally in `t/discovery-surfaces.t`, but not exposed by the current official runtime.
48- `com.atproto.temp.*`
49 `requestPhoneVerification`, `revokeAccountCredentials`, `checkSignupQueue`, and `dereferenceScope` are locally covered in `t/temp-endpoints.t`, while `fetchLabels` is covered in `t/label-rpc-surfaces.t`; these do not have a like-for-like official public comparison surface.
50- local label RPCs
51 `com.atproto.label.queryLabels`, `subscribeLabels`, and `com.atproto.temp.fetchLabels` are verified locally in `t/label-rpc-surfaces.t` and `t/labels.t`; the official runtime does not provide a directly comparable local-labeler implementation.
52- local appview emulation
53 `app.bsky.actor.getPreferences`, `putPreferences`, `notification.getPreferences`, `notification.putPreferencesV2`, and the conservative local feed/thread/profile fallback behavior are locally regression-tested rather than compared against an official self-hosted AppView implementation.
54
55## Intentional Local Divergences
56
57These are currently deliberate:
58
59- admin bearer shortcut
60 `perlsky` still accepts a local bearer-style admin secret in addition to strict Basic auth.
61- testing-friendly email toggles
62 `testing_allow_unauthenticated_email_confirm` and `testing_auto_confirm_email` intentionally keep no-SMTP testing practical.
63- self-service invite issuance
64 Disabled by default, but available behind `self_service_invite_codes`.
65- `applyWrites` missing-delete error shape
66 The official runtime still returns `500 InternalServerError` for one missing-delete path; `perlsky` intentionally keeps the cleaner `400 InvalidRequest`.
67
68## Reading This With `docs/TEST_AUDIT.md`
69
70- Use this file to answer “is this surface reference-differenced, local-only, or intentionally different?”
71- Use [TEST_AUDIT.md](./TEST_AUDIT.md) to answer “what specific behaviors were audited, fixed, or are still known gaps?”