pastilhasandClaude Opus 5 8e9d53b2d2 capabilities: both doors now read the same declaration
replaces the account backstop. it was two hand-written lists — NON_OWNER_PATHS
confining every non-owner to /api/auth + /api/music, and NON_OWNER_WS_PROVIDERS
doing the same for sockets. they were not wrong, they were unscalable in one
specific way: an allow-list answers "which paths" but never "why", so onboarding
anyone who needed anything other than music meant editing an array in a
middleware file and hoping the socket half got edited too.

now both doors resolve against the registry, so they cannot disagree about what
a role holds. terminal, chat, task-runner, pipeline and desktop are refused by
being `execution` capabilities rather than by being absent from a list somebody
maintains.

fail-closed everywhere: an unknown capability key, a missing row, a database
error or a deleted user all deny. the grant cache is keyed on role and has an
explicit invalidation contract — unlike the one super-admin.ts refuses to have,
this one has exactly one writer and it lives beside the reader.

seeded Member → music at WRITE, which is precisely what the old path-based
backstop allowed. granting `read` would have been a silent downgrade that broke
playlists for the three live member accounts overnight.

verified against the live database and real accounts: 27 http/socket cases, the
read/write split (personal sub-paths writable at read, /music/scan not), cache
invalidation after a revoke, and the borrowed test account's role restored.
20 new unit tests; full suite 362 pass 0 fail.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 00:55:12 +00:00
2026-02-16 19:34:35 +00:00
2026-02-16 19:34:35 +00:00
2026-02-16 19:34:35 +00:00
2026-02-22 03:35:26 +00:00
2026-02-16 19:34:35 +00:00
2026-02-16 19:34:35 +00:00
2026-02-16 19:34:35 +00:00
S
Description
No description provided
42 MiB
Languages
TypeScript 90.9%
Shell 4.7%
JavaScript 4.1%
CSS 0.2%
HTML 0.1%