Skip to content

Users vs Sessions

defangdevs edited this page Aug 11, 2026 · 1 revision

Users vs sessions

An agent-box host has Linux users, and each user has one or more sessions. This page says which one to add when. Issue #59 decided the architecture; the decision rule stayed implicit in its comments and kept getting re-derived, so #127 asked for it in writing.

The model

  • Linux user = trust boundary. Separate credentials and secrets (tokenDir, the ~/.config/agent-box/env file, the settings daemon), separate blast radius, separate systemd hardening, separate auth path. A user is created with Nix plus a rebuild — deliberately expensive, because adding a trust domain should be a declared act.
  • Session = unit of concurrent work. One agent, one job. Created at runtime through sessions.json, the agent-box-session CLI or the web UI. No sudo, cheap, destroyable, and spawnable by another agent.

The decision rule

Ask one question about the new agent: would it be a problem if it could read the other agents' tokens, memory and files?

  • No → a new session under an existing user. Shared memory, shared clones and a shared CLAUDE.md are a feature here, not a leak.
  • Yes → a new user. Use this for a different human owner, a different credential set, or a different risk posture — for example a skip-permissions agent that must not reach another agent's secrets (the threat model behind #49).

A session cannot be a boundary

The rule above is not only a preference. A session is unable to hold a boundary, so the "yes" branch has no cheaper answer than a new user. Measured on the maintainer's box on 2026-08-11, from inside a live session (uid 1001, five sessions on one user):

  1. A sibling session's environment is readable. /proc/<pid>/environ of another session returned GH_TOKEN, AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. kernel.yama.ptrace_scope is 1 on that box, but Yama gates only PTRACE_MODE_ATTACH; /proc/<pid>/environ uses PTRACE_MODE_READ, so Yama does not apply. The same uid is sufficient. hidepid= does not help either — it hides other users' processes, not same-uid ones.
  2. The tmux server is shared. One server owns every session of the user, so tmux -L agent-box list-sessions and send-keys from one session drive a sibling's pane. That is full control of the other agent, without reading any environment.

Closing both would need a PID namespace with its own /proc and a tmux server per session — a container per session. That breaks the supervisor, find_supervisor_pids in modules/src/settings-daemon.py (it scans the host /proc for same-uid pids), and the session manager.

So per-session isolation is off the table by construction. Any future feature that implies a per-session security boundary is a design error.

Per-session features are scoping, not isolation

Per-session environment, per-session working directory and per-session secrets are convenience and scoping layers. They must say so, and they must never advertise isolation.

Worked example: #135 (per-session env, persisted and RAM-only). The RAM-only half buys a shorter secret lifetime — the keys stay out of EBS snapshots, the AMI, the qcow2 and backups, and they die on reboot or Spot interruption. It buys no confidentiality from a sibling session, because of the two facts above.

Consequences

  • Most boxes want few users and many sessions. claude and codex as separate users was a pre-#59 workaround for per-CLI units, not a trust decision.
  • Cross-user sharing (opening HOMEs, chmod gymnastics) is an anti-pattern signal. Agents that routinely need each other's state belong in the same user. Genuine cross-domain sharing calls for a setgid shared directory, not readable HOMEs.
  • Workspace trampling inside one user is solved at the session layer (#126, one working directory per session), not by splitting users.

"1 user = 1 project"

This is a common special case of the rule, and it is often the right one. It follows when the project carries its own credential set — its own GitHub token, its own cloud keys. Then the project boundary and the credential boundary coincide, and the Nix-plus-rebuild cost buys a real trust domain.

It does not follow when several projects share one credential set and one human owner. You then pay a rebuild per project and gain nothing, and you re-create the cross-user sharing anti-pattern above: the agents keep needing each other's clones and memory, which is the signal that they belong in one user. Split by credentials and owner, not by directory.

Related

  • #59 — sessions as first-class; the architecture this rule describes.
  • #61 — cross-session conversation access; the mechanics one layer below this page.
  • #49 — the settings-daemon threat model behind the "different risk posture" case.