Skip to content

feat(code-editing): Step 2 — run-sandbox egress lock (gateway-only), flag-gated - #61

Merged
WayforthOfficial merged 1 commit into
mainfrom
feat/run-egress-lock-step2
Jun 27, 2026
Merged

feat(code-editing): Step 2 — run-sandbox egress lock (gateway-only), flag-gated#61
WayforthOfficial merged 1 commit into
mainfrom
feat/run-egress-lock-step2

Conversation

@WayforthOfficial

Copy link
Copy Markdown
Owner

Flips the run sandbox from the deny-list (allow-public-internet — the exfil gap) to a gateway-only allow-list, closing runtime exfiltration. Flag-gated + reversible like the token cutover. No merge until you're satisfied with #1.

#1 — Existing-agent survival (the migration gate) — ANSWERED, live-proven

What happens to a pre-Step-1 agent under the new egress: it does not pip install (that path is replaced when the flag is on). It boots the pre-built base image (full httpx/wayforth-sdk closure baked) and runs agent.py directly. All 6 existing agents call only /proxy (gateway, reachable under gateway-only egress), so they survive.

Live proof: an existing-agent workload — import httpx + a real httpx.get(gateway/status) — on the base image with deny_out=[0.0.0.0/0]+allow_out=[gateway]HTTPX_OK 0.28.1 GATEWAY 200, exit 0, no pip.

Sequence (so egress is never locked before agents can survive it): build base image → set AGENT_BASE_IMAGE → flip AGENT_GATEWAY_EGRESS_ENABLED. If the base image is unset, the code falls back to the deny-list path (a run never breaks). node is not locked in v1 (npm-installs at run time; node deps pipeline is a follow-on).

#2 — Reversible, flag-gated

AGENT_GATEWAY_EGRESS_ENABLED (default OFF). Off = byte-identical to today (deny-list + pip). Flip off → instant revert, no redeploy — same as the token flip.

⚠️ The survival proof caught a real bug (transparency)

BASE_DEPS was a hand-list that missed httpcore + typing-extensions. httpx imports httpcore lazily, only on the first request — so a bare import httpx check (last turn's #3) passed while a real request failed (ModuleNotFoundError: httpcore). This is exactly why you required a real survival proof, not an import check. Fixed: BASE_DEPS is now the full resolved closure (pip download with deps); lockfile updated (httpcore 1.0.9, typing-extensions 4.15.0); and deps_live_proof.py #3 now does a real request to catch this class of gap.

Tests (9)

flag off = deny-list + pip (unchanged) · flag on + base = gateway-only + base template + no pip · node never locked · base-image-unset fallback · flag-value parsing. Full suite green: 593 passed.

Cutover — three distinct moments (like the token flip)

build + set base image → deploy flag-off (inert) → flip AGENT_GATEWAY_EGRESS_ENABLED. The §6 ship-gate (malicious-package canary etc.) + the E2B Firecracker-token confirmation gate flip-on, not this PR.

🤖 Generated with Claude Code

@railway-app
railway-app Bot temporarily deployed to wayforth / wayforth-pr-61 June 27, 2026 16:43 Destroyed
…flag-gated

Flips the run sandbox from the deny-list (allow-public-internet) to a gateway-only
allow-list — closing runtime exfiltration. Flag-gated + reversible like the token cutover.

services/sandbox.py: when AGENT_GATEWAY_EGRESS_ENABLED is on AND AGENT_BASE_IMAGE is
set, a PYTHON run gets network deny_out=[0.0.0.0/0] + allow_out=[gateway], boots the
pre-built base image, and runs WITHOUT the run-time pip install (deps baked). Flag off
is byte-identical to today (deny-list + pip). node is NOT locked in v1 (npm-install at
run time; node deps pipeline is a follow-on). Base image unset -> safe fallback to the
deny-list path (a run never breaks).

#1 EXISTING-AGENT SURVIVAL — answered with a LIVE PROOF (the migration gate):
A pre-Step-1 python agent under the new egress does NOT pip-install; it boots the base
image (httpx/wayforth-sdk closure baked) and runs agent.py directly. All 6 existing
agents call only /proxy (gateway, reachable), so they survive. Proven live: an
existing-agent workload (import httpx + a REAL httpx.get to the gateway) on the base
image with gateway-only egress -> 'HTTPX_OK 0.28.1 GATEWAY 200', exit 0, no pip.
Sequence: build base image -> set AGENT_BASE_IMAGE -> flip flag. Reversible: flip off
-> instant revert to deny-list+pip, no redeploy.

The survival proof CAUGHT A REAL BUG: BASE_DEPS hand-list missed httpcore +
typing-extensions (httpx imports httpcore lazily only on a real request, so a bare
import-httpx check passed while a real run failed). Fixed: BASE_DEPS is now the FULL
resolved closure (pip download with deps); lockfile updated (httpcore 1.0.9,
typing-extensions 4.15.0). deps_live_proof.py #3 now does a REAL request to catch this.

Tests (9): flag off = deny-list + pip (unchanged); flag on + base = gateway-only +
base template + no pip; node never locked; base-unset fallback; flag value parsing.
Full suite green (593 passed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@WayforthOfficial
WayforthOfficial force-pushed the feat/run-egress-lock-step2 branch from 44a0b79 to 3863e05 Compare June 27, 2026 16:44
@railway-app
railway-app Bot temporarily deployed to wayforth / wayforth-pr-61 June 27, 2026 16:44 Destroyed
@WayforthOfficial
WayforthOfficial merged commit 6b56c95 into main Jun 27, 2026
2 of 3 checks passed
@WayforthOfficial
WayforthOfficial deleted the feat/run-egress-lock-step2 branch June 27, 2026 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants