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: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
hostandportfor protocols such as SSH, Postgres, or custom TCP servers.
HTTP and WebSocket services
Expose a guest port over HTTP (in Python, a bare port number iningress_ports means HTTP). The listener resolves to a routable HTTPS URL.
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 publichost and port that any TCP client can dial directly.
SSH access
For a quick interactive shell, useSailbox.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.
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: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.
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
To restrict which sources may connect, passallowlist. 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:
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:
"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 callsleep() on a Sailbox
with exposed listeners, the next inbound HTTP, WebSocket, or TCP connection
wakes the VM before forwarding traffic to the guest process.
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 between1 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.