Husk
GitHub

Computers

Ports

Publishing a port a machine is listening on, and what "reachable" means per provider.

Start the server inside the machine first, then publish the port. exposePort does not start anything; it makes an already-listening port reachable from the host.

husk exec dev -- 'cd /work && python3 -m http.server 8000 &'

From an agent, the tool is expose_port:

{ "port": 8000 }

which answers:

port 8000 is reachable at http://127.0.0.1:52431

The host port is not the guest port. Port 0 is passed to the OS so it picks a free one, which means two computers can both expose 8000 without colliding.

Per provider

ProviderHowWhat you get
docker, podmanA TCP proxy on the host, bound to 127.0.0.1:0, piping to the containerhttp://127.0.0.1:<random>
localNothing to forward — the process already bound the host's own stackhttp://127.0.0.1:<the same port>
sshAn ssh tunnel to the remote boxhttp://127.0.0.1:<random>
flyThe machine's own addressA Fly URL

On the container providers the forwarder is a Node TCP server created on demand and unref'd, so it never holds the CLI open. It is closed when the machine is destroyed.

PortBinding carries hostPort, url, and an optional publicUrl set only by providers that can offer a public tunnel. The expose_port tool prints publicUrl when it exists and url otherwise.

exposePort is idempotent: calling it twice for the same port returns the same binding rather than allocating a second forwarder.

The local provider caveat

On local there is no forwarding because there is no boundary to forward across. A server the agent starts on port 8000 is listening on your machine's port 8000, in your network namespace, reachable by anything else on your machine — and, if it binds 0.0.0.0, by your LAN.

That is the same trade the rest of the local provider makes, stated once here so it is not a surprise. If an agent is going to bind a port and you care who can reach it, use Docker.

Ports in husk ps --json

Published ports are recorded on the computer under ports, keyed by the in-machine port:

{
  "ports": {
    "8000": { "hostPort": 52431, "url": "http://127.0.0.1:52431" }
  }
}

Over the control plane

curl -s -X POST localhost:7377/v1/computers/cmp_x/ports \
  -H 'content-type: application/json' \
  -d '{"port":8000}'

The body is validated strictly: port must be an integer between 1 and 65535, and any other key is a 422.