correct two claims about agents that the SDK disproves
docs/per-user-linux-accounts.md carried the reasoning that per-user agents were a large piece of work, and both halves of that reasoning were wrong. The SDK does have somewhere to put a uid — spawnClaudeCodeProcess, documented for running Claude Code in VMs and containers — so a member's turn does not have to become its own process. And the credential claim was backwards: the proxy holds the OWNER'S credential, reading the owner's own ~/.claude/.credentials.json, so pointing a member at it spends the owner's account on the member's turns. The previous handoff had already retracted that one; the doc had not caught up, which is how a retracted claim stays live. Corrected in place rather than deleted, with what was believed and why it was wrong, because the superseded version is the interesting part: the first claim is what made agents look like a later stage than they are. Adds the constraint that actually is out of scope, which the old text never stated: no platform process ever runs as a member, because the sidecar holds POSTGRES_URL and the JWT secret. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -38,11 +38,15 @@ them. Installed SDK is **0.2.59**; `package.json:31` pins `^0.2.41`.
|
||||
the SDK also needs to reach the spawned process in a way `setpriv` breaks, this whole approach is wrong and
|
||||
the fallback is the earlier plan (member turn as its own process).
|
||||
|
||||
Second stale claim, already corrected once: `docs/per-user-linux-accounts.md:230` still says a member's
|
||||
`claude` "needs only `ANTHROPIC_BASE_URL` pointed at the proxy and no key of its own." The previous handoff
|
||||
§5 retracted that. **Both passages need editing; neither has been edited.** Left alone deliberately so this
|
||||
document is the record of what was believed when the code was written, rather than the doc silently agreeing
|
||||
with the code.
|
||||
Second stale claim, already retracted once by the previous handoff §5:
|
||||
`docs/per-user-linux-accounts.md:230` said a member's `claude` "needs only `ANTHROPIC_BASE_URL` pointed at the
|
||||
proxy and no key of its own." That is backwards — the proxy holds the **owner's** credential
|
||||
(`sidecar/claude/proxy.ts:7` reads the owner's own `~/.claude/.credentials.json`), so pointing a member at it
|
||||
spends the owner's account on their turns.
|
||||
|
||||
**Both passages have now been corrected in `docs/per-user-linux-accounts.md`** (same commit range as the code).
|
||||
The superseded text is in that file's git history. If you are reading this handoff against an older checkout,
|
||||
the doc is the one that moved.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -223,12 +223,28 @@ crossed the ancestor that mattered.
|
||||
- **This is not a sandbox.** A member with a shell is on the machine. They cannot read the owner's files
|
||||
or another member's, and `sudo` is not theirs — but they can run code, see process names, and reach the
|
||||
network. It isolates members from each other and from accidents, not from the host.
|
||||
- **Agents come later.** `claude-manager.ts` drives turns through `query()` from
|
||||
`@anthropic-ai/claude-agent-sdk`, which spawns `claude` itself and takes `env`/`cwd` but has nowhere to
|
||||
put a uid. Dropping privileges has to happen *outside* the SDK, which makes a member's turn its own
|
||||
process — a change of shape rather than a flag. The credential is not the problem:
|
||||
`officer-anthropic-proxy` already holds it, so a member's `claude` needs only `ANTHROPIC_BASE_URL`
|
||||
pointed at the proxy and no key of its own.
|
||||
- **Agents come later** — but by less than this said. Two claims here were wrong and are corrected on
|
||||
2026-08-11; the superseded text is in the git history of this file, and the working state is
|
||||
`COMMS/sidecar-app-store/2026-08-11-per-user-claude-handoff.md`.
|
||||
|
||||
It said the SDK "has nowhere to put a uid", so dropping privileges had to happen *outside* it, making a
|
||||
member's turn its own process — "a change of shape rather than a flag". It is a flag: `sdk.d.ts:951`
|
||||
exposes `spawnClaudeCodeProcess`, documented for running Claude Code "in VMs, containers, or remote
|
||||
environments", and `node:child_process.spawn` already satisfies the `SpawnedProcess` shape it wants. So the
|
||||
existing sidecar wraps the CLI spawn in `runAsArgv` per turn and there is no second process to stand up.
|
||||
|
||||
It also said the credential is not the problem because `officer-anthropic-proxy` already holds it, so a
|
||||
member's `claude` "needs only `ANTHROPIC_BASE_URL` pointed at the proxy". That is backwards. The proxy holds
|
||||
the **owner's** credential (`sidecar/claude/proxy.ts:7` reads the owner's own
|
||||
`~/.claude/.credentials.json`), so pointing a member at it spends the owner's account on the member's
|
||||
turns. Per-user Claude means their own login in their own home, and `setpriv --reset-env` is what makes that
|
||||
the default rather than something to remember: nothing crosses into their process unless it is written into
|
||||
the argv.
|
||||
|
||||
What does remain out of scope: **no platform process ever runs as a member.** The agent sidecar needs
|
||||
`POSTGRES_URL` and the JWT signing secret, so a member-uid process holding them could read every account and
|
||||
sign a token as the owner — more than their shell can do, and already refused by `assertSecretsClosed`. The
|
||||
harness stays the service user's; only `claude` itself drops privileges.
|
||||
- **`pty`, `vault` and `opencode` receive no identity at all** (`TODO.md` → Multi-user). pty keys purely
|
||||
on a `sessionId` from the query string, and its `/_officer/sessions` endpoints list and kill *every*
|
||||
session on the box. Safe today only because terminal is owner-only. **The moment a member has a shell
|
||||
|
||||
Reference in New Issue
Block a user