Skip to content

fix(local): rewrite URLs on this machine for the local containers they're handed to - #104

Merged
earakely-scale merged 6 commits into
mainfrom
edgararakelyan/local-container-urls
Oct 7, 2026
Merged

earakely-scale merged 6 commits into
mainfrom
edgararakelyan/local-container-urls

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

What and why

A container on a local sandbox can't reach this machine at localhost. Model endpoints, agent URLs and gateway URLs that agent-env handed into such containers were passed through unchanged. As a result:

  • a fully local task couldn't call a model proxy on this machine;
  • peer agents couldn't reach each other;
  • the gateway couldn't call its trigger executor.

The rewrite

  • LocalSandboxProvider.get_external_url now rewrites only a loopback host (localhost, 127.x, ::1, 0.0.0.0) to host.docker.internal. Before, it replaced the substring anywhere in the URL.
  • host_url_for(url, sandbox_type) rewrites a URL on this machine for a container on a local sandbox. Every other provider gets the URL unchanged.

URLs rewritten for the container that receives them

  • The model endpoint for an agent: configured or passed in, which covers deploy_agent and the agent judge. The agent's own default_env_vars are left alone.
  • The model endpoint for the unit-test verifier, after its env_vars and step_params, and for install_agent in container mode.
  • Peer URLs, including a human peer. A host-mode agent on a local sandbox runs on this machine, so it gets them as they are.
  • The trigger executor URL handed to the gateway.
  • The CLI gateway URL.

Linux

  • Linux has no native host.docker.internal, so Sandbox.extra_hosts carries host.docker.internal:host-gateway on a Linux local sandbox.
  • It is added to the gateway in the compose file (including the MultiEnv snapshot reload), to run_docker_container, and to agents placed on an existing local sandbox. Before, those agents got it only when local grants were on.
  • On Linux that name is the Docker bridge address, so a service on this machine must listen there. AGENTS.md and .agentenv/config.example.toml say to bind it to the bridge address (for example 172.17.0.1) and use that in its URL. That keeps it off the network; binding 0.0.0.0 works too but exposes it.

Remote providers see no change.

How it was tested

  • make unit-test: 6583 passed, including tests for each rewritten URL and for URLs where localhost is not the host. check_plugin_api.py reports no break.
  • Live, macOS and native Linux. A fully local solver plus agent judge with the model on this machine passed on both. Also checked, with full results in the chaos run comment:
    • peers, the trigger executor, the CLI gateway and the unit-test verifier container;
    • a step container on a custom network, and an enable_docker agent on its sidecar network;
    • two runs at once;
    • failure cases: a stopped executor, a removed peer, the model endpoint down.
  • Linux, service bound to the bridge address. Reachable from the host and from containers on the default bridge, a custom network and a compose network. Agent-judge and direct-LLM-judge tasks passed with the model proxy on 172.17.0.1. Bound to 127.0.0.1 only, containers are refused.
  • Mixed. A modal_vm gateway with one local agent and one Modal agent: URLs from beyond this machine pass through unchanged and are reached.

🤖 Generated with Claude Code

RetriggerConfidence Score: 5/5

The PR appears safe to merge; no outstanding issue was established.

What we checked:

  • CLI install reads a missing field: No. require_gateway_url rejects records without a gateway first. Gateway records have sandbox_type.

Summary

Local containers now get host-reachable addresses for URLs that point back to this machine, while remote sandbox URLs stay unchanged. The change covers model, peer, gateway, and CLI URLs, and adds Linux host-name mapping support.

  • Local containers can reach URLs that point back to this machine.
  • Agents and gateways get peer URLs they can reach.
  • Linux local containers get a name for reaching the host.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[URL on this machine] --> B{Recipient runs in a local container?}
  B -- Yes --> C{Loopback host?}
  C -- Yes --> D[Use host.docker.internal]
  C -- No --> E[Keep URL]
  B -- No --> E
Loading

Reviews (4) · Last reviewed commit: "refactor(local): drop an always-true che..." · Reviewed by Greptile

earakely-scale and others added 3 commits October 7, 2026 13:20
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…container-urls

# Conflicts:
#	src/agent_env/task_step/context.py
@earakely-scale
earakely-scale requested a review from a team as a code owner October 7, 2026 20:54
Comment thread src/agent_env/task_step/task_steps/verifiers/run_container_unit_tests_verifier.py Outdated
Comment thread src/agent_env/task_step/task_steps/peer_agents.py Outdated
…ts get peer URLs as they are

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@earakely-scale

Copy link
Copy Markdown
Collaborator Author

Chaos run on the local provider: macOS (Rancher Desktop) and Linux (native Docker 29, x86).

Scenario macOS Linux
Solver + agent judge, model on this machine pass pass, proxy on 0.0.0.0
Model proxy bound to 127.0.0.1 only pass containers refused; proxy saw 0 calls (the documented case)
Peers (2 agents + a human), fetched from the source's container pass pass
Human endpoint on 127.0.0.1 only pass refused (same case)
Gateway → trigger executor pass pass
Executor stopped before the trigger fires action_failed in 1.0 s, gateway still answers same
Peer removed after peering refused, no stale 200; re-peered replacement 200 same
CLI gateway URL from inside the agent pass pass
Unit-test verifier container → model pass pass
Step container on a custom network pass pass, with the host-gateway mapping
enable_docker agent on the sidecar network pass* pass*
Two runs at once 11/11 each, every URL is its own run's same
Model endpoint down fails in 3.4 s, tears down not run

Mixed topology: a modal_vm gateway, one local agent and one Modal agent.

  • Both agents reach the model endpoint (https, passed through unchanged).
  • The local agent gets the gateway's MCP and CLI URLs and its Modal peer's URL unchanged, and reaches them.
  • The Modal gateway calling the local executor fails cleanly in 0.3 s.

* deploy_agent can't place an agent on a local sandbox today: the rebuilt sandbox handle loses its port map, and _run_container publishes the container port. Both are on main and tracked separately; the test deployed onto the sandbox handle directly.

Nothing was left running on either machine.

earakely-scale and others added 2 commits October 7, 2026 14:39
…cker bridge address

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@earakely-scale
earakely-scale merged commit 81682ff into main Oct 7, 2026
13 checks passed
@earakely-scale
earakely-scale deleted the edgararakelyan/local-container-urls branch October 7, 2026 23:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant