diff --git a/src/servers/api/users/capabilities-routes.ts b/src/servers/api/users/capabilities-routes.ts index b850bb71..250d06ec 100644 --- a/src/servers/api/users/capabilities-routes.ts +++ b/src/servers/api/users/capabilities-routes.ts @@ -34,11 +34,17 @@ selfCapabilitiesRouter.get('/capabilities', async (ctx) => { ? CAPABILITIES.map((c) => ({ key: c.key, level: 'write' as const })) : [...grants].map(([key, level]) => ({ key, level })); + const heldKeys = new Set(held.map((h) => h.key)); + return ctx.json({ isOwner, capabilities: held, // Flattened for the dock and the route guard, which care about paths rather than capability keys. routes: held.flatMap(({ key }) => CAPABILITY_BY_KEY.get(key)?.routes ?? []), + // The complement, and the frontend genuinely needs both. "Not in `routes`" cannot distinguish a route + // this account lacks from a route no capability claims at all — `/`, the settings shell, the sign-in + // screens — and a guard that cannot tell those apart either blanks the app or guards nothing. + deniedRoutes: CAPABILITIES.filter((c) => !heldKeys.has(c.key)).flatMap((c) => c.routes ?? []), }); });