diff --git a/claude_bottle/backend/docker/bottle_plan.py b/claude_bottle/backend/docker/bottle_plan.py index c3d76a7..293e965 100644 --- a/claude_bottle/backend/docker/bottle_plan.py +++ b/claude_bottle/backend/docker/bottle_plan.py @@ -95,6 +95,7 @@ class DockerBottlePlan(BottlePlan): else: info(" ssh hosts : (none)") info(f" egress : {self.allowlist_summary}") + info(" tls intercept : mitmproxy (per-bottle ephemeral CA, generated at launch)") info( f"prompt : {len(v.agent.prompt)} chars; " f"first line: {v.prompt_first_line or '(empty)'}" @@ -119,6 +120,14 @@ class DockerBottlePlan(BottlePlan): "egress": { "host_count": len(hosts), "hosts": hosts, + # Reserved for PRD 0005: TLS interception via mitmproxy. + # ca_fingerprint is always null at dry-run because the + # CA is generated by the sidecar at launch time. Real + # launches print the fingerprint to stderr. + "mitm": { + "enabled": True, + "ca_fingerprint": None, + }, }, "prompt": { "length": len(v.agent.prompt), diff --git a/tests/integration/test_dry_run_plan.py b/tests/integration/test_dry_run_plan.py index c0ae3eb..45c4564 100644 --- a/tests/integration/test_dry_run_plan.py +++ b/tests/integration/test_dry_run_plan.py @@ -92,6 +92,12 @@ class TestDryRunPlan(unittest.TestCase): self.assertEqual(sorted(set(hosts)), hosts, "hosts must be sorted and deduplicated") + # PRD 0005: TLS interception block is part of the JSON + # contract. Fingerprint is null at dry-run (CA doesn't + # exist yet); real launches print it to stderr. + self.assertEqual({"enabled": True, "ca_fingerprint": None}, + plan["egress"]["mitm"]) + # No Docker side effects (see the GITEA_ACTIONS skip note # above — this guard runs locally only). if check_side_effects: