feat(bottle): opt-in gVisor runtime per bottle
test / run tests/run_tests.py (push) Successful in 19s

Bottles can now set "runtime": "runsc" to launch the agent container
under gVisor instead of runc, adding a userspace syscall barrier
between the agent and the host kernel. Default is runc (Docker
default). Pipelock stays on the default runtime per the research doc's
minimum-diff prescription.

The launcher verifies runsc is registered with the daemon before
launch, surfaces the runtime in the preflight plan, and dies with an
install pointer (and a macOS-not-supported note) when runsc is
requested but unavailable.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-10 00:48:11 -04:00
parent 3eff1e0b6e
commit e3f5a5907a
7 changed files with 122 additions and 5 deletions
+8
View File
@@ -21,6 +21,7 @@ from ..env_resolve import env_resolve
from ..log import die, info
from ..manifest import (
manifest_agent_bottle,
manifest_bottle_runtime,
manifest_env_names,
manifest_prompt,
manifest_require_agent,
@@ -101,6 +102,10 @@ def cmd_start(argv: list[str]) -> int:
)
manifest_require_bottle(manifest, bottle_name)
runtime = manifest_bottle_runtime(manifest, bottle_name)
if runtime == "runsc":
docker_mod.require_runsc()
ssh_entries = manifest_ssh(manifest, name)
if ssh_entries:
ssh_mod.ssh_validate_entries(ssh_entries)
@@ -166,6 +171,7 @@ def cmd_start(argv: list[str]) -> int:
)
info("skills : " + (" ".join(skill_names) if skill_names else "(none)"))
info(f"bottle : {bottle_name}")
info(f" runtime : {runtime}{' (gVisor)' if runtime == 'runsc' else ''}")
if ssh_entries:
ssh_names = ", ".join(e.get("Host", "") for e in ssh_entries)
info(f" ssh hosts : {ssh_names}")
@@ -216,6 +222,8 @@ def cmd_start(argv: list[str]) -> int:
"-e", f"HTTP_PROXY={proxy_url}",
"-e", "NO_PROXY=localhost,127.0.0.1",
]
if runtime != "runc":
docker_args.extend(["--runtime", runtime])
if env_file.stat().st_size > 0:
docker_args.extend(["--env-file", str(env_file)])
+16
View File
@@ -19,6 +19,22 @@ def require_docker() -> None:
die("docker not found")
def require_runsc() -> None:
"""Fail with an install pointer if the `runsc` (gVisor) runtime is
not registered with the local Docker daemon. Called when a bottle
sets `runtime: "runsc"`."""
result = subprocess.run(
["docker", "info", "--format", "{{json .Runtimes}}"],
capture_output=True,
text=True,
)
if result.returncode != 0 or "runsc" not in result.stdout:
info("This bottle requested runtime 'runsc' but the gVisor runtime is not registered with Docker.")
info("Install gVisor and register it with the daemon: https://gvisor.dev/docs/user_guide/install/")
info("On macOS, gVisor is not available natively; remove 'runtime' from the bottle or run on Linux.")
die("runsc runtime not available")
def image_exists(ref: str) -> bool:
return _silent_run(["docker", "image", "inspect", ref]) == 0
+24
View File
@@ -154,6 +154,30 @@ def manifest_bottle_ssh(manifest: Manifest, bottle_name: str) -> list[dict[str,
return list(bottle.get("ssh") or [])
_SUPPORTED_RUNTIMES: tuple[str, ...] = ("runc", "runsc")
def manifest_bottle_runtime(manifest: Manifest, bottle_name: str) -> str:
"""Container runtime for the bottle's agent container. Returns
"runc" (Docker default) or "runsc" (gVisor opt-in). Dies if the
field is present but not one of the supported values."""
bottle = (manifest.get("bottles") or {}).get(bottle_name) or {}
raw = bottle.get("runtime")
if raw is None:
return "runc"
if not isinstance(raw, str):
die(
f"bottle '{bottle_name}' runtime must be a string "
f"(was {_json_type(raw)})."
)
if raw not in _SUPPORTED_RUNTIMES:
die(
f"bottle '{bottle_name}' runtime '{raw}' is not supported. "
f"Use one of: {', '.join(_SUPPORTED_RUNTIMES)}."
)
return raw
def manifest_bottle_egress_allowlist(manifest: Manifest, bottle_name: str) -> list[str]:
"""Hostnames in bottles[bottle_name].egress.allowlist. Dies if the
field is present but not an array. Per-element string typing is