retire the single-user claim from the docs it outlived

CLAUDE.md asserted "single-user is a hard invariant, not a stage" while
users held six rows and role_capabilities held grants. Every doc that
repeated it is corrected here, in prose and in the code comments that
carried the same claim.

The accurate statement is narrower: one owner who bypasses every check,
other accounts holding only what their role is granted, and a set of
capabilities — terminal, chat, files, tasks, items, desktop, browser — that
are structurally ungrantable because they execute as the owner's OS user.

TODO.md gains a Multi-user section for what the read turned up: no way to
create a second account, dashboards.id colliding across users, authorize.ts
untested, pty/vault/opencode taking no identity, Radicale still owner_only.

claude-sidecar-isolation.md's open question is answered rather than left
open — the per-email spawn model is dead weight, because chat is an
execution capability and no second account can ever reach it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 21:58:44 +00:00
co-authored by Claude Opus 5
parent ac64a7362b
commit d56be0301d
13 changed files with 227 additions and 75 deletions
+11 -6
View File
@@ -284,12 +284,17 @@ is a real design decision and I don't have a confident recommendation.
## Open questions — the ones I'd rather you answered
1. **Is the per-email spawn model dead weight?** `CLAUDE.md` states single-user is a hard invariant
("If a change seems to need 'which user is this', the answer is always the owner"), yet the agent
sidecar is keyed per email — `claude:${email}`, a `claudeProcs` Map, a `claudeSpawnWaiters` Map, a
per-email PID lock. If there is only ever one owner, Stage 1a becomes trivial: one PM2 entry, no
fan-out, no registration polling. If you intend multi-tenant later, the fan-out has to stay and
Stage 1 gets harder. **This single answer changes the shape of the whole plan.**
1. ~~**Is the per-email spawn model dead weight?**~~**answered 2026-08-07: yes, it is.** The
question was whether multi-tenancy might later need the per-email fan-out (`claude:${email}`, the
`claudeProcs` and `claudeSpawnWaiters` Maps, the per-email PID lock). The capability model settled
it in the *other* direction from what "the platform is going multi-user" would suggest: `chat` is
`kind: 'execution'` in `capabilities/registry.ts`, which is **never grantable at any level**,
because the agent runs as the owner's OS user with `--dangerously-skip-permissions`. Additional
accounts exist now, and not one of them can ever open a chat.
So the fan-out is keyed on a dimension that is structurally guaranteed to have one value. Stage 1a
is the trivial version: one PM2 entry, no fan-out, no registration polling. This only reopens if
per-user home confinement is ever built, which is a project rather than a checkbox.
2. **Relay or redirect?** Officer proxies the agent WebSocket (one origin, keeps your HTTPS reverse
proxy and JWT model intact, but a restart still drops the socket for a moment), or officer hands