# Container coding agents For developers running a coding agent under AC's **Container** runtime: what AC does with your host credentials at launch, what works today, and what does not. > **Status: in progress.** Host login reuse works and is on by default: a container Claude Code session starts signed in with zero interaction. Container agents run with their work repos mounted, and the injected repo context targets container paths (`/repos/...`). Read [Repos in the container](#repos-in-the-container) and [Known limitations](#known-limitations) before you move an agent to the Container runtime. ## Why this exists Before host login reuse, you authenticated every container coding agent by hand: run `claude setup-token`, paste the output into a `CLAUDE_CODE_OAUTH_TOKEN` env row, repeat per setup. You were already logged in on the host, and the container could not use that login. Now you log in once on the host and containers reuse it. ## What AC does at launch When Claude Code runs under the Container runtime and `containerCredentialsFromHost` is on (the default), AC: 1. **Copies the host credential file** for that coding agent into the container's config dir inside the bind mount. Claude Code: host `~/.claude/.credentials.json` → `/.claude/.credentials.json`, which the container reads as `/workspace/.claude/.credentials.json`. 2. **Sets `CLAUDE_CONFIG_DIR`** to that dir, so the CLI reads the copied token. AC injects this only when you have not set `CLAUDE_CONFIG_DIR` yourself. Your own value always wins. 3. **Stamps the container's first-run state** in the container's `.claude.json`, next to the copy: `hasCompletedOnboarding: true`, plus `hasTrustDialogAccepted` and `hasCompletedProjectOnboarding` for the `/workspace` project. Claude Code gates its onboarding wizard on these flags and checks neither against the credential, so without them a valid copied token still lands on "Select login method". 4. **Deletes the copied credential** when the session stops, on every teardown path. AC never reads or modifies your host config: `~/.claude.json` is not touched. Only the credential file is copied out of `~/.claude/`. Verified end to end: a cold replica reaches an authenticated Claude session with no human interaction, launching plain `claude`. If AC finds no host credential, or if the destination directory or file is a symlink or junction, it skips the copy and stamps **nothing**. With no token, the login wizard is the correct behavior. ### AC answers the folder-trust prompt for you First-run stamping means AC pre-accepts Claude Code's **"do you trust this folder?"** dialog for the container's `/workspace`. That is a safety prompt, and AC answers it on your behalf so the agent does not stall at launch. The folder is the agent's replica root, which AC already bind-mounts read-write. This is deliberate, it is stated in the Settings hint, and it is stated here. ## Terminal snapshots from a container Orchestrator An automatically bound live container Orchestrator receives the distinct `terminal-snapshot` API scope in addition to its ordinary bridge scopes. It can read one verified non-Orchestrator member in the same exact physical project and room. A worker token never receives the scope, and a manual API client remains unauthorized even if its registry entry lists the string. The **Settings > General > Terminal snapshots** gate is on by default, so this works without any setup; confirm it has not been turned off. The screen can contain credentials, source, prompts, and personal data, and AgentsCommander does not redact it. Use the helper with the automatically injected environment: ```bash agentscommander-api-helper terminal-snapshot \ --to "project:room-1-team/member" ``` For PNG: ```bash agentscommander-api-helper terminal-snapshot \ --to "project:room-1-team/member" \ --format png \ --output "/workspace/evidence/snapshot.png" \ --timeout 15 ``` The helper reads only `AGENTSCOMMANDER_API_URL` and `AGENTSCOMMANDER_API_TOKEN` for authority. Do not pass `--token` or `--root`. It sends one non-idempotent `POST /api/v1/terminal-snapshot`, bypasses ambient proxies, disables redirect and retry behavior, requests identity encoding, and uses one absolute deadline. JSON is the default; PNG requires an absolute new `.png` path inside the container filesystem. A successful JSON read writes one compact ASCII-only document plus LF. PNG writes a fully validated file and prints metadata only, never bytes or base64. The API caps the request and error body at 8 KiB, success transport at 24 MiB, and decoded JSON or PNG content at 16 MiB. Every route-produced response has `Content-Type: application/json; charset=utf-8`, `Cache-Control: no-store`, and `Pragma: no-cache`. Root snapshot authority is deliberately host-only. Root must use `agentscommander terminal-snapshot` with a live Root session token and never receives an API identity. This read does not depend on frontend visibility or access to the target's repository. For repo access, see [Repos in the container](#repos-in-the-container) below. See [Terminal snapshots](terminal-snapshots.md) for authorization, schema, fidelity, renderer, privacy, errors, and cleanup. ## Repos in the container Container agents mount their [work repos](../glossary.md#work-repo) read-write inside the container. The mount root is always `/repos`, and each repo lands at `/repos/`, where `` is the repo directory's own name, for example `/repos/repo-AgentsCommander`. Each agent mounts only the repos enabled for it, the `repos[]` entries in its own replica `config.json`. Two agents in the same room can see different repos. The injected `# Agent Repos` context shows the container path, not the host path. A local (non-container) session still shows host paths. An entry is mounted only if it resolves to a directory whose name starts with `repo-`, sitting directly under the room root, and not to a reserved location such as another agent's replica or `messaging/`. Two things can go wrong, and they behave differently: | The `repos[]` entry | What happens | |---|---| | Points at a directory that is not there | The session starts normally. The repo still appears in the injected `# Agent Repos` list with its `/repos/` path, marked `(NOT FOUND)`. Nothing is mounted at that path, so any command against it fails inside the container. Restore or re-clone the directory on the host. | | Resolves to something that exists but is not an admissible `repo-*` directory | The session does not start at all. AC refuses the spawn with an error that begins `container repo mount refused:` and names the entry, the path it resolved to, and which check failed. No container is created. Correct `repos[]` in that agent's `config.json`, then start the session again. | ## Known limitations All of the following are current. Host login reuse does not fix any of them. ### 1. One-time "Bypass Permissions mode" consent With `--dangerously-skip-permissions`, a brand-new replica still shows Claude Code's one-time bypass-mode acceptance screen. A human must accept it **once per replica, ever**. This is Anthropic's consent gate, not an AC bug, and AC deliberately does **not** auto-accept it. Host login reuse removes the login wizard, not this. ### 2. Credential reuse is Claude Code only Codex, Antigravity, and Pi have no credential descriptor. AC copies no credentials and stamps no provider-specific first-run state for them. Their container sessions authenticate with credentials you supply yourself. For Pi, AC also does not copy or map host `~/.pi/agent/` state, translate `PI_CODING_AGENT_SESSION_DIR`, or provision a `--session-dir` path. Pi 0.80.10 accepts only the separated spelling `--session-dir `; it rejects `--session-dir=`. AC preserves the user-authored spelling and path during any eligible `--continue` insertion but does not make the path container-aware. Any custom session directory and its durable state must already be meaningful inside the container. ### 3. Deferred hardening ([#933](https://github.com/mblua/AgentsCommander/issues/933)) | Gap | Detail | |---|---| | **No owner-only ACL on Windows** | On Unix AC sets `0o600` on the copied credential. On Windows it sets no ACL, so the copy inherits the project tree's ACL, which for a user-chosen repo path can be broader than `~/.claude` (shared drives, `Everyone:R`). | | **Crash residue** | Teardown deletes the copy, and the next launch of the same agent overwrites it. There is no boot-time sweep. An unclean host crash (SIGKILL, power loss) leaves the copied credential on disk until that replica's next same-agent launch. A replica you never relaunch keeps a live refresh token on disk indefinitely. | ### 4. No shared team container ([#936](https://github.com/mblua/AgentsCommander/issues/936), paused) One container per session today, each mounting its own agent's replica plus that agent's enabled work repos. One shared container for the whole room is a recorded requirement, not a shipped feature, and its feasibility analysis is paused. ## What the copied file actually is A **full-account credential in plaintext**: a short-lived access token plus a **long-lived refresh token**, account-scoped. It sits in the project tree, inside a read-write bind mount, for the lifetime of the session. See [Security model → Container coding agents](../security.md#container-coding-agents-copied-host-credentials) for the exposure this adds and how AC limits it. Host and containers share **one login**. The copy is a snapshot, not a live mount. If the provider rotates the refresh token when it is used, whichever party refreshes first can invalidate the others: a container refresh can force a re-login on the host, or break a sibling container. This is the accepted tradeoff of copying in, which avoids a token-refresh write race between the host and every container. ## Turning it off **Settings → General → Container Coding Agents → "Reuse host login for container coding agents"**, or in `settings.30.instance.no-git.json`: ```json { "containerCredentialsFromHost": false } ``` With it off, AC copies nothing, injects no `CLAUDE_CONFIG_DIR`, and stamps no first-run state. You supply credentials yourself, for example a `CLAUDE_CODE_OAUTH_TOKEN` env row on the agent: see the [ac-claude image README](../../docker/ac-claude/README.md). ## Troubleshooting Every step logs under the `[container-cred]` prefix. Set `logLevel` to `info` (the default) and read the log. | What you see | What it means | |---|---| | `[container-cred] no host credential at ; container will start without host login` | You are not logged in on the host, or your credential lives elsewhere. Log in on the host, or point `CLAUDE_CONFIG_DIR` (an **absolute** path; a relative value is ignored) at the config dir that holds `.credentials.json`. | | `[container-cred] copied host credential into ` | The copy succeeded. | | `[container-cred] dest dir is a symlink/reparse point; skipping copy-in` | AC refuses to write a token through a symlink or junction it did not create. Nothing was copied and nothing was stamped; the agent shows its login wizard. Remove the link. | | `[container-cred] is not valid JSON (...); skipping first-run state` | The container's `.claude.json` is unparseable, so AC left it byte for byte rather than clobber it. The token is in place, but you get the onboarding wizard. | | The agent is signed in, but a repo under `/repos` is listed as `(NOT FOUND)` | That `repos[]` entry has no directory on the host. The session runs and the repo is not mounted. See [Repos in the container](#repos-in-the-container). | | The session refuses to start with `container repo mount refused: ...` | A `repos[]` entry resolves to something that is not an admissible `repo-*` directory under the room root. The message names the entry, its resolved path, and the failed check. See [Repos in the container](#repos-in-the-container). | ## See also - [Settings reference → Container coding agents](../reference/settings.md#container-coding-agents): the `containerCredentialsFromHost` field - [Security model](../security.md#container-coding-agents-copied-host-credentials): threat model for the copied credential - [Coding agents](../integrations/coding-agents.md): which CLIs AC drives and how it finds them - [Terminal snapshots](terminal-snapshots.md): the separate authorized backend-viewport read capability - [ac-claude image README](../../docker/ac-claude/README.md): building the container image and supplying credentials by hand