Package pinned native Claude and prove isolated startup
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0726e-5232-73f2-aaca-2c05ceb62efb
This commit is contained in:
tegwick 2026-09-06 23:51:50 +02:00
parent cfc79d51af
commit 174dba17b6
7 changed files with 299 additions and 8 deletions

View file

@ -7,7 +7,9 @@ Run with the sand-boxer Python environment; uv must be available on PATH.
from __future__ import annotations
import argparse
import hashlib
import json
import re
import subprocess
from pathlib import Path
@ -21,7 +23,31 @@ def checked(command: list[str]) -> str:
return result.stdout.strip()
def build(output: Path, rein_source: Path, llm_source: Path) -> dict:
def install_claude(output: Path, source: Path, sha256: str, version: str) -> dict:
"""Copy only an explicitly pinned native executable, never an interactive HOME."""
if not re.fullmatch(r"[0-9a-f]{64}", sha256) or not version.strip():
raise ValueError("Claude requires an exact SHA-256 and expected version")
if source.is_symlink() or not source.is_file():
raise ValueError("Claude source must be a regular file, not a symlink")
content = source.read_bytes()
if hashlib.sha256(content).hexdigest() != sha256:
raise ValueError("Claude executable digest mismatch")
if not content.startswith(b"\x7fELF"):
raise ValueError("Claude executable must be native ELF, not a host wrapper")
destination = output / "bin/claude"
with destination.open("xb") as stream:
stream.write(content)
destination.chmod(0o755)
return {"path": f"{RUNTIME_MOUNT}/bin/claude", "sha256": sha256,
"expected_version": version, "source": str(source)}
def build(output: Path, rein_source: Path, llm_source: Path,
claude_binary: Path | None = None, claude_sha256: str | None = None,
claude_version: str | None = None) -> dict:
supplied = (claude_binary, claude_sha256, claude_version)
if any(x is not None for x in supplied) and not all(x is not None for x in supplied):
raise ValueError("Claude binary, SHA-256 and expected version must be supplied together")
revisions = {}
for name, source in (("rein-aharness", rein_source), ("llm-connect", llm_source)):
if checked(["git", "-C", str(source), "status", "--porcelain"]):
@ -46,6 +72,8 @@ def build(output: Path, rein_source: Path, llm_source: Path) -> dict:
"print(json.dumps({'python':platform.python_version(),'packages':"
"{d.metadata['Name']:d.version for d in importlib.metadata.distributions()}}))",
]))
if claude_binary is not None:
metadata["claude"] = install_claude(output, claude_binary, claude_sha256, claude_version)
metadata["source_revisions"] = revisions
(output / "build-info.json").write_text(json.dumps(metadata, indent=2) + "\n")
return {"runtime": {"path": str(output), "sha256": runtime_digest(output)},
@ -57,8 +85,12 @@ def main() -> int:
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--rein-source", type=Path, required=True)
parser.add_argument("--llm-source", type=Path, required=True)
parser.add_argument("--claude-binary", type=Path)
parser.add_argument("--claude-sha256")
parser.add_argument("--claude-version")
args = parser.parse_args()
result = build(args.output.resolve(), args.rein_source.resolve(), args.llm_source.resolve())
result = build(args.output.resolve(), args.rein_source.resolve(), args.llm_source.resolve(),
args.claude_binary, args.claude_sha256, args.claude_version)
print(json.dumps(result, indent=2))
return 0

View file

@ -38,6 +38,17 @@ assert not source.exists()
help_result = subprocess.run(['rein-aharness', '--help'], capture_output=True, timeout=15)
assert help_result.returncode == 0
assert b'usage:' in help_result.stdout
claude_version = None
proxy_present = False
if sys.argv[2]:
assert 'ANTHROPIC_API_KEY' not in os.environ
proxy_present = os.environ['HTTPS_PROXY'].startswith('http://127.0.0.1:')
assert proxy_present
cli = subprocess.run([str(runtime / 'bin/claude'), '--version'],
capture_output=True, text=True, timeout=20)
assert cli.returncode == 0, cli.stderr
claude_version = cli.stdout.strip()
assert claude_version == sys.argv[2] + ' (Claude Code)', claude_version
assert subprocess.check_output(['git', 'status', '--porcelain', '--ignored=matching']) == b''
interfaces = [line.split(':', 1)[0].strip()
for line in Path('/proc/net/dev').read_text().splitlines()[2:]]
@ -47,6 +58,8 @@ print(json.dumps({'rein_cli_started': True, 'adapter_imported': True,
'home_outside_workspace': True, 'home_mode': '0700',
'worktree_clean': True, 'interfaces': interfaces,
'python_prefix': sys.prefix,
'claude_version': claude_version, 'https_proxy_present': proxy_present,
'claude_provider_request_proven': False,
'credential_refs': json.loads(os.environ['SANDBOXER_CREDENTIAL_ROUTE_REFS'])}))
'''
@ -55,22 +68,26 @@ def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--runtime-path", required=True)
parser.add_argument("--runtime-sha256", required=True)
parser.add_argument("--claude-version", default="")
args = parser.parse_args()
with tempfile.TemporaryDirectory(prefix="sandboxer-runtime-proof-") as temporary:
root = Path(temporary)
source = root / "source"
source.mkdir()
subprocess.run(["git", "init", "-q", str(source)], check=True)
egress = ["api.anthropic.com:443"] if args.claude_version else []
extension = BwrapExtension({
"allowed_egress": egress,
"base_dir": str(root / "sandboxes"),
"runtime": {"path": args.runtime_path, "sha256": args.runtime_sha256},
})
profile = Profile(id="profile.runtime-proof", version="1", extension="ext.bwrap")
profile = Profile(id="profile.runtime-proof", version="1", extension="ext.bwrap",
network={"default": "deny", "egress": egress})
handle = extension.provision(profile, {"repo": str(source)}, "localhost")
try:
extension.wait_ready(handle)
result = extension.execute(
handle, ["python3", "-c", PROBE, str(source)],
handle, ["python3", "-c", PROBE, str(source), args.claude_version],
credential_route_refs=[],
execution_context={"actor": "agt", "project": "sand-boxer-runtime-proof",
"run_id": "sand-wp-0015-proof"},
@ -91,12 +108,17 @@ def main() -> int:
facts = json.loads(result["stdout"]) if passed else {}
private_state_persisted = persistence["exit_code"] == 0
passed = passed and private_state_persisted and teardown["workspace_removed"] == "True"
proxy_removed = not egress or (
not Path(handle["egress_dir"]).exists()
and not extension._pid_alive(int(handle["egress_pid"]))
)
passed = passed and proxy_removed
print(json.dumps({
"ok": passed, "sandbox_id": handle["sandbox_id"],
"runtime_sha256": args.runtime_sha256, "proof": facts,
"workspace_removed": teardown["workspace_removed"] == "True",
"exit_code": result["exit_code"], "model_run_proven": False,
"private_state_persisted": private_state_persisted,
"private_state_persisted": private_state_persisted, "proxy_removed": proxy_removed,
}, indent=2))
return 0 if passed else 1