---
title: "Declare what your app uses"
description: "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."
section: "guides"
status: "Building for 0.3"
spec: "app-permissions"
canonical: "https://mainsdk.dev/docs/guides/declare-what-your-app-uses"
updated: "2026-10-28"
---

# 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: Building for 0.3 · Canonical: https://mainsdk.dev/docs/guides/declare-what-your-app-uses · Updated: 2026-10-28

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.

> **Note:** `app-permissions` is being built for 0.3 (spec `app-permissions`). Discovery and the `permissions` block run from the repository and are not published to npm yet. Enforcement stays free on every plan.

## You need

- A MainSDK app with a `main.yaml` (see [Thirty-minute path](https://mainsdk.dev/docs/getting-started/thirty-minutes)).
- 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](https://mainsdk.dev/docs/guides/restrict-network).

## 1. Run discovery

```bash
npx mainsdk discover
```

Discovery 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 `hostname` or `platform`.
- **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.

```yaml title="main.yaml"
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 run
```

Start 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`).

```ts title="lib/cache.ts"
import { disk } from "mainsdk";

export async function saveRates(rates: unknown) {
  await disk.write(".cache/rates.json", JSON.stringify(rates));   // .cache is under disk.write
}
```

```ts title="lib/build-info.ts"
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](https://mainsdk.dev/docs/interfaces/disk) and [Server facts and programs](https://mainsdk.dev/docs/interfaces/system).

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](https://mainsdk.dev/docs/guides/restrict-network).

## 4. See what the app uses

```bash
npx mainsdk info            # human-readable
npx mainsdk info --json     # for scripts, agents and reviews
```

`info` 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:

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

The 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

```bash
npx mainsdk check --json --agent
```

The 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

```text
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](https://mainsdk.dev/docs/guides/restrict-network) 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](https://mainsdk.dev/docs/guides/restrict-network).

## Next

- [Restrict which hosts and IPs your app can reach](https://mainsdk.dev/docs/guides/restrict-network)
- [Local files](https://mainsdk.dev/docs/interfaces/disk) and [Server facts and programs](https://mainsdk.dev/docs/interfaces/system), the interface references
- [See every call, decision and change your app makes](https://mainsdk.dev/docs/guides/see-what-your-app-does)
