Egress proxy
Let agents install packages without giving them the internet.
The workspace network has no route out. Everything except the broker and model gateway goes through egress-proxy, the only service on both the workspace and upstream networks. Compose sets HTTPS_PROXY/HTTP_PROXY and points workspace DNS at 127.0.0.1, so external names don't resolve.
Allowlist
{
"allow": ["registry.npmjs.org:443", "*.pythonhosted.org:443"],
"deny": ["uploads.example.com"],
"maxBytesPerHost": 2147483648
}Point AGENTGATE_EGRESS_CONFIG_PATH at your own copy. examples/egress.ide.json adds the VS Code, Cursor, and Open VSX hosts an attached IDE needs; see IDEs & workspace profiles.
- Only
CONNECT host:port. Any other method gets 405 — no plaintext proxying. - Entries are
host:portor*.suffix:port.*.example.orgdoes not matchexample.org. - IP literals and invalid entries are rejected at startup.
Always denied
GitHub — github.com, *.github.com, *.githubusercontent.com, *.githubassets.com, *.github.io, ghcr.io — is denied whatever the allowlist says. GitHub access goes through the broker only.
SSRF protection
The proxy resolves every address itself and refuses the tunnel if any is loopback, private, link-local, CGNAT, documentation, multicast, reserved, IPv4-mapped, or a NAT64/6to4 form of those. It connects only to vetted addresses and never re-resolves, so DNS rebinding doesn't work.
Quotas
maxBytesPerHost(default 2 GiB) caps bytes in both directions per client and rule.- Idle tunnels close after 5 minutes; at most 32 tunnels per client.
- CONNECT headers must arrive within 10 seconds.
Each decision is logged with host, port, decision, reason, and byte counts — never payloads.
Attributing egress to a runtime
Give the proxy the issuer keys and it attributes each tunnel to the runtime whose assertion arrives as proxy credentials:
{ "allow": ["registry.npmjs.org:443"], "identity": { "audience": "agentgate:acme", "issuers": [{ "kid": "k1", "publicKeyPem": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----\n" }], "required": false } }The workspace profile rewrites HTTPS_PROXY, HTTP_PROXY, https_proxy, and http_proxy to http://runtime:<assertion>@egress-proxy:3128 when a runtime assertion is present; Git, curl, npm, pip, and apt send that as Proxy-Authorization on every CONNECT. The proxy verifies the signature, audience, lifetime, and claims (not the token binding, which it cannot see), records runtime on the egress event, and keeps the byte quota per runtime and rule instead of per address. A credential that fails verification, or a user other than runtime, is refused with 407; with "required": true a tunnel without credentials is refused too. Tools that print their proxy URL in error output will show the assertion; it names the developer and task but grants nothing without the workspace token.
Warning
Allowlisted hosts that accept uploads (like package publish endpoints) remain an exfiltration channel.