DocsNetwork and public URLs

Network and public URLs

Every port a sandbox listens on has a public HTTPS URL derived from the sandbox ID. You choose what the sandbox may reach on the way out, and whether anyone may reach it on the way in.

Every example reads IMPELLO_API_KEY from the environment; get a key at dashboard.impello.ai/keys.

Reach a port from the internet

getHost(port) returns the host for a port, <port>-<sandboxId>.sandbox.impello.ai. You supply the scheme and the path. There is no separate "expose port" call — start listening and the URL works.

import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base')

await sandbox.commands.run('python3 -m http.server 8000 --bind 0.0.0.0', {
  background: true,
  timeoutMs: 0,
})

console.log(`https://${sandbox.getHost(8000)}`)

Bind the server to 0.0.0.0, not 127.0.0.1. Inside the sandbox, localhost is the sandbox itself, and a port that is only bound there refuses the proxied request.

timeoutMs: 0 (timeout=0 in Python) is there because the SDK stays attached to a command for 60 seconds by default, background: true included. Without it the handle throws after 60 seconds — the server itself keeps running and the URL keeps working, but the handle you held is gone. See Run commands.

What the URL carries

The proxy in front of a sandbox URL forwards HTTPS and WebSocket only: no SSH, no port-forwarded database socket and no other protocol reaches the sandbox from outside. Outbound traffic is a separate question, governed by allowOut and denyOut below. Run shell work with commands or an interactive terminal instead.

Restrict outbound traffic

Pass allowInternetAccess: false on create to cut the sandbox off from the internet. It behaves exactly like denying 0.0.0.0/0 — including the rule that an allowOut entry still wins over that deny, so a sandbox created with both can reach the hosts you allowed.

import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base', { allowInternetAccess: false })

const result = await sandbox.commands.run('curl -sS https://example.com || true')
console.log(result.stdout)

For anything finer, use network.allowOut and network.denyOut (allow_out / deny_out in Python). Allowed entries always win over denied entries, so the usual shape is "deny everything, allow these". ALL_TRAFFIC is the 0.0.0.0/0 sentinel and is exported from both SDKs.

import { ALL_TRAFFIC, Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base', {
  network: {
    allowOut: ['api.github.com', '*.npmjs.org'],
    denyOut: [ALL_TRAFFIC],
  },
})
TypeScriptPythonDefaultWhat it does
allowInternetAccessallow_internet_accesstrueTop-level option on create. false is the same as denying 0.0.0.0/0
network.allowOutnetwork["allow_out"]unsetDestinations the sandbox may reach. Unset means all egress is allowed
network.denyOutnetwork["deny_out"]unsetDestinations the sandbox may not reach
network.rulesnetwork["rules"]unsetPer-host header transforms. Does not allow egress on its own
network.allowPublicTrafficnetwork["allow_public_traffic"]trueWhether sandbox URLs are reachable without authentication
network.maskRequestHostnetwork["mask_request_host"]${PORT}-sandboxid.sandbox.impello.aiThe Host the sandbox sees on proxied requests. ${PORT} is replaced with the port

The table covers the options this page documents. The Python network dict also accepts egress_proxy, which has no TypeScript equivalent; if you set it, read Update at runtime before you change the policy on a running sandbox.

An allowOut entry can be a CIDR block (8.8.8.8/32), a bare IP address (8.8.8.8) or a domain (example.com, *.example.com). denyOut takes CIDR blocks and IP addresses only — a domain name in a deny list is not supported.

Both lists also accept a callback instead of an array. It receives the all-traffic sentinel and the hosts you registered under rules, which is how you say "allow exactly the hosts I wrote rules for".

There is no published outbound IP range for sandboxes and neither SDK exposes one, so authenticate the calls a sandbox makes into your own services instead of allowlisting an address.

Per-host rules

rules maps a host to an ordered list of rules. A rule's transform.headers are injected into matching outbound HTTP and HTTPS requests, replacing any header of the same name, so the code in the sandbox never sees the credential.

Registering a host under rules does not allow egress to it. The host must also appear in allowOut.

import { ALL_TRAFFIC, Sandbox } from '@impello/sdk'

const token = process.env.UPSTREAM_TOKEN
if (!token) {
  throw new Error('UPSTREAM_TOKEN is not set')
}

const sandbox = await Sandbox.create('base', {
  network: {
    allowOut: ({ rules }) => [...rules.keys()],
    denyOut: [ALL_TRAFFIC],
    rules: {
      'api.example.com': [
        {
          transform: {
            headers: { Authorization: `Bearer ${token}` },
          },
        },
      ],
    },
  },
})

In TypeScript the callback receives { allTraffic, rules }, where rules is a Map. In Python it receives a context object with ctx.all_traffic and ctx.rules.

Restrict inbound traffic

Set network.allowPublicTraffic to false and the sandbox URLs stop answering unauthenticated callers. The create call returns the sandbox's traffic access token for the proxy in front of that one sandbox. Both SDKs expose it on the instance and type it as optional, so check it before reading it.

import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base', {
  network: { allowPublicTraffic: false },
})

console.log(sandbox.trafficAccessToken)

Neither SDK attaches the token to requests you build yourself, and neither one documents the form the proxy expects it in — header, query parameter or cookie. getHost(port) returns a host and nothing else, so it does not carry the token either. Until that form is published, read allowPublicTraffic: false as a switch that closes the URL rather than one that reopens it to callers holding the token.

secure is a different switch. It defaults to true and puts a token of its own on the sandbox's control channel — the one files, commands and pty use. Leaving secure at its default does nothing about a web server you start on port 8000; allowPublicTraffic is the option for that.

Update at runtime

updateNetwork changes the egress policy of a running sandbox without restarting it.

import { ALL_TRAFFIC, Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base')

await sandbox.updateNetwork({
  allowOut: ['api.github.com'],
  denyOut: [ALL_TRAFFIC],
})

The update takes the egress fields this page covers: allowOut, denyOut, rules and allowInternetAccess (allow_out, deny_out, rules, allow_internet_access in Python). It does not accept allowPublicTraffic; the update route carries no field for it, so set the inbound policy at create time.

Python's egress_proxy is part of the same replacement. An update that leaves it out stops tunneling and sends the sandbox's traffic out directly, even when you only meant to change the allow and deny lists, so repeat it in every update that should keep tunneling.

Both SDKs also expose it without an instance: Sandbox.updateNetwork(sandboxId, network) in TypeScript, Sandbox.update_network(sandbox_id, network) in Python.

Read the current policy

getInfo() returns the sandbox's network configuration as the server holds it, including whether internet access was explicitly set.

import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base')

const info = await sandbox.getInfo()
console.log(info.allowInternetAccess)
console.log(info.network?.allowOut)
console.log(info.network?.allowPublicTraffic)

Next: Pause, resume and snapshots.