# Custom domains, TCP ports, dedicated addresses and private networks Four ways to connect sandboxes to the rest of your world. They are for paid accounts: an account that has not added credit gets `payment_required` (402). A custom domain or a TCP port also needs the sandbox it serves to be a paid one. - **Custom domain:** a sandbox's web port at your own hostname, with HTTPS. - **TCP port:** a public `address:port` for anything that is not HTTP, such as Postgres, Redis, MQTT or a game server. - **Dedicated outbound address:** every sandbox of your account sends from one address of its own, so you can allow-list it. - **Private network:** a WireGuard tunnel from your own network, in any cloud or on your premises, into your sandboxes. None of them belongs to one sandbox alone, so each is its own product in the CLI, the SDKs and MCP: `runtime domain`, `runtime port`, `runtime address` and `runtime tunnel`; `runtime.domains`, `runtime.ports`, `runtime.addresses` and `runtime.tunnel`; `runtime_domain_*`, `runtime_port_*`, `runtime_address_*` and `runtime_tunnel_*`. They are not billed yet; the [pricing page](./pricing) will list their prices before they are. ## Custom domains ```bash no-run runtime domain add app.example.com 3000 ``` The answer lists two DNS records to set at your DNS provider: | Record | Name | Value | What it does | | ------ | ------------------------------------ | ------------------------- | ------------------------- | | TXT | `_runtime-challenge.app.example.com` | `runtime-verify=` | Proves the name is yours | | CNAME | `app.example.com` | `domains.runtimehost.com` | Sends visitors to Runtime | A name with no subdomain (`example.com`) cannot have a CNAME; set an A record to the address the answer gives instead. Then check: ```bash no-run runtime domain verify app.example.com ``` The name goes live when the TXT record matches. The first visit gets its certificate from Let's Encrypt, which takes a few seconds. Your server must listen on `0.0.0.0` or `localhost` inside the sandbox, as for a [preview link](./sandbox-environment), and be started with `spawn`, which keeps it running. WebSockets work, and a paused sandbox is woken by a visit, like a preview. - **Ownership is the TXT record.** A CNAME pointing at Runtime proves nothing, so a record someone forgot to delete cannot be used to take their name. Each claim has its own token. - **The DNS owner wins.** If another account proves the same name later, it takes the name over and your claim ends. - **To move the name to another sandbox or port,** run `add` again with the new target. To stop serving it, `runtime domain rm app.example.com`. - **Limits:** 50 domains per account, 20 added a day. Certificates are issued for proved names only. - **Reports** reach Runtime through the `X-Runtime-Report` header on every answer and the abuse address, as for previews. Runtime can take a name down; it then stays down for every account. ## TCP ports ```bash no-run runtime port open 5432 # 203.0.113.10:23456 psql "postgres://app@203.0.113.10:23456/app?sslmode=require" ``` A TCP port carries raw TCP from a public address and port to a port inside the sandbox. Opening the same sandbox and port again returns the same public port. - **Anyone who knows the address can connect.** Use your service's own authentication and TLS. - **Only TCP is carried.** UDP (most game servers' own traffic, QUIC) is not. - **Limits:** 5 ports per sandbox, 20 per account and 50 opened a day. Each sandbox's ports share 256 open connections, 50 new connections a second, and 32 connections from any one client address. Bandwidth and the daily byte quota are the same as the sandbox's outbound ones. - **A closed port rests for a day** before anyone else can be given it, so a client still pointed at it never reaches someone else's service. - **A paused sandbox is woken** by a connection, and its connections end when it stops, pauses or is deleted. - `runtime port ls` lists them; `runtime port close ` closes one. ## Dedicated outbound addresses ```bash no-run runtime address reserve # 203.0.113.50 ``` From then on every sandbox of your account sends from that address, on every port, so a database, an API provider or a firewall can admit exactly you. No other account sends from it while you hold it. - One IPv4 address per account, and one IPv6 with `--ipv6`. - `runtime address release ` gives it back, and your sandboxes send from the shared addresses again at once. A released address rests for 30 days before any other account gets it. Remove it from your allow-lists first. - Addresses are added to Runtime as accounts need them. If none is free, the answer is `no_capacity` (503): write to support. ## Private networks A WireGuard tunnel from a machine or router on your network into your sandboxes. Every sandbox gets an address in the tunnel's subnet, and your machines reach any port of it there. ```bash no-run runtime tunnel create # subnet 10.250.0.0/16 by default runtime tunnel peer add office --route 10.0.0.0/16 # writes runtime.conf sudo wg-quick up ./runtime.conf runtime tunnel get # each sandbox's address psql -h 10.250.0.32 app ``` `runtime tunnel peer add` makes the WireGuard key pair on your machine; the private key goes only into `runtime.conf`, which only you can read. Any WireGuard client works with the file: `wg-quick` on Linux and macOS, the WireGuard apps on Windows, macOS, iOS and Android, or your router. To use a key you already have, pass `--public-key`. - **Choose a subnet your network does not use,** from `/16` to `/24`, when you create the tunnel: `--subnet 172.30.0.0/16`. - **`--route`** names your own ranges behind the peer. List them when your sandboxes will reach your network through the peer. - **What a peer reaches:** your own account's sandboxes, at their tunnel addresses, on any port except Runtime's own relays inside the sandbox (10800, 10802 and 10853). Nothing else: not another account's sandboxes, not Runtime's servers, not the internet. Only TCP is carried. - **Rotate a key** with `runtime tunnel peer rotate `: the old key stops working within seconds, and connections opened with it end. `runtime tunnel peer rm ` removes a peer the same way. - **Limits:** one tunnel per account, 16 peers. A paused sandbox is woken by a connection. From the SDKs: ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); await runtime.tunnel.create(); const office = await runtime.tunnel.addPeer({ name: "office", routes: ["10.0.0.0/16"] }); // office.config is the wg-quick file; with no publicKey given, it holds the // private key Runtime generated for you, shown once. ``` ```python check runtime.tunnel.create() office = runtime.tunnel.add_peer("office", routes=["10.0.0.0/16"]) ``` ## Errors | Code | Status | Meaning | | --------------------- | -----: | --------------------------------------------------------------- | | `payment_required` | 402 | The account has not added credit, or the sandbox is a trial one | | `network_not_allowed` | 403 | Runtime turned network features off for the account | | `quota_exceeded` | 409 | A limit above was reached | | `rate_limited` | 429 | Too many added today | | `no_capacity` | 503 | No public port or dedicated address is free just now | | `network_unavailable` | 503 | The feature is not switched on in this region yet |