claude-code: write the agent's home as its own account where it has one
On the control node agents are to run as an account that cannot become root (hq ADR 0266). The runtime keeps running as the operator, so the token, the identity and the home-scope items go to the agent's home through a helper started as that account; the licence follows the move, what was placed in the operator's home goes, and the guard refuses a session running as anyone else, recording to the journal. Unchanged where no agent account is named.
This commit is contained in:
@@ -142,11 +142,42 @@ Remove these by hand, once; the mesh removes nothing it did not make (ADR 0182):
|
||||
- `~/.claude/skills/cleanup/`, `~/.claude/skills/hal-switch-license/`
|
||||
- the hand-made console entry in `~/.claude.json` under `mcpServers` — it is ignored now anyway
|
||||
|
||||
## Where the agent runs as an account of its own (hq ADR 0266)
|
||||
|
||||
A machine may name an **agent account** beside the operator's, at the controller's terminal
|
||||
(`mesh-controller node agent-account <node> <account>`); the control node does. Agents there run as that
|
||||
account, which cannot become root without a person, and the operator keeps their own account and its sudo.
|
||||
The runtime hands this module `MESH_AGENT_ACCOUNT` and `MESH_AGENT_HOME` — the operator's where none is
|
||||
named, and read as the operator's from a controller that sets neither — and everything it writes for the
|
||||
agent goes to that account's home:
|
||||
|
||||
- the licence token (`~/.claude/.credentials.json`) and the account it is logged in as (`~/.claude.json`);
|
||||
- an API-key licence and its key-helper, in `~/.claude/mesh/`, since the account cannot enter the
|
||||
module's state;
|
||||
- the items registered at the home scope, recorded per home (`home-placed-<account>.json`).
|
||||
|
||||
The runtime's account cannot enter that home (0700, the agent's), so a path under it is touched by a
|
||||
**helper running as the agent's account**: this module's binary as the plugin carries it
|
||||
(`/etc/claude-code/marketplace/plugins/nox-mesh/guard/guard home-fs`, root's), started once through
|
||||
`sudo -n -u <account>` and kept, one JSON request and answer per line. The escalation goes down only: the
|
||||
runtime's account may become the agent's, never the other way.
|
||||
|
||||
When a machine moves its agent to such an account, the licence follows at once — a binding counts as
|
||||
applied only while the agent's account holds it — and what this module placed under the operator's home
|
||||
goes: the access-token-only credentials file (one holding a refresh token is a login the person made, and
|
||||
stays, said), the home-scope items unless changed by hand, the key kept in the state.
|
||||
|
||||
**The guard there refuses every command and file edit of a session that does not run as the agent's
|
||||
account**, whatever it is, and no override lifts it: the operator's account may become root, and an
|
||||
agent started in it by habit must do nothing. Its record goes to the machine's journal
|
||||
(`journalctl -t mesh-claude-code-guard`), which the session's account may add to and not change;
|
||||
`claude_code_guard` reads it there.
|
||||
|
||||
## Escalation
|
||||
|
||||
Writing `/etc/claude-code` needs root. The runtime runs as the operator account, and the module uses
|
||||
that account's passwordless `sudo`; on a machine without it, `claude_code_render` says so and nothing
|
||||
is written.
|
||||
is written. The same sudo starts the helper as the agent's account, where there is one.
|
||||
|
||||
## Code
|
||||
|
||||
|
||||
Reference in New Issue
Block a user