Skip to main content
Sailboxes are closed to inbound traffic by default for security.

Inbound access control

To expose a service at creation time, pass the guest port in the create request, start a process that listens on that port, and wait for the listener endpoint to become ready:
You can also add and remove ports on a running Sailbox with expose/unexpose (see Add or remove ports at runtime). Sail supports two inbound protocols:
  • HTTP, which returns a public HTTPS URL and supports HTTP and WebSocket traffic.
  • Raw TCP, which returns a public host and port for protocols such as SSH, Postgres, or custom TCP servers.
Terminating a Sailbox also removes all of its listeners.

HTTP and WebSocket services

Expose a guest port over HTTP (in Python, a bare port number in ingress_ports means HTTP). The listener resolves to a routable HTTPS URL.
Use the same URL for WebSocket clients by replacing https:// with wss:// when the process inside the Sailbox speaks WebSocket on that port.

Raw TCP ports

Expose a port as raw TCP instead of HTTP when the protocol is not HTTP-aware or the service already implements its own authentication. The listener resolves to a public host and port that any TCP client can dial directly.
After the service starts, wait for the endpoint and connect with the matching client:

SSH access

For a quick interactive shell, use Sailbox.shell() or sail box shell: it streams a terminal over the same channel as exec, uses no ingress port, and while open it forwards the box’s localhost servers and browser opens to your machine. Set up SSH when you want a standard SSH endpoint: a devbox, your own client, scp, or port forwarding. It exposes guest port 22 as raw TCP, which counts against your org’s raw-TCP endpoint limit. SSH access is organization-scoped: a box trusts your org’s SSH certificate authority, so anyone in the org can connect with a short-lived certificate signed for their key. There are no per-box keys to manage. A private box is the exception: its sshd accepts only its creator’s certificates. Enable SSH at create time, or with enable_ssh() on a Sailbox that already exists. Both install the org CA as trusted, start sshd, and expose port 22. enable_ssh() is idempotent; re-run it to bring sshd back up if it stops.
To connect, wire up your machine with the sail box ssh CLI and use the box’s <name>.sail shortcut:
alias fetches your certificate and writes the shortcut into your SSH config. The shortcut is what presents the certificate, so alias a box before you connect. Run it on each machine you connect from, whether you or a teammate enabled the box; it only touches local config and never wakes or changes the box. sail box ssh enable <id> enables SSH on an existing box and runs alias for you in one step, as does creating a box with sail box create --enable-ssh. To restrict which sources may connect, pass an allowlist of CIDR prefixes: sb.enable_ssh(allowlist=["203.0.113.0/24"]) in Python, box.enableSsh({ allowlist: ["203.0.113.0/24"] }) in TypeScript, the allowlist argument of enable_ssh in Rust, or sail box ssh enable <id> --allowlist 203.0.113.0/24 from the CLI. A new allowlist replaces the current one, so re-running can tighten or relax access. Disabling SSH (sail box ssh disable) removes the port-22 listener along with its restriction. The SSH server persists across sleep and resume. An open SSH session drops when the Sailbox sleeps, but reconnecting with ssh <name>.sail wakes it and uses the same host key.

Inspect endpoints

Look up one listener when you know the guest port, or list all published ports for a Sailbox:
HTTP listeners expose a public URL. TCP listeners expose a public host and port.

Add or remove ports at runtime

You don’t have to declare every port at create time. expose publishes a new port on a running Sailbox and unexpose removes one, with no guest restart. The listener returned by expose carries the resolved endpoint but an "unknown" route status: the response confirms configuration, not reachability. wait_for_listener confirms the route is live.
Re-exposing a port under the same protocol updates its allowlist to the value you pass. Use it to tighten or relax a live port. Unexposing a raw-TCP port stops serving it, so it no longer counts against your org’s raw-TCP endpoint limit. Its public host:port stays owned by your org. Sail may reassign an idle address to another of your Sailboxes; it is never given to another org. If the address has not been reassigned, re-exposing the same guest port reclaims it exactly. Otherwise the re-expose allocates a new address, so read the endpoint from the response instead of assuming the old one. A stale client that dials an old raw-TCP address may therefore reach a different Sailbox in your org, and never another org’s. A raw-TCP guest port can’t be repurposed to HTTP; use a different guest port. HTTP ports carry no such reservation and are released on unexpose. expose and unexpose work on a paused or sleeping Sailbox without waking it; a later resume serves the new listener. From the CLI:

Access controls

A raw-TCP port is reachable from the public internet with no platform-side authentication. The in-guest daemon, such as sshd, is the only access control, so make sure it requires credentials.
To restrict which sources may connect, pass allowlist. Entries that parse as CIDR prefixes match source IPs on HTTP and TCP listeners. Other entries are treated as Sail app names for authenticated Sailbox-origin traffic, supported on HTTP listeners:
Connections from outside the listed CIDR prefixes, or from authenticated Sailbox-origin requests whose app name is not listed, fail before reaching the guest. App names do not need to exist when you configure the listener. Cross-organization app-name matches are denied. An empty or omitted allowlist means any source may connect. For HTTP requests from one Sailbox to an app-name allowlisted listener, include the SDK-provided source headers. Inside the calling Sailbox, the Python SDK reads its own identity:
From outside a Sailbox, such as an orchestrator or test driving Sailboxes from the host, fetch the same headers for a specific live Sailbox you own (requires an organization-scoped API key):
Raw-TCP connections do not carry source app identity, so app-name entries are rejected on "tcp" listeners: a raw-TCP allowlist must contain only CIDR prefixes. Exposing a well-known unauthenticated service port, such as Postgres, MySQL, or Redis, as raw TCP without an explicit allowlist is rejected. Set source restrictions, or use the all-sources CIDR allowlist to confirm you want it publicly reachable:

Sleeping services

Sleeping Sailboxes wake on network ingress. If you call sleep() on a Sailbox with exposed listeners, the next inbound HTTP, WebSocket, or TCP connection wakes the VM before forwarding traffic to the guest process.
Use pause() instead when you want to preserve VM state without waking on network traffic.

Ports and cleanup

Ports must be unique within a Sailbox and between 1 and 65535. Ports 10000, 10001, 15001, and 15002 are reserved by Sail. Port 22 is the SSH port and cannot be exposed as HTTP; expose it as raw TCP instead. Each org can hold a limited number (32) of concurrent raw-TCP endpoints. The limit counts actively-exposed endpoints, so unexpose frees a slot. An idle host:port stays owned by your org, even after the Sailbox that used it terminates. Sail may reassign it to another of your Sailboxes; another org never receives it. Contact us to raise your limit if you need more concurrent endpoints.