Per-run instance HOME with credential copy/wipe #202

Closed
opened 2026-07-22 11:43:40 +02:00 by dominik.polakovics · 2 comments

What to build

Give every run a private HOME at <state-dir>/instances/<run>/home, still spawning on the host (HOME= in the session env). This is the isolation seam the container runner will mount later, de-risked without podman:

  • At spawn: create the instance HOME, copy the provider's model credentials from the master store (the service HOME today) into it, and run trust seeding (~/.claude.json, onboarding flags) against the instance HOME instead of the shared one.
  • At stop/rollback: wipe the instance HOME (credential copy shredded); GC orphaned instance dirs on boot like runtime creds are GC'd today.
  • Providers resolve transcript/session paths per-run from the instance HOME (claude ~/.claude/projects/..., codex ~/.codex/sessions/...), and the chat tailer + deep-link capture read those per-run paths. One credentialInjector seam per provider so a server-side proxy can replace the copy later.

Acceptance criteria

  • New runs get a private HOME; ~/.claude* / ~/.codex of the master store are no longer read or written by instance processes
  • Chat tailing, dialog-capture spool, and deep-link capture work unchanged against per-run paths (claude and codex)
  • Credential copy is wiped on stop and on launch rollback; boot-time sweep removes orphans
  • Trust seeding writes only into the instance HOME
  • Existing chat/tailer tests pass; new tests cover per-run path resolution and wipe-on-stop

Blocked by

None - can start immediately

## What to build Give every run a private HOME at `<state-dir>/instances/<run>/home`, still spawning on the host (HOME= in the session env). This is the isolation seam the container runner will mount later, de-risked without podman: - At spawn: create the instance HOME, copy the provider's model credentials from the master store (the service HOME today) into it, and run trust seeding (`~/.claude.json`, onboarding flags) against the instance HOME instead of the shared one. - At stop/rollback: wipe the instance HOME (credential copy shredded); GC orphaned instance dirs on boot like runtime creds are GC'd today. - Providers resolve transcript/session paths per-run from the instance HOME (claude `~/.claude/projects/...`, codex `~/.codex/sessions/...`), and the chat tailer + deep-link capture read those per-run paths. One credentialInjector seam per provider so a server-side proxy can replace the copy later. ## Acceptance criteria - [ ] New runs get a private HOME; `~/.claude*` / `~/.codex` of the master store are no longer read or written by instance processes - [ ] Chat tailing, dialog-capture spool, and deep-link capture work unchanged against per-run paths (claude and codex) - [ ] Credential copy is wiped on stop and on launch rollback; boot-time sweep removes orphans - [ ] Trust seeding writes only into the instance HOME - [ ] Existing chat/tailer tests pass; new tests cover per-run path resolution and wipe-on-stop ## Blocked by None - can start immediately
Author
Owner

This was generated by AI during triage.

Agent Brief

Category: enhancement
Summary: Give every run a private HOME under <state-dir>/instances/<run>/home with provider-credential copy at spawn and wipe at stop, still spawning on the host.

The issue body's "What to build" and acceptance-criteria checklist are the authoritative spec (grilled 2026-07-22). This brief adds the interface map and scope fences.

Current behavior:
Instance processes share the service user's HOME. Provider trust seeding (the claude provider's SeedTrust, which writes the per-directory folder-trust entry and the machine-global hasCompletedOnboarding flag into ~/.claude.json) targets that shared HOME. Providers resolve transcript/session paths under the shared ~/.claude/projects/... and ~/.codex/sessions/..., and the chat tailer and deep-link capture read those shared paths. Provider model credentials live in a master store in the service HOME. Per-run git credentials already have the lifecycle this issue mirrors: the vault materializes per-(cred, op) files under the runtime dir, cleans up per op, and sweeps orphans on boot.

Desired behavior:
Per the issue body: instance HOME created at spawn with the provider's model credentials copied in; trust seeding targets the instance HOME; transcript/session paths resolve per-run for both providers and the tailer + dialog/deep-link capture follow them; wipe on stop and on launch rollback; boot-time sweep for orphaned instance dirs; HOME set in the session env.

Key interfaces:

  • spawnEnv / the session env — carries the per-run HOME (and anything derived from it).
  • Provider trust seeding (SeedTrust and the codex equivalent) — must accept a home root instead of assuming the process HOME.
  • Provider transcript/session path resolution consumed by the chat tailer and deep-link capture — parameterized by the run's HOME.
  • A new per-provider credentialInjector seam: given the master store and an instance HOME, install working credentials — exactly one call site per provider so a server-side proxy can replace the copy later.
  • Wipe/GC lifecycle — mirror the vault's per-op cleanup plus boot sweep pattern for instance dirs.

Acceptance criteria: the checklist in the issue body.

Out of scope:

  • podman/containers (#205) and the unix socket (#201) — this is host-mode only.
  • Changing the master credential store's format or location.
  • Login flows (#206) — logins keep writing to the master store.
> *This was generated by AI during triage.* ## Agent Brief **Category:** enhancement **Summary:** Give every run a private HOME under `<state-dir>/instances/<run>/home` with provider-credential copy at spawn and wipe at stop, still spawning on the host. The issue body's "What to build" and acceptance-criteria checklist are the authoritative spec (grilled 2026-07-22). This brief adds the interface map and scope fences. **Current behavior:** Instance processes share the service user's HOME. Provider trust seeding (the claude provider's `SeedTrust`, which writes the per-directory folder-trust entry and the machine-global `hasCompletedOnboarding` flag into `~/.claude.json`) targets that shared HOME. Providers resolve transcript/session paths under the shared `~/.claude/projects/...` and `~/.codex/sessions/...`, and the chat tailer and deep-link capture read those shared paths. Provider model credentials live in a master store in the service HOME. Per-run git credentials already have the lifecycle this issue mirrors: the vault materializes per-`(cred, op)` files under the runtime dir, cleans up per op, and sweeps orphans on boot. **Desired behavior:** Per the issue body: instance HOME created at spawn with the provider's model credentials copied in; trust seeding targets the instance HOME; transcript/session paths resolve per-run for both providers and the tailer + dialog/deep-link capture follow them; wipe on stop and on launch rollback; boot-time sweep for orphaned instance dirs; `HOME` set in the session env. **Key interfaces:** - `spawnEnv` / the session env — carries the per-run HOME (and anything derived from it). - Provider trust seeding (`SeedTrust` and the codex equivalent) — must accept a home root instead of assuming the process HOME. - Provider transcript/session path resolution consumed by the chat tailer and deep-link capture — parameterized by the run's HOME. - A new per-provider credentialInjector seam: given the master store and an instance HOME, install working credentials — exactly one call site per provider so a server-side proxy can replace the copy later. - Wipe/GC lifecycle — mirror the vault's per-op cleanup plus boot sweep pattern for instance dirs. **Acceptance criteria:** the checklist in the issue body. **Out of scope:** - podman/containers (#205) and the unix socket (#201) — this is host-mode only. - Changing the master credential store's format or location. - Login flows (#206) — logins keep writing to the master store.
Author
Owner

The autoland validator's CONCERN on PR #209 (claude credential-isolation asymmetry vs codex) is addressed in 24ab1b0 on afk/202 — with one deviation from the suggested fix.

Concern confirmed, and slightly worse than stated: live probe of claude 2.1.214 shows CLAUDE_CONFIG_DIR outranks HOME for all state — credentials, .claude.json, and projects/ (transcripts) — so an inherited value would leak the master store and break per-run transcript locate. Latent today: nothing in-repo sets it and the deployed server env (inherited by this very session through the tmux path in question) doesn't either.

Suggested one-liner rejected: pinning CLAUDE_CONFIG_DIR=<home>/.claude would relocate the CLI's .claude.json to <home>/.claude/.claude.json (live-probed), away from the <home>/.claude.json the trust seed and oauthAccount mirror write — regressing PR #209's live-verified onboarding/trust suppression.

Shipped instead: the injector now unconditionally returns CLAUDE_CONFIG_DIR= (empty). The CLI treats empty as unset and stays on HOME resolution (live-probed), so the defense is codex-symmetric without relocating anything. Both load-bearing CLI facts are pinned in compat.md §3a + opt-in live test TestCompat_Live_configDirResolution; tmux delivery of the empty-valued -e entry probe-verified. Build, full test suite, and golangci-lint 2.12.2 green (tmux integration tests fail in this sandbox identically on the untouched tree — CI is their home).

The autoland validator's CONCERN on PR #209 (claude credential-isolation asymmetry vs codex) is addressed in 24ab1b0 on afk/202 — with one deviation from the suggested fix. **Concern confirmed, and slightly worse than stated**: live probe of claude 2.1.214 shows CLAUDE_CONFIG_DIR outranks HOME for *all* state — credentials, `.claude.json`, and `projects/` (transcripts) — so an inherited value would leak the master store *and* break per-run transcript locate. Latent today: nothing in-repo sets it and the deployed server env (inherited by this very session through the tmux path in question) doesn't either. **Suggested one-liner rejected**: pinning `CLAUDE_CONFIG_DIR=<home>/.claude` would relocate the CLI's `.claude.json` to `<home>/.claude/.claude.json` (live-probed), away from the `<home>/.claude.json` the trust seed and oauthAccount mirror write — regressing PR #209's live-verified onboarding/trust suppression. **Shipped instead**: the injector now unconditionally returns `CLAUDE_CONFIG_DIR=` (empty). The CLI treats empty as unset and stays on HOME resolution (live-probed), so the defense is codex-symmetric without relocating anything. Both load-bearing CLI facts are pinned in compat.md §3a + opt-in live test `TestCompat_Live_configDirResolution`; tmux delivery of the empty-valued `-e` entry probe-verified. Build, full test suite, and golangci-lint 2.12.2 green (tmux integration tests fail in this sandbox identically on the untouched tree — CI is their home).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/coding-lab#202
No description provided.