feat(code-editing): Step 2 — run-sandbox egress lock (gateway-only), flag-gated - #61
Merged
Merged
Conversation
…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
force-pushed
the
feat/run-egress-lock-step2
branch
from
June 27, 2026 16:44
44a0b79 to
3863e05
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 (fullhttpx/wayforth-sdkclosure baked) and runsagent.pydirectly. 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 realhttpx.get(gateway/status)— on the base image withdeny_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→ flipAGENT_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.BASE_DEPSwas a hand-list that missedhttpcore+typing-extensions.httpximportshttpcorelazily, only on the first request — so a bareimport httpxcheck (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_DEPSis now the full resolved closure (pip downloadwith deps); lockfile updated (httpcore 1.0.9,typing-extensions 4.15.0); anddeps_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