From fb10c8dd8af44878e3429e0c8847a5e05134a7a9 Mon Sep 17 00:00:00 2001 From: didericis Date: Tue, 12 May 2026 14:52:53 -0400 Subject: [PATCH] feat(bottle-plan): render TLS interception in the dry-run preflight Third step of PRD 0006. The preflight now surfaces the TLS- intercept layer so the operator sees it before agreeing to launch. - Text output: one new line under the egress summary ("tls intercept : pipelock (per-bottle ephemeral CA, generated at launch)"). - JSON output (--format=json contract): new egress.tls_interception: { enabled: true, ca_fingerprint: null } block. Fingerprint is always null at dry-run because the CA only exists after launch; real launches print it as a stderr log line from provision_ca. - Pin the new shape in the dry-run integration test. Co-Authored-By: Claude Opus 4.7 --- claude_bottle/backend/docker/bottle_plan.py | 10 ++++++++++ tests/integration/test_dry_run_plan.py | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/claude_bottle/backend/docker/bottle_plan.py b/claude_bottle/backend/docker/bottle_plan.py index 5ad3da8..cd9dc19 100644 --- a/claude_bottle/backend/docker/bottle_plan.py +++ b/claude_bottle/backend/docker/bottle_plan.py @@ -93,6 +93,7 @@ class DockerBottlePlan(BottlePlan): else: info(" ssh hosts : (none)") info(f" egress : {self.allowlist_summary}") + info(" tls intercept : pipelock (per-bottle ephemeral CA, generated at launch)") info( f"prompt : {len(v.agent.prompt)} chars; " f"first line: {v.prompt_first_line or '(empty)'}" @@ -117,6 +118,15 @@ class DockerBottlePlan(BottlePlan): "egress": { "host_count": len(hosts), "hosts": hosts, + # PRD 0006: pipelock's `tls_interception` block is on + # for every launched bottle. ca_fingerprint is always + # null at dry-run because the CA doesn't exist yet — + # real launches print the fingerprint to stderr from + # provision_ca. Reserved field for forward-compat. + "tls_interception": { + "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..b564ae3 100644 --- a/tests/integration/test_dry_run_plan.py +++ b/tests/integration/test_dry_run_plan.py @@ -92,6 +92,14 @@ class TestDryRunPlan(unittest.TestCase): self.assertEqual(sorted(set(hosts)), hosts, "hosts must be sorted and deduplicated") + # PRD 0006: TLS interception is on for every launched + # bottle. Fingerprint is null at dry-run (no CA exists + # yet); real launches log it from provision_ca. + self.assertEqual( + {"enabled": True, "ca_fingerprint": None}, + plan["egress"]["tls_interception"], + ) + # No Docker side effects (see the GITEA_ACTIONS skip note # above — this guard runs locally only). if check_side_effects: