Skip to content

Ports & URLs

A server listening on a port inside a sandbox answers at https://<subdomain>-<port>.on.boat.dev once Boat hosts the port. A hosted port is protected by default: requests need its token. A public port answers anyone.

Without the token, the port’s URL refuses the request:

_port_auth cookie403box-node-…-3000.on.boat.devprotectedanyoneno tokenApiWorker · getTcpPort(3000):3000npm run dev_port_auth cookie403box-node-…-3000.on.boat.devprotectedanyoneno tokenApiWorker · getTcpPort(3000):3000npm run dev
The port's URL refuses requests without its token. getTcpPort gets the token from Boat and sends it as the _port_auth cookie on every request.

getTcpPort hosts the port and returns its URL and token. Its fetch sends a request there with the token:

const port = yield* sandbox.getTcpPort(3000);
const response = yield* port.fetch(HttpClientRequest.get("/health"));

The request’s path, query, method, headers and body are kept, and bodies stream both ways.

Pass the Worker’s own request to fetch to forward it to the sandbox, and return the response:

fetch: Effect.gen(function* () {
const port = yield* devBox.getTcpPort(3000);
return yield* port.fetch(yield* HttpServerRequest);
});
const port = yield* sandbox.getTcpPort(3000, { public: true });

Anyone with the URL can call it. For a program, set public: true on its Boat.Sandbox.

Boat relays to a server bound to 0.0.0.0. For one that listens on 127.0.0.1 only, pass localhost: true:

const port = yield* sandbox.getTcpPort(5173, { localhost: true });

The token works on the _token query parameter as well, which answers with a redirect that sets the cookie, so a browser can open the URL:

const port = yield* sandbox.getTcpPort(3000);
const link = `${port.url}?_token=${Redacted.value(port.token!)}`;

The token is the same every time for one sandbox and port. Treat it as a secret.

To hear when sandboxes start and stop, see Lifecycle events.