MainSDK
Search⌘K
How-to

Restrict which hosts and IPs your app can reach

Allow only named hosts, IPs and CIDR ranges, roll the rule out from observe to enforce, and export it as Kubernetes or AWS security group rules.

Building for 0.3

Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.

Stability The promise an interface ships with.

  • Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
  • Beta Usable in production; may change before it becomes stable.
  • Experimental May change or go away; needs an explicit opt-in in main.yaml.
Stability levels
Updated 2026-10-28

Your app will reach only the hosts, IPs and ranges you name. Every other outbound call is blocked in process and reported with the file and line that made it. You will write an allowlist, run it in observe mode to see what the app really calls, switch it to enforce, and export the same list as a firewall rule for a second, independent wall. It takes about 15 minutes.

You need

  • A MainSDK app with a main.yaml (see Thirty-minute path).
  • The list of outside services your app calls. You don't need the hosts of your drivers: each driver declares its own.

1. Start in observe mode

main.yaml
network:
  allow:
    - api.partner.com      # exactly this host, not its subdomains
    - "*.acme.dev"         # subdomains of acme.dev only, not acme.dev itself
    - 10.20.0.0/16         # an IPv4 range, such as your VPC
    - 8.8.8.8              # one IP
    - "2001:db8::/32"      # an IPv6 range
  default: deny
  mode: observe            # observe, then warn, then enforce

In observe, every call still succeeds, and each call that enforce would block is written to the decision log as a would-block.

2. Call outside APIs through the SDK

lib/rates.ts
import { http } from "mainsdk";

export async function loadRates() {
  const res = await http.fetch("https://api.partner.com/v1/rates");
  return res.json();
}

http.fetch is the one client every driver and your own code use, so every outbound call passes the same check. mainsdk check fails on raw sockets, other HTTP libraries and a bare fetch outside the SDK (MAIN-NET-0001), so nothing slips past it.

The helpers http.get, http.post, http.json, http.events and http.websocket cover the other common calls, and http.assert checks a database URL or git remote before a client connects. Each one is checked the same way. Programs such as git or curl run through system.run, which checks every URL in their arguments.

What is covered

Kind of callHow to make itChecked by
Web requests (fetch, get, post, json)http.fetch, http.get, http.post, http.jsonnetwork.allow at call time; json also refuses non-2xx answers (MAIN-NET-0008)
WebSocketshttp.websocket(url)the host, before the socket opens
Server-sent eventshttp.events(url)network.allow at call time
Database URLs and git remoteshttp.assert(url), then connectthe host, before the client connects
Programs that reach the network (git, curl, wget)system.run(cmd, args), declared under permissions.processesevery URL in the arguments
Local filesdisk (declared under permissions.disk)not the network; see Declare what your app uses
User uploadsfilesthe upload driver's own hosts, which the driver declares

Local folders are not network calls, so network.allow does not govern them. Read Declare what your app uses for folders, facts and programs.

3. Find every host your code uses

bash
npx mainsdk discover

discover reads your source and lists every host the code connects to, with the file and line. A host that network.allow does not list is marked MISSING, and the output ends with the lines to add. Add the hosts you expect; remove the code that calls the ones you don't.

npx mainsdk info --json shows the same list under uses, next to the destinations each driver declares, with each would-block's rule and calling file and line. npx mainsdk discover --json prints the full manifest for scripts and agents.

To keep the list with the build, write it to a file:

bash
npx mainsdk discover --out build/manifest.json

Run it in CI or in your build step. The manifest records every host, folder, fact and program the app uses, so a reviewer or a platform rule can check it without running the app.

mainsdk check fails with MAIN-NET-0007 for any host the code uses that network.allow doesn't list, so run discovery again after you add a call.

4. Enforce

main.yaml
network:
  mode: enforce

A call to any other host now fails with MAIN-NET-0002. The SDK also checks:

  • DNS answers. A declared name that resolves to a private range you didn't list, such as 127.0.0.1, is blocked as rebinding.
  • Redirects. A redirect from an allowed host to an undeclared one is refused with MAIN-NET-0003.

The decision is made in your app, with no network round trip. If the policy layer fails, the last good list keeps applying (see Fail static).

5. Add a second wall at the platform

An in-app check can't stop code that never loads the SDK, such as a native module. Write the same list into your platform's own egress controls:

bash
npx mainsdk network export --to k8s          # Kubernetes NetworkPolicy
npx mainsdk network export --to aws-sg       # AWS security group egress rules

Exports for Vercel and Cloudflare are planned. For the list above, --to k8s writes egress rules for 10.20.0.0/16 and 8.8.8.8/32. Platform rules match IPs, so names are resolved when you export; re-export when a provider's IPs change. Add --discovered to include hosts the code uses that network.allow does not list yet.

Verify

bash
npx mainsdk check --env prod

A pass exits 0 with no MAIN-NET finding. In a test, call an undeclared host and expect MAIN-NET-0002.

If it fails

CodeCauseFix
MAIN-NET-0001Code outside a driver imports node:net, axios or another client, calls fetch directly, opens new WebSocket or EventSource, or runs git, curl or wget as a child process.Call through http or a driver; run programs with system.run.
MAIN-NET-0002A call went to a host that is neither in network.allow nor declared by a driver.Add the host if you expect the call; otherwise remove the code.
MAIN-NET-0003An allowed host redirected to an undeclared one.Declare the target host, or call it directly.
MAIN-NET-0004A third-party API offers less than TLS 1.2.Ask the provider to upgrade; meanwhile add an exception with an expiry.
MAIN-NET-0005An entry in network.allow is not an IP, a CIDR range, an exact host or a *.domain.Fix the entry; the error names it.
MAIN-NET-0006A request was redirected more times than the SDK follows.Call the final URL directly, or fix the redirect loop.
MAIN-NET-0007mainsdk check found a host the code uses that network.allow doesn't list.Add the host, or remove the call. npx mainsdk discover prints the lines.
MAIN-NET-0008http.json or http.events got a non-2xx answer, or a body that is not JSON.Check the URL, the credentials and the service status, or read the response with http.fetch.

For agents

text
Goal: restrict this app's outbound network to the hosts it needs.
Read: docs/specs/network.md, this page, main.yaml.
Touch only: main.yaml, files that call outside APIs.
Do: add network.allow with mode: observe; replace raw fetch, axios, sockets and child-process curl or git with
http helpers or system.run; run npx mainsdk discover and add only the expected hosts; ask the owner before switching to enforce.
Run npx mainsdk discover again after every new outbound call.
Verify: npx mainsdk check --json --agent
Never add a wildcard that covers a whole provider or "*" to make a call pass.

What comes later

  • Across many apps. Fleet pushes one signed allowlist to every app, can tighten an app's list but never loosen it, and keeps a live map of every destination (phase 4, specs fleet-control and fleet-observe).
  • Inbound traffic. Secure headers, CSRF and CORS are in inbound protection, and rate limits in limits. This page is outbound only.

Next

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet