a first-party plugin may claim a root path the platform reserved
caldav is not only /api/caldav. four of its surfaces are top-level and above the account gate on purpose, because DAVx5, iOS and Thunderbird send HTTP Basic on every request and have nowhere to put a JWT: /dav, the ios profile under it, and the two .well-known discovery paths a client probes before it has any credential. the mechanism for that already existed — unauthenticatedRootPaths, exclusive claims, first come wins. what stopped caldav using it was RESERVED_ROOT, and what that list was recording is worth being exact about: `dav` and `.well-known` are in it because the PLATFORM serves them today, not because they are dangerous in themselves. once caldav is a plugin, the thing the reservation protects IS the plugin, and keeping it would mean the platform permanently owning routes for a feature it no longer contains. so a reserved name is claimable by a first-party plugin and refused to everyone else. the owner drew that line today: fine for a plugin we wrote, and letting a stranger's plugin claim /dav is a question that needs thinking about properly rather than falling out of a flag. /api stays refused to us too — everything of officer's lives under it and protocolHeaders is the only sanctioned way in. .well-known needed two segments, which the single-segment rule refuses. allowed by NAME rather than by pattern, and the reason is in the same namespace: acme-challenge is how this machine gets its TLS certificates, so a plugin owning it could take the domain. caldav and carddav are on the list; nothing else is, for us as much as for anyone. the OPTIONS carve-out moved with it. hono's cors() answers every OPTIONS as a preflight and never calls the route beneath — which to a DAV client is not a preflight but the question "what can you do", answered by a header iOS refuses the account without. that was a literal list of four DAV paths; it asks the claim registry now, so it follows whoever owns the route rather than naming it here. verified: server boots (totality passes), /.well-known/caldav still 301s and /dav/ still asks for basic auth. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
import { mountPrefix, publicPathProblem, rootPathProblem, type DiscoveredPlugin } from './manifest';
|
||||
import {
|
||||
mountPrefix,
|
||||
publicPathProblem,
|
||||
rootPathProblem,
|
||||
FIRST_PARTY_PUBLISHER,
|
||||
type DiscoveredPlugin,
|
||||
} from './manifest';
|
||||
|
||||
// Which plugin paths are served ABOVE the account gate.
|
||||
//
|
||||
@@ -111,8 +117,12 @@ export function resolvePluginRootPaths(
|
||||
): { segments: string[]; refused: string[] } {
|
||||
const segments: string[] = [];
|
||||
const refused: string[] = [];
|
||||
// Provenance decides whether a RESERVED name may be claimed. A first-party plugin can take one back
|
||||
// from the platform — which is what caldav owning `/dav` is — and a third-party plugin cannot.
|
||||
const firstParty = plugin.manifest.publisher === FIRST_PARTY_PUBLISHER;
|
||||
|
||||
for (const path of plugin.manifest.unauthenticatedRootPaths ?? []) {
|
||||
const problem = rootPathProblem(path);
|
||||
const problem = rootPathProblem(path, firstParty);
|
||||
if (problem) {
|
||||
refused.push(`${plugin.appName}: ${problem}`);
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user