Found while implementing the /auth segment-boundary repair for #16026. Out of that card's scope (it narrows exactly one prefix); filed unassigned.
The mechanism
DomainRoute.match defaults to 'prefix' — DomainHandlerRegistry.matches' default: branch is a bare path.startsWith(route.prefix) with no segment boundary. The registry preserved that on purpose when the domains were lifted out of the legacy if-chain, and says so in its own header comment:
Matching semantics are deliberately faithful to the legacy if-chain, INCLUDING its rough edges (match: 'prefix' on /i18n also matches /i18nxx, exactly as startsWith did) — fixing those edges is explicitly not this seam's job; behavior preservation is.
That was the right call for the migration. The migration is now well past that point, and the tree is visibly half-converted.
Enumerated on origin/main at 8647c87b2f5
Declare match: 'segment' and are boundary-correct — 5 routes:
packages/runtime/src/domains/security.ts /security
packages/runtime/src/domains/share-links.ts /share-links
packages/runtime/src/domains/keys.ts /keys
packages/runtime/src/domains/mcp.ts /mcp
packages/runtime/src/domains/mcp.ts /mcp/skill
Still carry the implicit 'prefix' default — 10 routes:
packages/runtime/src/domains/actions.ts /actions
packages/runtime/src/domains/ai.ts /ai
packages/runtime/src/domains/analytics.ts /analytics
packages/runtime/src/domains/automation.ts /automation
packages/runtime/src/domains/data.ts /data
packages/runtime/src/domains/i18n.ts /i18n
packages/runtime/src/domains/meta.ts /meta
packages/runtime/src/domains/notifications.ts /notifications
packages/runtime/src/domains/packages.ts /packages
packages/runtime/src/domains/ui.ts /ui
So /datax, /metaxyz, /uifoo, /aixx, /packagesomething are each claimed by a domain that does not own them, exactly as /authx and /authentication/foo were claimed by /auth.
What IS measured here, and what is not
MEASURED: the enumeration above (read from source), and the mechanism (the registry's default: branch). The /auth member of this family was measured end to end on a real boot for #16026 and is fixed.
NOT MEASURED here, deliberately, and it is the reason this is its own card rather than a rider:
- The wire consequence per domain.
/auth's was 200 {} because the auth domain forwards a Fetch Response. The other ten each hand their sub-path to a different handler with a different parse, so what /datax actually answers has to be measured per domain, not assumed to match. One of them may already be harmless; another may be worse.
- Whether any of the ten is load-bearing. A caller may already depend on a lexical extension being claimed — the sibling-namespace claim is a bug, but it is a shipped bug on a shipped HTTP surface, and each removal narrows what that surface answers.
Why not one guard for the whole class
The obvious repair is to flip DomainHandlerRegistry's default from 'prefix' to 'segment', which closes all ten at once. That is attractive and may well be the answer, but it changes ten shipped route claims in a single unmeasured step, which is precisely what the per-domain measurement above exists to prevent. Whoever takes this should decide between the one-line default flip plus ten measurements, and ten explicit match: 'segment' declarations plus ten measurements — the measurements are owed either way.
Context: #16026 (the /auth member, fixed) and its repair, which added match: 'segment' to createAuthDomain and pinned both the narrowed rows and the overshoot controls.
Found while implementing the
/authsegment-boundary repair for #16026. Out of that card's scope (it narrows exactly one prefix); filed unassigned.The mechanism
DomainRoute.matchdefaults to'prefix'—DomainHandlerRegistry.matches'default:branch is a barepath.startsWith(route.prefix)with no segment boundary. The registry preserved that on purpose when the domains were lifted out of the legacy if-chain, and says so in its own header comment:That was the right call for the migration. The migration is now well past that point, and the tree is visibly half-converted.
Enumerated on
origin/mainat8647c87b2f5Declare
match: 'segment'and are boundary-correct — 5 routes:Still carry the implicit
'prefix'default — 10 routes:So
/datax,/metaxyz,/uifoo,/aixx,/packagesomethingare each claimed by a domain that does not own them, exactly as/authxand/authentication/foowere claimed by/auth.What IS measured here, and what is not
MEASURED: the enumeration above (read from source), and the mechanism (the registry's
default:branch). The/authmember of this family was measured end to end on a real boot for #16026 and is fixed.NOT MEASURED here, deliberately, and it is the reason this is its own card rather than a rider:
/auth's was200 {}because the auth domain forwards a FetchResponse. The other ten each hand their sub-path to a different handler with a different parse, so what/dataxactually answers has to be measured per domain, not assumed to match. One of them may already be harmless; another may be worse.Why not one guard for the whole class
The obvious repair is to flip
DomainHandlerRegistry's default from'prefix'to'segment', which closes all ten at once. That is attractive and may well be the answer, but it changes ten shipped route claims in a single unmeasured step, which is precisely what the per-domain measurement above exists to prevent. Whoever takes this should decide between the one-line default flip plus ten measurements, and ten explicitmatch: 'segment'declarations plus ten measurements — the measurements are owed either way.Context: #16026 (the
/authmember, fixed) and its repair, which addedmatch: 'segment'tocreateAuthDomainand pinned both the narrowed rows and the overshoot controls.