Every agent run's network traffic leaves through an egress mode chosen for that session. The three modes are direct, frp, and client-proxy.
Egress is scoped to a session: the mode binds to the session, switching modes is an explicit lifecycle change, and failure never degrades silently.
Egress modes
| Mode | Transport | When to use |
|---|---|---|
direct |
The executing machine's existing route (no tunnel). | Machines with a public IP or otherwise reachable network. |
frp |
An frp reverse tunnel. | Enrolled devices behind NAT that need inbound reachability. |
client-proxy |
SOCKS5 through a selected client as the network exit. | Tools like browsers that should use a specific client's network. |
How a mode is selected
The default rule: a machine with a public IP uses direct; with an frp server configured it uses frp; with a designated relay client it uses client-proxy; otherwise direct (outbound always works, inbound goes through the enrollment channel). The mode can also be pinned by configuration:
MAGICFROG_EGRESS_MODE=client_proxy # or "direct" (default when unset)
MAGICFROG_CLIENT_ID=<client id> # defaults to the computer id
MAGICFROG_FRPS_ADDR=127.0.0.1
MAGICFROG_FRPS_PORT=7000
MAGICFROG_FRPS_TOKEN=<token>
The tunnel endpoint is published as socks5://<frps host>:<remotePort> once healthy.
Fail-closed session scoping
Switching is an explicit lifecycle change with four hard rules:
- Before a switch, the system verifies the target client is online and authorized; if the check fails, the session stays as it was.
- After a successful switch, the session binds the new egress; browser sessions rebuild on the new egress.
- When a switch fails (tunnel establishment failed, client offline, unauthorized), the session enters a failed state — no fallback to direct; the failure is reported explicitly with its reason.
- A failed session has no in-place recovery: the only way back to direct is to destroy and rebuild the session, never a silent migration.
Browser session binding
Browser sessions bind the mode at creation. A client_proxy session's browser runs entirely through the designated proxy, while an explicit direct session connects directly — that is a configured mode, not a fallback. While switching or failed, the browser refuses to bind: without a stable egress there is no new browser session.
If the tunnel dies mid-session, the browser keeps the now-dead proxy settings: connections fail; the browser never bypasses the proxy on its own.
Switching egress
Switching is an explicit lifecycle change driven through PATCH /v1/runs/{runId}/egress:
- Request:
{ "mode": "direct" | "client_proxy", "clientId": "..." }—clientIdis required when mode isclient_proxy(400 otherwise). - Errors are typed and explicitly surfaced:
400missing clientId,403unauthorized,410client offline (fail-closed, no fallback),504tunnel establish deadline.
A rejected switch leaves the healthy session untouched. Once a switch is committed, any later failure is reported explicitly — never a return to the old egress.
Revocation timing
Revoking a client's egress grant takes effect within 5 seconds: the session enters a failed state, and every connection routed through that proxy is actively terminated, not left to time out on its own.