One of four plugin-API seams from building 33 out-of-tree plugins on the #206 loader (report). The most-hit finding: 7+ consumers.
The gap
A plugin can register routes outside its one prefix, but the loader WAC-exempts only that single prefix. So:
/.well-known/* works by luck — core happens to blanket-exempt it (nip05 nostr.json, webfinger, the DAV discovery docs). The two most-wanted .well-known docs a deployment serves ride an undocumented coincidence.
- Fixed protocol roots do NOT work —
/api,/oauth (mastodon), /xrpc (bluesky), /ap (activitypub), /_matrix (matrix) all 401 at the WAC hook until the operator hand-passes appPaths. The plugin cannot self-exempt the paths the protocol fixes.
- Parameterized paths can't be expressed at all —
didweb/ must serve /<user>/did.json inside the pod's own WAC-governed namespace; appPaths matches literal prefixes, and the DID method pins the URL (no fake root to escape to).
Witnessed collision (both directions)
webfinger/ and remotestorage/ both legitimately own parts of /.well-known/webfinger. Load one order → boot fails with a duplicate-route error (and the loader drops err.code — filed as #599). Load the other → the try/catch-guarded claimant silently loses its links. Loud failure or silent loss, both wrong: for a shared discovery document the reservation primitive isn't enough — a link/JRD registry (api.webfinger.addLink) is the real fix for that case.
Counter-witnesses that scope it: micropub/ and jmap/ needed no reservation because their endpoints are client-/session-discovered, not protocol-fixed. The seam is for protocols that pin absolute paths, not API shims per se.
Sketch
api.reservePath('/xrpc') / api.reservePath('/:user/did.json') (or paths: [...] on the loader entry): the loader WAC-exempts and claims each deliberately, and reports collisions at boot. Plus api.webfinger.addLink(...) (or a generic link registry) for the shared-document case.
Cost
Small-medium — generalizes the existing appPaths mechanism from operator config to plugin declaration.
One of four plugin-API seams from building 33 out-of-tree plugins on the #206 loader (report). The most-hit finding: 7+ consumers.
The gap
A plugin can register routes outside its one
prefix, but the loader WAC-exempts only that singleprefix. So:/.well-known/*works by luck — core happens to blanket-exempt it (nip05nostr.json, webfinger, the DAV discovery docs). The two most-wanted.well-knowndocs a deployment serves ride an undocumented coincidence./api,/oauth(mastodon),/xrpc(bluesky),/ap(activitypub),/_matrix(matrix) all 401 at the WAC hook until the operator hand-passesappPaths. The plugin cannot self-exempt the paths the protocol fixes.didweb/must serve/<user>/did.jsoninside the pod's own WAC-governed namespace;appPathsmatches literal prefixes, and the DID method pins the URL (no fake root to escape to).Witnessed collision (both directions)
webfinger/andremotestorage/both legitimately own parts of/.well-known/webfinger. Load one order → boot fails with a duplicate-route error (and the loader dropserr.code— filed as #599). Load the other → the try/catch-guarded claimant silently loses its links. Loud failure or silent loss, both wrong: for a shared discovery document the reservation primitive isn't enough — a link/JRD registry (api.webfinger.addLink) is the real fix for that case.Counter-witnesses that scope it:
micropub/andjmap/needed no reservation because their endpoints are client-/session-discovered, not protocol-fixed. The seam is for protocols that pin absolute paths, not API shims per se.Sketch
api.reservePath('/xrpc')/api.reservePath('/:user/did.json')(orpaths: [...]on the loader entry): the loader WAC-exempts and claims each deliberately, and reports collisions at boot. Plusapi.webfinger.addLink(...)(or a generic link registry) for the shared-document case.Cost
Small-medium — generalizes the existing
appPathsmechanism from operator config to plugin declaration.