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],
},
})| TypeScript | Python | Default | What it does |
|---|---|---|---|
allowInternetAccess | allow_internet_access | true | Top-level option on create. false is the same as denying 0.0.0.0/0 |
network.allowOut | network["allow_out"] | unset | Destinations the sandbox may reach. Unset means all egress is allowed |
network.denyOut | network["deny_out"] | unset | Destinations the sandbox may not reach |
network.rules | network["rules"] | unset | Per-host header transforms. Does not allow egress on its own |
network.allowPublicTraffic | network["allow_public_traffic"] | true | Whether sandbox URLs are reachable without authentication |
network.maskRequestHost | network["mask_request_host"] | ${PORT}-sandboxid.sandbox.impello.ai | The 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.