Pds: MapAtProtoPds cannot exclude or override individual XRPC endpoints #39
Labels
No labels
breaking-change
bug
documentation
duplicate
enhancement
good first issue
help wanted
performance
question
wontfix
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Grandiras/ATProto.NET#39
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
MapAtProtoPds()maps all PDS XRPC endpoints unconditionally. There is no way to exclude, replace, or wrap a single endpoint — attempting to map the same route yourself produces an ambiguous-match conflict.Concrete need: Updraft must own
com.atproto.server.createAccountto enforce real invite-code validation (see the invite-codes issue). The only way today is terminal middleware registered beforeMapAtProtoPds()that matches the path manually and never callsnext()— workable, but it bypasses endpoint routing (no endpoint metadata, no route-level auth policies, easy to get subtly wrong).Suggestion, either of:
MapAtProtoPds(options => options.Exclude("com.atproto.server.createAccount"))so consumers can map their own implementation, orPdsService(e.g. anIAccountCreationGuardinvoked insideCreateAccountAsync) so the default endpoint stays but policy is pluggable.The hook approach composes better with the invite-store suggestion; the exclude approach is more general.
🤖 On it — follow along in the action run; the reply will link the full session transcript.
Implemented the exclude/override option (the more general of the two suggestions) on branch
claude/issue-39.What changed
New overload
MapAtProtoPds(Action<PdsEndpointOptions>)— the parameterlessMapAtProtoPds()is untouched and still maps everything, so this is source- and binary-compatible.PdsEndpointOptionssurface:Exclude(params string[] nsids)— skips mapping those endpoints, so mapping your own on the same path produces no ambiguous-match conflict.Only(params string[] nsids)— maps just the listed subset (e.g. a read-only mirror).Excludewins overOnly.Configure(nsid, builder => …)/ConfigureAll((nsid, builder) => …)— apply route conventions to the endpoints that are mapped: authorization policies, endpoint filters, rate limiting, metadata. This covers the "no route-level auth policies" half of the complaint without excluding anything.IsMapped(nsid)— query the resolved configuration.PdsEndpointNamesholds constants for all twelve NSIDs plusAll. Every NSID passed to the options is validated against that list and throwsArgumentExceptionat startup — a typo likecreateAcountfails loudly instead of silently leaving the built-in endpoint mapped. Mapped endpoints also now carry their NSID as the endpoint display name (previously the generatedHTTP: POST /xrpc/…), so they're identifiable in logs and diagnostics.On the hook alternative
I did not add
IAccountCreationGuard. The exclude approach unblocks the concrete Updraft need on its own, and aPdsServicepolicy hook is better designed together with the invite-store issue it composes with — adding a half-shaped hook now would likely need reworking there. Worth noting: with the exclude route, a customcreateAccounthandler still has to callPdsService.CreateAccountAsyncitself for the non-invite part, which it can (the service is a public singleton).Verification
dotnet buildon the full solution: 0 errors, no new warnings inATProtoNet.Pds.dotnet test tests/ATProtoNet.Tests/: 1029 passed, 0 failed — including 17 new tests inPdsEndpointOptionsTests, which spin up aTestServerand assert that an excluded endpoint 404s, that a host-mapped replacement on the same route actually serves the request (the ambiguous-match case),Only/Excludeprecedence, and thatConfigure/ConfigureAllmetadata plus the NSID display name reach the matched endpoint at request time.Docs updated: new "Excluding or Overriding Individual Endpoints" section in
docs/pds.md, a cross-reference indocs/xrpc-handlers.md, andCHANGELOG.mdunder[Unreleased](Added+Changed).⏱ 7m 11s · 53 turns · tokens: 87.3k in / 29.1k out (+3818.3k cache-read)
📜 Full session transcript
→ Opened PR: #59