Skip to content

feat(forward): reverse loopback forwarding - #13

Merged
celrenheit merged 1 commit into
mainfrom
reverse-forward
Aug 5, 2026
Merged

feat(forward): reverse loopback forwarding#13
celrenheit merged 1 commit into
mainfrom
reverse-forward

Conversation

@celrenheit

Copy link
Copy Markdown
Contributor

clawk forward add exposes a guest port on the host; the other direction
had no answer. A guest process that dials 127.0.0.1 reaches the guest's own
loopback, and no allow-list entry can route that to the host's — so tools
that assume both ends share a loopback cannot work in a sandbox at all. The
case that prompted it (#10): the Claude Code IDE plugins
advertise a websocket port in ~/.claude/ide/.lock and claude connects
to ws://127.0.0.1:, with no knob to point it elsewhere.

clawk forward add-reverse <sandbox> <spec> fills it in. The in-guest agent
binds the port on the guest's loopback and tunnels each connection over
vsock (port 1028) to the daemon, which checks the requested port against the
sandbox's configured set before dialling the host service. The guest names a
port and never an address, so only the listed ports are reachable — reaching
host loopback through the gvproxy gateway NAT instead would have been
all-or-nothing, which is the wrong default for an agent sandbox.

Unlike outbound forwards these apply to a running sandbox: the daemon holds
a control stream to the agent and pushes the complete set on every edit
(POST /v1/reload-forwards on the existing control socket). That matters
because the IDE port is per-window. Host dials try both loopback families —
"localhost" resolves to ::1 first on macOS, so a server told to bind the
name often listens on [::1] alone (python3 -m http.server --bind localhost, much Node tooling), and an IPv4-only dial made those look dead:
the guest accepted the connection and immediately reset it.

Adds internal/revfwd (wire protocol, mirrored by hand in the guest agent
and guarded by a reflection-based lock-step test), the host proxy, the guest
forwarder, Sandbox.ReverseForwards, a reverse entry inside clawk.mod's
forwards block, status/JSON surfacing, and docs including the IDE recipe.
The host proxy is transport-agnostic so its accept/handshake/bridge path is
tested over TCP on any platform. vz only: firecracker's vsock is one-way, so
the endpoint 404s and the CLI reports that rather than an apply that never
happened.

Fixes #10

`clawk forward add` exposes a guest port on the host; the other direction
had no answer. A guest process that dials 127.0.0.1 reaches the guest's own
loopback, and no allow-list entry can route that to the host's — so tools
that assume both ends share a loopback cannot work in a sandbox at all. The
case that prompted it (#10): the Claude Code IDE plugins
advertise a websocket port in ~/.claude/ide/<port>.lock and claude connects
to ws://127.0.0.1:<port>, with no knob to point it elsewhere.

`clawk forward add-reverse <sandbox> <spec>` fills it in. The in-guest agent
binds the port on the guest's loopback and tunnels each connection over
vsock (port 1028) to the daemon, which checks the requested port against the
sandbox's configured set before dialling the host service. The guest names a
port and never an address, so only the listed ports are reachable — reaching
host loopback through the gvproxy gateway NAT instead would have been
all-or-nothing, which is the wrong default for an agent sandbox.

Unlike outbound forwards these apply to a running sandbox: the daemon holds
a control stream to the agent and pushes the complete set on every edit
(POST /v1/reload-forwards on the existing control socket). That matters
because the IDE port is per-window. Host dials try both loopback families —
"localhost" resolves to ::1 first on macOS, so a server told to bind the
name often listens on [::1] alone (`python3 -m http.server --bind
localhost`, much Node tooling), and an IPv4-only dial made those look dead:
the guest accepted the connection and immediately reset it.

Adds internal/revfwd (wire protocol, mirrored by hand in the guest agent
and guarded by a reflection-based lock-step test), the host proxy, the guest
forwarder, Sandbox.ReverseForwards, a `reverse` entry inside clawk.mod's
forwards block, status/JSON surfacing, and docs including the IDE recipe.
The host proxy is transport-agnostic so its accept/handshake/bridge path is
tested over TCP on any platform. vz only: firecracker's vsock is one-way, so
the endpoint 404s and the CLI reports that rather than an apply that never
happened.

Fixes #10
@celrenheit
celrenheit merged commit 1bdc64a into main Aug 5, 2026
2 checks passed
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.

Reverse loopback forwarding: let the guest reach host-bound localhost services (e.g. JetBrains Claude Code plugin)

1 participant