--- id: multi-user/assertpermissiontotality-checks-the-wrong title: '`assertPermissionTotality` checks the wrong list, and `registry.test.ts` has been red since 2026-08-13' theme: multi-user status: done created: 2026-08-13 date_source: stated order: 12 --- **`assertPermissionTotality` checks the wrong list, and `registry.test.ts` has been red since 2026-08-13.** It is fed `Object.keys(handlers)` from `server.tsx`, but Bun serves the _route table_. Those diverged when the cliamp/desktop/vault plugins were switched off: `/api/cliamp/ws` and `/api/cliamp/audio/ws` are still live routes with their handlers and registry claims commented out. Not exploitable — `isWsProviderAllowed` finds no permission and 403s a member; the owner upgrades onto a dead socket. But the boot check that exists to stop exactly this cannot see it. Two fixes: point totality at the route table, and either delete the dead routes or restore their claims. The 8 failing tests in `registry.test.ts` are the same drift — `REAL_WS` still lists all nine providers as served, which is why nobody noticed. Found 2026-08-14. --- **Fixed 2026-08-22.** Half of this item was already stale and half was still exactly true, which is worth separating because the stale half is what would have made someone close it. Stale: the eight red tests in `registry.test.ts` are green, and have been since cliamp was deleted on 2026-08-15 — that removed the live instance of the drift along with the routes. Still true: the two lists were still two lists. `assertPermissionTotality` read `Object.keys(handlers)`, the map of implementations, while Bun served a hand-written route table twenty lines away. The comment beside the check claimed neither could be a stale copy of the other, and that was simply false. The fix is one declaration. `WS_ROUTES` maps path → provider, the Bun route table is GENERATED from it, and the boot check reads it — so a socket route cannot exist without being checked. Two smaller guards came with it: a route naming a provider with no handler now refuses to boot with the path in the message, and the provider union that was inline in `upgradeWs` is named (`UpgradableProvider`) so the route table typechecks against what that function can actually serve. `sidecar` and `dev-proxy` are listed explicitly as served-but-not-in-WS_ROUTES, because they upgrade through their own handlers. Named rather than omitted, so their absence cannot read as an oversight. Verified by running the check against the real prefix lists: the served set passes, and adding one unclaimed provider is refused with `websocket provider 'cliamp-audio' is served but no permission claims it`.