Containerized runner: rootless podman spawn tracer bullet #205

Closed
opened 2026-07-22 11:44:15 +02:00 by dominik.polakovics · 1 comment

What to build

First end-to-end containerized run. Per the 2026-07-22 container-isolation design: rootless podman + crun, tmux stays host-side, the pane command becomes podman run -it (replacing the prlimit wrapper).

  • repos.runner setting, container | host, end to end: migration, agent-visible config, repo-settings UI. Host mode is break-glass and labeled "unsandboxed — full host access" in the UI. Container is the intended default once stable; ship with host default behind readiness of preflight.
  • Spawn argv: podman run -it --rm with mount inventory — worktree rw and bare repo rw at their host-identical paths, agent.sock (from #201), instance HOME (from #202) at /home/agent, per-run runtime dir (materialized git creds, dialog spool, --settings file), tools image via --mount type=image (from #203) with PATH prepend. --userns=keep-id so the server can tail files. pasta netns: full egress, no host route.
  • Env: only explicit -e vars (spawnEnv set), nothing inherited.
  • Limits: --memory=8g --pids-limit=4096 --ulimit nofile=16384:16384 as global config defaults with per-repo override; prlimit wrapper retired for container runs.
  • Startup preflight for container mode: podman >= 4, subuid/subgid for the service user, cgroup-v2 delegation, tools image resolvable — refuse container spawns with actionable errors otherwise.
  • Pre-push hook must find labctl on the injected PATH inside the container while still working for host-mode runs (hookMarker migration convention).
  • Liveness/stop/kill semantics preserved: container exits with the pane, survives server restart (KillMode=process), stop tears down the container.

Integration test must skip gracefully where rootless podman is unavailable (lab dev sandbox); unit-test the argv/mount construction and preflight thoroughly.

Acceptance criteria

  • A container-mode run completes a full loop: spawn -> chat visible via tailer -> labctl issue/pr over the socket -> git commit + push (hook scan fires) -> stop cleans up container and instance dir
  • Inside the container: other repos' worktrees/bares, the state dir, master.key, and host loopback (Postgres, web UI) are unreachable; agent.sock and internet egress work
  • repos.runner UI shows the unsandboxed warning on host mode; setting round-trips schema->API->UI
  • Preflight refuses container mode with actionable errors when podman/subuid/cgroup-delegation/image are missing
  • Limits applied and overridable per repo; env inside container contains only the explicit set
  • Host-mode runs still work unchanged (dual-runner seam tested)

Blocked by

## What to build First end-to-end containerized run. Per the 2026-07-22 container-isolation design: rootless podman + crun, tmux stays host-side, the pane command becomes `podman run -it` (replacing the prlimit wrapper). - `repos.runner` setting, `container` | `host`, end to end: migration, agent-visible config, repo-settings UI. Host mode is break-glass and labeled "unsandboxed — full host access" in the UI. Container is the intended default once stable; ship with host default behind readiness of preflight. - Spawn argv: `podman run -it --rm` with mount inventory — worktree rw and bare repo rw at their host-identical paths, `agent.sock` (from #201), instance HOME (from #202) at `/home/agent`, per-run runtime dir (materialized git creds, dialog spool, --settings file), tools image via `--mount type=image` (from #203) with PATH prepend. `--userns=keep-id` so the server can tail files. pasta netns: full egress, no host route. - Env: only explicit `-e` vars (spawnEnv set), nothing inherited. - Limits: `--memory=8g --pids-limit=4096 --ulimit nofile=16384:16384` as global config defaults with per-repo override; prlimit wrapper retired for container runs. - Startup preflight for container mode: podman >= 4, subuid/subgid for the service user, cgroup-v2 delegation, tools image resolvable — refuse container spawns with actionable errors otherwise. - Pre-push hook must find labctl on the injected PATH inside the container while still working for host-mode runs (hookMarker migration convention). - Liveness/stop/kill semantics preserved: container exits with the pane, survives server restart (KillMode=process), stop tears down the container. Integration test must skip gracefully where rootless podman is unavailable (lab dev sandbox); unit-test the argv/mount construction and preflight thoroughly. ## Acceptance criteria - [ ] A container-mode run completes a full loop: spawn -> chat visible via tailer -> labctl issue/pr over the socket -> git commit + push (hook scan fires) -> stop cleans up container and instance dir - [ ] Inside the container: other repos' worktrees/bares, the state dir, master.key, and host loopback (Postgres, web UI) are unreachable; agent.sock and internet egress work - [ ] repos.runner UI shows the unsandboxed warning on host mode; setting round-trips schema->API->UI - [ ] Preflight refuses container mode with actionable errors when podman/subuid/cgroup-delegation/image are missing - [ ] Limits applied and overridable per repo; env inside container contains only the explicit set - [ ] Host-mode runs still work unchanged (dual-runner seam tested) ## Blocked by - #201 - #202 - #203
Author
Owner

This was generated by AI during triage.

Agent Brief

Category: enhancement
Summary: First end-to-end containerized run: rootless podman + crun, tmux stays host-side, the pane command becomes podman run -it --rm behind a repos.runner (container|host) setting.

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, verification notes, and scope fences.

Current behavior:
There is no container code in the repo. Panes run the provider CLI directly on the host, wrapped by the tmux wrapper's prlimit nofile cap. The launch path assembles spawnEnv, materializes per-run git credentials into the runtime dir, and the tailer reads transcripts from provider paths (per-run HOME once #202 lands).

Desired behavior:
Per the issue body: repos.runner end to end with host mode labeled "unsandboxed — full host access"; the container argv with the full mount inventory (worktree + bare at host-identical paths, agent.sock from #201, instance HOME from #202 at /home/agent, per-run runtime dir, tools image from #203 via --mount type=image with PATH prepend); --userns=keep-id; pasta netns with egress but no host route; explicit -e env only; memory/pids/nofile limits as global defaults with per-repo override; startup preflight refusing container spawns with actionable errors; pre-push hook finding labctl on the injected PATH; liveness/stop/kill semantics preserved (container dies with the pane, survives server restart, stop tears it down).

Key interfaces:

  • The launch path's pane-command construction — introduce a runner seam where host yields today's prlimit-wrapped command and container yields the podman argv; the prlimit wrapper is retired for container runs only.
  • repos.runner — follow the established per-repo settings slice: paired sqlite/postgres migrations, the repo PATCH settings-update surface, and a repo-settings UI section with the unsandboxed warning.
  • Preflight: podman >= 4, subuid/subgid for the service user, cgroup-v2 delegation, tools image resolvable — checked at startup and surfaced on container spawn attempts.
  • The pre-push guard hook (marker-guarded script requiring labctl on PATH) — must resolve labctl both host-side and inside the container; a hook-content change follows the established marker migration convention.
  • Global limits config with per-repo override for --memory / --pids-limit / --ulimit nofile.

Verification notes:
The lab agent sandbox has no rootless podman: the integration test must detect that and skip gracefully, while argv/mount construction and preflight logic are unit-tested exhaustively. The full-loop acceptance criterion (spawn → chat → labctl over socket → push with hook scan → stop cleanup) and the in-container reachability assertions will be validated by the maintainer on the dev host — say so in the PR rather than claiming e2e verification.

Acceptance criteria: the checklist in the issue body.

Out of scope:

  • Containerized login (#206).
  • Per-repo dev-image selection (#207) — the tracer runs on a fixed/globally configured image.
  • Flipping the default to container — ship with host default until preflight is proven on the dev host.

Blocked by

(recorded in the issue body: #201, #202, #203)

> *This was generated by AI during triage.* ## Agent Brief **Category:** enhancement **Summary:** First end-to-end containerized run: rootless podman + crun, tmux stays host-side, the pane command becomes `podman run -it --rm` behind a `repos.runner` (`container`|`host`) setting. 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, verification notes, and scope fences. **Current behavior:** There is no container code in the repo. Panes run the provider CLI directly on the host, wrapped by the tmux wrapper's prlimit nofile cap. The launch path assembles `spawnEnv`, materializes per-run git credentials into the runtime dir, and the tailer reads transcripts from provider paths (per-run HOME once #202 lands). **Desired behavior:** Per the issue body: `repos.runner` end to end with host mode labeled "unsandboxed — full host access"; the container argv with the full mount inventory (worktree + bare at host-identical paths, agent.sock from #201, instance HOME from #202 at /home/agent, per-run runtime dir, tools image from #203 via `--mount type=image` with PATH prepend); `--userns=keep-id`; pasta netns with egress but no host route; explicit `-e` env only; memory/pids/nofile limits as global defaults with per-repo override; startup preflight refusing container spawns with actionable errors; pre-push hook finding labctl on the injected PATH; liveness/stop/kill semantics preserved (container dies with the pane, survives server restart, stop tears it down). **Key interfaces:** - The launch path's pane-command construction — introduce a runner seam where `host` yields today's prlimit-wrapped command and `container` yields the podman argv; the prlimit wrapper is retired for container runs only. - `repos.runner` — follow the established per-repo settings slice: paired sqlite/postgres migrations, the repo PATCH settings-update surface, and a repo-settings UI section with the unsandboxed warning. - Preflight: podman >= 4, subuid/subgid for the service user, cgroup-v2 delegation, tools image resolvable — checked at startup and surfaced on container spawn attempts. - The pre-push guard hook (marker-guarded script requiring labctl on PATH) — must resolve labctl both host-side and inside the container; a hook-content change follows the established marker migration convention. - Global limits config with per-repo override for `--memory` / `--pids-limit` / `--ulimit nofile`. **Verification notes:** The lab agent sandbox has no rootless podman: the integration test must detect that and skip gracefully, while argv/mount construction and preflight logic are unit-tested exhaustively. The full-loop acceptance criterion (spawn → chat → labctl over socket → push with hook scan → stop cleanup) and the in-container reachability assertions will be validated by the maintainer on the dev host — say so in the PR rather than claiming e2e verification. **Acceptance criteria:** the checklist in the issue body. **Out of scope:** - Containerized login (#206). - Per-repo dev-image selection (#207) — the tracer runs on a fixed/globally configured image. - Flipping the default to `container` — ship with `host` default until preflight is proven on the dev host. ## Blocked by (recorded in the issue body: #201, #202, #203)
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#205
No description provided.