Run per-session sidecars on a per-session network#13
Merged
Conversation
A container that must join more than one network has to be created, connected to the extra networks, then started -- docker run only attaches one network at creation. Add a pure argv builder that swaps just the run subcommand for create, preserving every other token, so the multi-network launch path can reuse the anvil argv the Makefile already built.
Session tongs live on a per-session network so concurrent anvils cannot reach each other's sidecars by container name. Add the DockerCLI methods that path needs: ensure_network (inspect-or-create, reusing a leftover from a crashed session), network_connect (attach a shared tong under its canonical alias), and best-effort network_disconnect/network_rm for teardown. An anvil that joins both its per-session network and the pre-existing NETWORK= network cannot be started with a single docker run, so add run_foreground_multi: create the anvil on its primary network, connect the extra networks, then start it attached. Teardown owns removing the created container, so a failed connect/start does not orphan it.
A `session` tong is per-session: when one is discovered the launcher creates a network named for the anvil's --name handle, starts the session tongs on it under their canonical aliases, connects each network-facing shared tong to it for this session, and joins the anvil to it plus the pre-existing NETWORK= network (via create -> connect -> start, since docker run attaches only one network). Readiness is probed on the network the anvil will use, so a shared tong is checked at the alias the anvil dials. The per-session connect of a shared tong is made idempotent with a best-effort disconnect first: a hard-killed prior session can leave its network behind with the shared tong still attached, and ensure_network reuses that network, so a stale endpoint must not fail the connect. On exit -- including SIGINT -- the session tongs and the per-session network are torn down and the connected shared tongs disconnected, while the long-lived shared tongs keep running. Removing the session tongs and the anvil before the network avoids docker's refusal to delete a network with live endpoints. The secret, MCP, and volume guards stay: only the bare session-lifecycle refusal is lifted, so a session tong that needs secret delivery or MCP config is still refused rather than started half-wired.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Teaches the host launcher (
scripts/run_anvil.py) to start per-session sidecarcontainers, each on a docker network scoped to a single harness session. Until
now the launcher started only long-lived (
shared) sidecars on the harness'sexisting network; this adds the per-session lifecycle and the network isolation
it needs.
What this does
When a discovered sidecar declares a per-session lifecycle, the launcher now:
--name, soconcurrent harness sessions cannot reach each other's sidecars by container
name;
--network-alias, so the name the agent dials is identical across worktrees(never the session-suffixed container name);
sharedsidecar to the session network for theduration of the session, and disconnects -- but does not stop -- it on exit;
NETWORK=network.
Because
docker runattaches only one network at creation, a harness that joinsboth networks is created, connected to the extra network, then started attached
(
docker create->docker network connect->docker start --attach). A newpure
tongs.to_create_argvrewrites only therunsubcommand tocreate,preserving every other token, so the create path reuses the exact argv the
Makefile built.
On exit -- including Ctrl-C -- the per-session sidecars and the per-session
network are torn down (and the connected
sharedsidecars disconnected) whilethe long-lived
sharedsidecars keep running. The per-session connect is madeidempotent with a best-effort disconnect first, so a network left behind by a
hard-killed prior session does not wedge the next launch.
Structure
The per-session network calls (create / connect / disconnect / remove) and the
create -> connect -> start harness run go through the existing
DockerCLIseam,so the whole sequence is unit-tested against an in-process fake: network
create/teardown, per-session sidecar start under its alias, shared-sidecar
connect/disconnect, readiness probing on the session network, Ctrl-C teardown,
the missing-
--nameguard, and the byte-identical passthrough exec.