Networking#
Every port in a sandbox can get a public HTTPS URL, and every sandbox is reachable over SSH through our gateway. Both wake a paused sandbox on the first request.
Public URLs for ports#
POST /sandboxes/{sandbox_id}/ports publishes a port at:
https://<short_id>-<port>.taskhost.cloud
<short_id> is the sandbox ID without the sbx_ prefix (sbx_a2b3c4d5e6f7 → a2b3c4d5e6f7): an underscore is not valid in hostnames. Use port.url or the sandbox's url_template instead of building URLs by hand.
HTTP/1.1, HTTP/2 and WebSocket are supported. TLS terminates at our edge; your server listens on plain HTTP inside the VM (bind to 0.0.0.0 or 127.0.0.1).
Sandbox URLs live on taskhost.cloud, a registrable domain separate from task.cloud (website, API and docs). Code running in a sandbox is therefore never same-site with the API and cannot set cookies for task.cloud. taskhost.cloud is submitted to the Public Suffix List so that browsers also treat each sandbox hostname as a separate site from other sandboxes.
await sandbox.exec({ command: "npm run dev -- --port 3000", cwd: "/home/user/app", background: true });
const port = await sandbox.ports.expose({ port: 3000, visibility: "public" });
console.log(port.url); // https://a2b3c4d5e6f7-3000.taskhost.cloud
sandbox.exec(command="npm run dev -- --port 3000", cwd="/home/user/app", background=True)
port = sandbox.ports.expose(port=3000, visibility="public")
print(port.url)
curl -sS -X POST https://api.task.cloud/v1/sandboxes/sbx_2fqxnt4bkq7m/ports \
-H "Authorization: Bearer $TASKCLOUD_API_KEY" -H "Content-Type: application/json" \
-d '{"port":3000,"visibility":"public"}'
taskcloud port expose sbx_2fqxnt4bkq7m 3000 --public
You can also expose ports at creation time with ports: [{"port": 3000, "visibility": "public"}] (up to 20). List with GET .../ports, remove with DELETE .../ports/{port}. Exposing an already exposed port updates its settings. Target: the URL responds within 1 s of your server starting.
Visibility#
visibility |
Who can open the URL |
|---|---|
private (default) |
requests with an access token (header X-Sandbox-Token or query sandbox_token), or an API key with sandboxes:read |
public |
anyone with the URL |
For a private preview in your end user's browser, mint a short-lived access token instead of exposing an API key. It grants HTTP access to this sandbox's private ports only.
const { token, expires_at } = await sandbox.accessTokens.create({ ttl_seconds: 3600, ports: [3000] });
const previewUrl = `${sandbox.ports.url(3000)}/?sandbox_token=${token}`;
t = sandbox.access_tokens.create(ttl_seconds=3600, ports=[3000])
preview_url = f"{sandbox.ports.url(3000)}/?sandbox_token={t.token}"
ttl_seconds ranges from 60 to 86400 (default 3600). The token is shown once.
Wake on request#
A request to the URL of a paused sandbox resumes it (target p95 < 800 ms) and is then served normally. Inbound traffic on exposed ports counts as activity, so an app with visitors stays awake, and it auto-pauses once traffic stops. See Lifecycle.
Not yet supported#
- Raw TCP/UDP ports (
protocolother thanhttp): Coming soon. Use SSH port forwarding meanwhile. - Custom domains: not available.
SSH#
POST /sandboxes/{sandbox_id}/ssh returns connection details for the gateway ssh.taskhost.cloud:22. The gateway username is the sandbox ID.
Three ways to authenticate:
- Account SSH keys (
POST /ssh-keys): work for every sandbox in the account. - A key for this sandbox only: pass
public_keyin the request; valid forttl_seconds(default 3600, max 604800). - One-time password: returned in
passwordwhen you pass no key and the account has none.
const access = await sandbox.ssh({ public_key: "ssh-ed25519 AAAA... me@laptop", ttl_seconds: 3600 });
console.log(access.command); // ssh sbx_2fqxnt4bkq7m@ssh.taskhost.cloud
access = sandbox.ssh(public_key=open(os.path.expanduser("~/.ssh/id_ed25519.pub")).read(), ttl_seconds=3600)
print(access.command)
curl -sS -X POST https://api.task.cloud/v1/sandboxes/sbx_2fqxnt4bkq7m/ssh \
-H "Authorization: Bearer $TASKCLOUD_API_KEY" -H "Content-Type: application/json" \
-d "{\"public_key\":\"$(cat ~/.ssh/id_ed25519.pub)\"}"
# {"host":"ssh.taskhost.cloud","port":22,"username":"sbx_2fqxnt4bkq7m","password":null,"expires_at":"...","command":"ssh sbx_2fqxnt4bkq7m@ssh.taskhost.cloud","host_key_fingerprint":"SHA256:..."}
taskcloud ssh sbx_2fqxnt4bkq7m # sends ~/.ssh/id_ed25519.pub as a temporary key and connects
The response also contains host_key_fingerprint; verify it on first connect. Log in as another Linux user with user (default user).
SSH supports everything OpenSSH does through the gateway:
ssh sbx_2fqxnt4bkq7m@ssh.taskhost.cloud 'uname -a' # one command
ssh -L 5432:localhost:5432 sbx_2fqxnt4bkq7m@ssh.taskhost.cloud # local forward
ssh -R 8080:localhost:8080 sbx_2fqxnt4bkq7m@ssh.taskhost.cloud # reverse forward
scp ./data.csv sbx_2fqxnt4bkq7m@ssh.taskhost.cloud:/home/user/
VS Code and Cursor Remote-SSH work with a host entry:
Host sbx_2fqxnt4bkq7m
HostName ssh.taskhost.cloud
User sbx_2fqxnt4bkq7m
IdentityFile ~/.ssh/id_ed25519
Connecting to a paused sandbox wakes it. An open SSH session counts as activity.
Outbound traffic#
Outbound internet access from the sandbox is open by default. An egress allowlist (restricting outbound traffic to listed domains) is Coming soon. Abuse such as mining, spam or scanning leads to suspension; see Security.
Inbound traffic reaches the VM only through exposed ports and the SSH gateway. Each sandbox has its own network namespace and cannot reach other sandboxes.
Required scopes#
| Operation | Scope |
|---|---|
| list ports | sandboxes:read |
| expose / remove ports, access tokens | sandboxes:write |
| SSH access, SSH keys | ssh |
Related#
- Exec and files
- Desktop and computer use: the desktop has its own URL,
https://<short_id>-desktop.taskhost.cloud - Security
- Limits