Skip to content

Port Allowlist

Sandboxes default to closed for public proxy access. A port inside a sandbox is only reachable from the public internet after an explicit exposePort call. This prevents unauthenticated access to databases, debug endpoints, or accidental listeners.

Once a port is exposed, it is reachable via two equivalent URLs:

1. Dedicated port URL - returned directly by exposePort:

https://<sandbox-id>-<port>.<domain>

2. Proxy path - constructed from the sandbox's main public URL:

https://<sandbox-id>.<domain>/proxy/<port>/

Both URLs become active after exposePort and return 403 before it. The dedicated URL is the cleaner base URL for web apps and APIs. The proxy path is always available in both domain mode and IP-only mode.

const sandbox = await client.create({ image: 'ubuntu:22.04', allowPublicTraffic: true })
// Before exposing - 403
await fetch(`${sandbox.publicURL}/proxy/21212/`)
// Expose the port - returns the discriminated ExposeResult
const exposure = await sandbox.exposePort(21212)
// exposure.url → "https://<sandbox-id>-21212.sandbox.example.com"
// Both URLs now reach the service
await fetch(exposure.url) // 200 - dedicated URL
await fetch(`${sandbox.publicURL}/proxy/21212/`) // 200 - proxy path

Removes the port from the allowlist and tears down its public route. Both URLs return 403 again immediately.

await sandbox.unexposePort(21212)
await fetch(`${sandbox.publicURL}/proxy/21212/`) // 403

The allowlist is maintained server-side. Every exposePort and unexposePort call updates the allowlist and creates or removes the corresponding Caddy route. When a sandbox stops and restarts, the allowlist is restored automatically so previously exposed ports become reachable again without any extra calls.

  1. Confirm the port appears in exposed_ports on GET /v1/sandboxes/<id>.
  2. Check that exposePort completed without error.
  3. Verify the sandbox is in running state - stopped sandboxes do not serve traffic.