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 (protocol other than http): 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:

  1. Account SSH keys (POST /ssh-keys): work for every sandbox in the account.
  2. A key for this sandbox only: pass public_key in the request; valid for ttl_seconds (default 3600, max 604800).
  3. One-time password: returned in password when 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