Declare what your app uses
List the hosts, folders, server facts and programs your app uses, declare them in main.yaml like a phone's app privacy page, and keep the list in the build.
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.
Your app should say what it touches before it runs, the way a phone's app privacy page lists what an app may reach. You will run discovery to list every host, folder, server fact and program the code uses. You will declare them in main.yaml, use disk and system in place of raw file, OS and process calls, check the result with mainsdk info, and write the list into your build. It takes about 20 minutes for an app that already calls outside services.
You need
- A MainSDK app with a
main.yaml(see Thirty-minute path). - Server-side code that reads files, reads server facts or runs programs. A browser-only app needs only the network part, which is in Restrict which hosts and IPs your app can reach.
1. Run discovery
npx mainsdk discoverDiscovery reads your source and main.yaml and lists four things, each with the file and line that uses it:
- network: every host the code connects to, with how it connects (
http,websocket,event-stream,database,git). - disk: every folder the code reads or writes.
- system: every server fact it reads, such as
hostnameorplatform. - processes: every program it runs, such as
git.
Each item is marked declared or MISSING. The output ends with the main.yaml lines that would declare what is missing. Treat it as a first draft: remove the code that uses something you don't want the app to use.
2. Declare the permissions
Add the lines discovery printed, or write them by hand. Paths are relative to the app folder.
network:
allow:
- api.partner.com
- github.com
permissions:
mode: observe # observe first, then enforce
disk:
read: [content] # folders the app may read
write: [.cache] # folders the app may write; these are readable too
system: [hostname, platform] # facts the app may read
processes: [git] # programs the app may runStart with mode: observe. Calls succeed, and each use that enforce would refuse is recorded with its file and line. When the list is right, switch to mode: enforce. An undeclared use then fails with MAIN-PERM-0002 (403), and the SDK refuses it before it happens.
Declare the narrowest thing you can. A folder such as . or a program such as sh gives the app far more reach than it needs.
3. Use disk and system in place of raw calls
Replace direct node:fs, node:os and node:child_process calls. mainsdk check fails on those (MAIN-PERM-0001).
import { disk } from "mainsdk";
export async function saveRates(rates: unknown) {
await disk.write(".cache/rates.json", JSON.stringify(rates)); // .cache is under disk.write
}import { system } from "mainsdk";
export async function currentCommit() {
const { stdout } = await system.run("git", ["rev-parse", "HEAD"]); // git is under processes
return stdout.trim();
}
export function host() {
return system.hostname(); // hostname is under system
}system.run runs the program without a shell, and checks every URL in its arguments against network.allow. The rest of the call shapes are in Local files and Server facts and programs.
For a web request, keep using http. It is the network side of the same model, covered by Restrict which hosts and IPs your app can reach.
4. See what the app uses
npx mainsdk info # human-readable
npx mainsdk info --json # for scripts, agents and reviewsinfo shows a uses section next to what is declared: the hosts, the folders, the facts and the programs the code uses, and which of them permissions does not yet declare. Each enforced or observed decision names its calling file and line, so you can see what the app asked for in production as well as in the code.
5. Write the list into the build
Keep a record of what the app uses with each build:
npx mainsdk discover --out build/manifest.jsonThe manifest is the discovery result as JSON: every host, folder, fact, program and environment variable the code reads, with file and line, and what is undeclared. Store it with the build artifact so that a reviewer, a security team or a platform rule can read what the app uses without running it. Add it to CI next to mainsdk check, and diff it on each pull request to catch a new destination or folder.
Discovery lists an environment variable the code reads, but it never prints the variable's value.
Verify
npx mainsdk check --json --agentThe check passes with no MAIN-PERM or MAIN-NET-0007 finding. npx mainsdk info --json lists nothing in uses that you did not mean to add. Switch mode to enforce once the observe run is clean, and run the check again.
If it fails
| Code | Cause | Fix |
|---|---|---|
MAIN-PERM-0001 | Code imports node:fs, node:os or node:child_process outside a driver. | Use disk for files and system for facts and programs. |
MAIN-PERM-0002 | In enforce mode, the app used a folder, fact or program that permissions doesn't declare (403). | Declare it if the app needs it; otherwise remove the code that asks. |
MAIN-PERM-0003 | The check found a folder, fact or program the code uses that permissions doesn't declare. | Run npx mainsdk discover and add the lines it prints. |
MAIN-PERM-0004 | A name under permissions.system is not a known fact. | Use user, hostname, platform or network-interfaces. |
MAIN-PERM-0005 | A declared program could not start, for example because it is not installed on the server. | Install it on the server, or check its name. |
MAIN-NET-0007 | The code connects to a host that network.allow doesn't list. | Add the host, or remove the call. |
For agents
Goal: declare everything this app uses on the server and in the network, and stop using raw file, OS and process calls.
Read: docs/specs/app-permissions.md, this page, main.yaml.
Touch only: main.yaml, code that imports node:fs, node:os, node:child_process or calls outside hosts.
Do: run npx mainsdk discover; add the lines it prints with mode: observe; replace node:fs with disk, node:os with system,
child_process with system.run; run npx mainsdk discover again after every new call; ask the owner before switching to enforce.
Verify: npx mainsdk check --json --agent and npx mainsdk info --json
Never declare "." for disk, a shell such as sh or bash for processes, or a wildcard host to make a finding pass.What comes later
- Across many apps. Fleet can hold one organisation-wide list for every app (see Restrict which hosts and IPs your app can reach for the network side).
- Platform rules. The same network list exports as platform egress rules, as in Restrict which hosts and IPs your app can reach.
Next
Was this page helpful?