Skip to content

fix(container-gateway-hook): start the gateway on demand before a container command - #1814

Merged
potiuk merged 3 commits into
apache:mainfrom
potiuk:fix/container-gateway-on-demand
Oct 25, 2026
Merged

potiuk merged 3 commits into
apache:mainfrom
potiuk:fix/container-gateway-on-demand

Conversation

@potiuk

@potiuk potiuk commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

Summary

  • The container gateway is started only by the SessionStart hook, but it can exit while the session still needs it:

    • after its idle timeout (4 h by default);
    • when another session on the same project ends, because that session's SessionEnd stops the one gateway both share;
    • when it or its backend crashes.

    From then on every sandboxed docker / podman command fails with dial unix …/docker.sock: connect: no such file or directory, until the next session. Seen in an adopter repo: Docker Desktop was up, the gateway was gone, and the log showed no start since the session began.

  • New container-gateway-hook.sh ensure action, wired as a PreToolUse hook on Bash with two if filters, Bash(docker *) and Bash(podman *). The if filter is best-effort, so the script checks the command itself too.

    • Any command not naming docker / podman (or the -compose variants) exits before any other work, in about 10 ms.
    • It serves only the directory start vetted for this session. start records it by session id under ~/.claude/magpie-gateway/sessions/, where the sandbox cannot write, and the hook refuses to run if any grant covers that directory. ensure never uses the payload's cwd, which follows the agent's cd: judged from another directory, the session project's own write grants would go uncounted, and a source or interpreter the sandbox can write could pass as trusted. With no record, it does nothing. stop drops the record, and records older than 30 days are pruned.
    • It then runs every check start runs (trusted interpreter and gateway source, writable roots, worktree records), so an on-demand start can run nothing that start would refuse.
    • It then asks the gateway's own status whether it is serving. If not, it starts it and waits up to 5 s for it to serve, so the triggering command does not race the start. serve --daemon returns before the sockets are bound.
    • It never blocks the command: every exit is 0, as before.
  • The SessionStart / SessionEnd pair is unchanged and still starts the gateway with the session.

  • Docs and RFC:

    • RFC-AI-0004 Principle 2 (layer 1b) now records that the gateway's lifetime is tied to use, and that it is always started from the operator-installed copy.
    • Updated to the new lifecycle and registration: the container-gateway spec, secure-agent-setup.md (registration example, migration jq), the tool README / tool.md, the agent-isolation README, and sandbox troubleshooting.
    • isolated-setup-install L.2 wires the new hook.
    • isolated-setup-verify 12a reports a missing ensure hook as ◐, since start/stop alone still work, and isolated-setup-update reports it as drift.
    • The doctor's "gateway socket missing" row now points at the ensure hook.
  • isolated_fingerprint.py and two measured_tokens stamps are regenerated by their hooks.

Type of change

  • Skill change (.claude/skills/<name>/) — eval fixtures updated below
  • Tool / bridge contract (tools/<system>/*.md)
  • Python package (tools/*/ with pyproject.toml)
  • Groovy reference impl
  • Cross-cutting (RFC, AGENTS.md, sandbox, privacy-LLM)
  • Documentation (docs/, README.md, CONTRIBUTING.md)
  • Project template (projects/_template/)
  • CI / dev loop (prek, workflows, validators)
  • Other:

Test plan

  • New tests in tools/agent-isolation/tests/test_container_gateway_hook.py:
    • ensure starts the gateway for docker / podman / compose commands, including after cd x &&, ;, a path prefix, a VAR= prefix, or inside ( … ).
    • It ignores other commands, including cat Dockerfile, vim docker.md and ./mydocker.
    • It ignores a malformed payload.
    • It refuses a sandbox-writable gateway source exactly like start does.
    • It passes vetted MAGPIE_CONTAINER_GATEWAY_ARGS.
    • After a cd elsewhere, it serves the session's recorded project and still counts that project's grants. Both tests fail against the first commit.
    • It does nothing without a session record, for another session's id, for a session id that is not a plain name (../x, /etc/passwd), or for a symlinked record.
    • start records the session, stop drops the record, and a refused start records nothing.
  • uv run pytest tests/test_container_gateway_hook.py in tools/agent-isolation: all 115 pass, outside the sandbox (the suite skips under the sandbox scratch dir).
  • Live on macOS with Docker Desktop:
    • stopped the gateway;
    • ensure with an ls payload: no-op, 0.01 s;
    • ensure with a docker run payload: gateway started and serving, 0.75 s;
    • ensure again: 0.54 s;
    • a sandboxed docker run --rm alpine:3 echo … then succeeded.
  • prek hooks pass on the changed files
  • prek run --all-files in CI

RFC-AI-0004 compliance

  • Sandbox — no new host access. The new action runs the same trust checks before executing anything, starts only the operator-installed gateway, and the sandbox still reaches only the gateway's own sockets.

Linked issues

Follows #1298 (container gateway) and #1797 (per-machine hook registration).

Notes for reviewers (optional)

  • The second commit closes a trust-boundary gap in the first, found by an automated security review. In the first, ensure took the project from the per-command cwd, so a sandboxed cd into another directory could make the session project's write grants go uncounted. ensure now serves only the directory start vetted for the session. A session that began before the hook was wired gets nothing from ensure until its next start.
  • Other harnesses still start the gateway by hand or from their wrapper; this only adds the Claude Code hook.
  • Existing installs keep working without the new hook. isolated-setup-update proposes it as drift.

🤖 Generated with Claude Code

@potiuk potiuk added family:setup setup-* skills capability:fix Implement code change to resolve an issue labels Oct 25, 2026
@github-actions github-actions Bot added family:tools tools/* family:docs Docs, MISSION.md, READMEs capability:reassess Re-run historical issues against current code capability:platform Framework / agent substrate skills (install, verify, doctor, override, status, setup bootstrap) substrate:sandbox Tool substrate: agent isolation, egress control, settings audit labels Oct 25, 2026
@potiuk
potiuk marked this pull request as draft October 25, 2026 14:52
@potiuk
potiuk marked this pull request as ready for review October 25, 2026 15:25
…tainer command

The gateway exits mid-session after its idle timeout, when another session on the same project ends, or on a crash, and only SessionStart ever started it again. A new ensure action, wired as a PreToolUse hook on Bash, starts it before a docker/podman command, after the same trust checks as start.

Generated-by: Claude Opus 5
main's prek has failed on this file since apache#1815.

Generated-by: Claude Code (Claude Opus 5)
…nt's cwd

The payload's cwd follows the agent's cd: before every tool call, and at
the SessionStart that follows a compaction, /clear or resume. Judged from
another directory, the session project's own write grants went uncounted,
so a sandboxed cd elsewhere could get a source or interpreter the sandbox
can write past the trust checks. Every action now takes the directory
from CLAUDE_PROJECT_DIR, which Claude Code sets on every hook to the
directory the session started in; it does not follow cd, and a settings
env entry cannot override it (checked against Claude Code). start and
stop fall back to the payload's cwd when it is unset; ensure does
nothing. The payload's cwd and its settings are counted as writable too.

Generated-by: Claude Code (Claude Opus 5)
@potiuk
potiuk force-pushed the fix/container-gateway-on-demand branch from 117039b to 64262c5 Compare October 25, 2026 16:09
@potiuk
potiuk enabled auto-merge (squash) October 25, 2026 16:13
@potiuk
potiuk merged commit 2257a02 into apache:main Oct 25, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

capability:fix Implement code change to resolve an issue capability:platform Framework / agent substrate skills (install, verify, doctor, override, status, setup bootstrap) capability:reassess Re-run historical issues against current code family:docs Docs, MISSION.md, READMEs family:setup setup-* skills family:tools tools/* substrate:sandbox Tool substrate: agent isolation, egress control, settings audit

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant