refactor(de-sidecar): remove the per-bottle companion-container architecture

The per-agent companion container (the egress/git-gate/supervise data
plane run once per bottle) is the pre-consolidation architecture. Remove
it and disable the backends that still depend on it, per the #385 thread.

- Delete `backend/docker/sidecar_bundle.py`; docker's live path uses the
  consolidated shared gateway, not a per-bottle bundle.
- Disable the firecracker and macos-container backends: their `launch()`
  fails closed (they launched a per-bottle companion; firecracker's
  consolidated relaunch is #354, macos follows). Their `enumerate` return
  empty and `cleanup` drop the companion-container discovery (firecracker
  keeps VMM/run-dir cleanup).
- Fail-close both backends' `egress_apply` reload (it signalled the
  per-bottle container); consolidated egress policy resolves per-request
  against the orchestrator, so gateway-side apply is a follow-up.
- Rename `egress_sidecar_env_entries` → `egress_gateway_env_entries`,
  `SIDECAR_PORTS` → `GATEWAY_PORTS`.
- Move the shared DockerBottlePlan fixture to `tests/unit/_docker_bottle_plan.py`;
  delete tests for the removed launch paths; update cleanup/egress-apply tests.

Docker consolidated launch verified end-to-end (multitenant isolation
integration test passes). macos/firecracker are intentionally disabled.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBMWTEtQdJ4W5UrWuLHCck
This commit is contained in:
2026-07-14 17:02:08 -04:00
parent c10d1cb6e0
commit 77948ef56c
22 changed files with 259 additions and 1950 deletions
+13 -44
View File
@@ -1,60 +1,29 @@
"""Host-side helper for egress sidecar inspection and live updates.
"""Host-side egress route-apply for the docker backend.
The approve path uses this module to validate a proposed routes file,
write it to the bottle's live egress state dir, and signal the sidecar
bundle so the mitmproxy addon reloads it.
The per-bottle companion container this used to signal (`docker kill
--signal HUP <container>`) was removed in the de-sidecar cleanup (#385).
In the consolidated model the shared gateway resolves egress policy
per-request against the orchestrator rather than reloading a per-bottle
routes file, so the live per-bottle reload is not supported here and
fails closed until the gateway-side apply lands.
"""
from __future__ import annotations
import os
import subprocess
from ...egress import EGRESS_ROUTES_IN_CONTAINER
from ...log import warn
from ..egress_apply import EgressApplicator, EgressApplyError
from .sidecar_bundle import sidecar_bundle_container_name
def fetch_current_routes(slug: str) -> str:
container = sidecar_bundle_container_name(slug)
r = subprocess.run(
["docker", "exec", container, "cat", EGRESS_ROUTES_IN_CONTAINER],
capture_output=True, text=True, check=False,
)
if r.returncode != 0:
raise EgressApplyError(
f"could not read routes.yaml from {container}: "
f"{(r.stderr or '').strip() or 'container not running?'}"
)
return r.stdout
class DockerEgressApplicator(EgressApplicator):
def _signal_bundle_reload(self, slug: str) -> None:
container = sidecar_bundle_container_name(slug)
result = subprocess.run(
["docker", "kill", "--signal", "HUP", container],
capture_output=True, text=True, check=False, env=os.environ,
del slug
raise EgressApplyError(
"live egress route-apply was removed with the per-bottle "
"companion container (#385); route changes will flow through "
"the consolidated gateway in a follow-up."
)
if result.returncode != 0:
last_error = (result.stderr or "").strip() or (result.stdout or "").strip()
warn(
f"egress: routes updated on disk for {slug}, but bundle reload failed: "
f"{last_error or 'docker kill failed'}"
)
raise EgressApplyError(
f"could not reload egress bundle {container}: "
f"{last_error or 'docker kill failed'}"
)
applicator = DockerEgressApplicator()
__all__ = [
"DockerEgressApplicator",
"EgressApplyError",
"applicator",
"fetch_current_routes",
]
__all__ = ["DockerEgressApplicator", "EgressApplyError", "applicator"]
@@ -1,33 +0,0 @@
"""Sidecar bundle constants + helpers for the Docker backend
(PRD 0024).
A per-bottle sidecar bundle runs egress + git-gate + supervise as one
container under a small Python init supervisor. As of PRD 0024 chunk 5
the bundle is the only shape — the legacy four-sidecar topology and its
`BOT_BOTTLE_SIDECAR_BUNDLE` feature flag are gone.
The bundle's image is the **same data plane** as the consolidated
per-host gateway (PRD 0070), so it is one image with one name and one
Dockerfile: `bot-bottle-gateway` / `Dockerfile.gateway` (#384). These
constants alias the gateway's so a per-bottle bundle and the shared
gateway can never drift onto different images."""
from __future__ import annotations
from ...orchestrator.gateway import GATEWAY_DOCKERFILE, GATEWAY_IMAGE
# The per-bottle bundle image == the gateway data-plane image (aliased so
# there is one source of truth; the `BOT_BOTTLE_GATEWAY_IMAGE` env override
# on the gateway constant applies here too).
SIDECAR_BUNDLE_IMAGE = GATEWAY_IMAGE
SIDECAR_BUNDLE_DOCKERFILE = GATEWAY_DOCKERFILE
def sidecar_bundle_container_name(slug: str) -> str:
"""`bot-bottle-sidecars-<slug>`. Same prefix scheme as the
per-sidecar containers it replaces, so the dashboard's
discovery-by-prefix logic keeps working. (The per-bottle *container*
name keeps the historical `sidecars-` prefix; only the *image* was
renamed to `bot-bottle-gateway` in #384.)"""
return f"bot-bottle-sidecars-{slug}"