# Reference

The quick contract for the fuast CLI and HTTP surface. For the code-level API see [capsule API](https://fuast.com/docs/capsule-api.md) and [database](https://fuast.com/docs/database.md).

## CLI

Run `bunx @revnu/fuast <command>`, or `fuast <command>` when it is installed. Every command that prints data accepts `--json`. `fuast <command> --help` (or `fuast help <command>`) prints one command's usage and options.

### Targeting an app

Inspection commands (`inspect`, `run`, `db`, `logs`, `storage`) pick their target like this:

1. `--port <n>`: the `fuast dev` server on that port, even if a slug is also given.
2. A slug or app URL argument: the deployed app.
3. Neither: `fuast dev` on port 3000 (or `FUAST_PORT_DEV`).

With a slug, the CLI talks only to the platform (`FUAST_API`), which forwards to the app, so your token never goes to the app's own origin. With an app URL, pass `--inspect-token`. They do not read `fuast.json`. To inspect a deployed app, pass its slug (the `app` value in `fuast.json`) or its URL.

An [unclaimed preview](#deploy-without-an-account) is inspected the same way, by the slug `deploy` printed, on the machine that deployed it. The CLI finds the preview's edit token in `~/.fuast/previews.json` and sends it instead of an account token. `inspect`, `logs`, `db list|dump|export`, `jobs list`, `storage list`, and `run` work. Commands that change an app outside its handlers (`db import`, `storage put|delete`, `jobs cancel`, `run schedule`, `share`, `domains`) need an owner: claim the app first.

Platform commands (`share`, `users`, `domains`, `deploy archive|restore|terminate`) take a slug argument or fall back to `fuast.json` in the current directory. `deploy` reads `fuast.json` from the capsule directory.

### Create

```txt
fuast new <dir> [--template todo|guestbook|signature|approvals|live-demo] [--no-git]
fuast create <dir> ...                 alias of new
fuast init [--template ...] [--no-git] scaffold into the current directory
```

`new` writes the template, `fuast-env.d.ts`, `AGENTS.md`, `CLAUDE.md`, `.gitignore`, and `tsconfig.json`, then creates a git repository with a first commit unless you are inside one or pass `--no-git`. `init` refuses when the directory has files other than hidden files, `README*`, `LICENSE*`, `AGENTS.md`, and `CLAUDE.md`, and appends fuast's lines to existing `AGENTS.md`, `CLAUDE.md`, and `.gitignore`.

### Develop

```txt
fuast dev [dir] [--port 3000] [--host 127.0.0.1] [--reset] [--no-typecheck] [--allow-host <name>]...
fuast run-many [dir] [--count 20] [--base-port 4000]
fuast check [dir] [--no-typecheck] [--json]
fuast build [dir] [--out artifact.json] [--no-typecheck] [--json]
fuast types [dir]
fuast templates
```

- `dev` runs the app locally with hot reload for server and client, the same WebSocket protocol, database engine, limits, and inspection routes as hosted apps. Data and files persist in `.fuast/dev`. `--reset` wipes them. `--host 0.0.0.0` exposes the server, including its open inspection routes, to your network. `--allow-host <name>` accepts another `Host` header (for a tunnel or a LAN name); repeat it per name. `--no-typecheck` turns off the type check after each save.
- `run-many` starts `--count` independent dev servers of the capsule on consecutive ports from `--base-port`, each with its own data, and prints their URLs. Ctrl-C stops them all.
- `check` builds the capsule, runs the TypeScript checker, and verifies the contract (imports, `capsule()` keys, schema, cron expressions, favicon). Exit code 1 on any error. Run it before every deploy.
- `check --no-typecheck` skips only the type check. `check --json` prints `{ ok: true, manifest, ... }` or `{ ok: false, error, code, diagnostics }`. Each diagnostic has `file`, `line`, `column`, `code`, `category`, `message`, and a source `frame` when the file is known.
- `build` builds without deploying and prints bundle sizes. `--out` writes one JSON artifact with the manifest, bundles, CSS, and favicon.
- `types` rewrites `fuast-env.d.ts` (and `tsconfig.json` if missing) from the installed CLI. Run it after upgrading.
- `templates` lists the starter templates.

Local identities: `?fuast_user=<name>` and `?fuast_guest=1` in the URL, or `fuast auth as <name>`. See [auth](https://fuast.com/docs/auth.md#local-identities).

### Deploy

```txt
fuast deploy [dir] [--name "My App"] [--delete-table TABLE=COUNT]... [--no-typecheck] [--no-unclaimed] [--claim | --new] [--json]
fuast claim [dir]
fuast status [dir]
fuast deploy list
fuast deploy rollback <deployId>
fuast deploy archive [slug|deployId]
fuast deploy restore [slug|deployId]
fuast deploy terminate [slug|deployId] --yes
```

- `deploy` uploads the capsule source and `.env.fuast.server`. The platform type checks it (errors block the deploy unless `--no-typecheck`), builds it, describes it in a sandbox, applies schema changes, and makes it live.
- Logged in (`fuast login`, or `FUAST_TOKEN` in CI), it deploys to your account and prints the app URL. The first deploy creates the app and writes `fuast.json` (`{"app": "<slug>"}`). Commit it.
- Not logged in, it deploys an unclaimed preview and prints a preview link, a claim link, and when the preview is deleted. See [deploy without an account](#deploy-without-an-account).
- Before uploading, `deploy` runs the same build, contract check, and type check as `fuast check` locally, and stops on the first error.
- `--name` sets the app's display name on first deploy. Without it, the CLI uses `capsule({ name })`, then the directory name.
- `deploy list` prints each app you own with its status, live deploy id, and URL.
- `--delete-table TABLE=COUNT` confirms dropping a populated table removed from the schema. Repeat it per table. See [schema changes](https://fuast.com/docs/database.md#schema-changes).
- A rejected destructive change returns code `DESTRUCTIVE_SCHEMA_CHANGE` (HTTP 409) with `tables: [{ table, rowCount }]`.
- Every deploy is an immutable record with its source snapshot. `rollback` makes an earlier deploy live again with the current data. It fails if that deploy's schema cannot accept the current rows.
- `archive` stops serving the app until `restore`. `terminate` stops it permanently. Both keep its data, logs, and records.

### Deploy without an account

`fuast deploy` works before anyone has a fuast account. It puts the app online as an **unclaimed preview** and prints two links:

- The **preview link** (`https://preview-<random>.<apps domain>/?fuast_preview=…`) shows the app, after a fuast page that says what a preview is. Anyone who has the link can look. It cannot claim the app.
- The **claim link** (`https://fuast.com/claim/…`) makes the app someone's. They open it, sign in with a verified account, and choose Claim. It works once.

What the CLI does:

- It saves `{ api, slug, editToken, expiresAt, url, claimUrl }` in `~/.fuast/previews.json` (mode 600), under the real path of the project folder. The edit token lets this machine redeploy and inspect the preview. The CLI never prints it and sends it only to the platform that issued it. Nothing is written to the project folder: `fuast.json` is for apps that have an owner.
- It never reads a credential from the project folder. A repository can ship any file, so a `.fuast/unclaimed.json` in the working tree is ignored, with the warning `ignoring .fuast/unclaimed.json in this folder`. A folder this machine has no record for gets a new preview. Copying or moving the folder does the same.
- `fuast deploy` again redeploys the same preview. The links and the expiry stay the same.
- `fuast claim` prints the claim link, the preview link, and the expiry again. `fuast status` prints the platform, the account, and what the folder is: not deployed, an unclaimed preview, a preview someone claimed, or an app linked in `fuast.json`. `fuast whoami` and `fuast auth status` mention the preview too.
- After the claim, a deploy without a login stops with `This app was claimed by <email> and now deploys with that account. Run \`fuast login\`, then \`fuast deploy\` again`. Once you are logged in as an account that may deploy the claimed app, `deploy` updates it, writes `fuast.json`, and forgets the preview record.
- A logged-in `deploy` in a folder whose preview is still unclaimed does not claim it. It stops with code `preview_unclaimed` (`--json`: `{ ok: false, code, slug, claimUrl, choices }`) and asks you to choose: `fuast deploy --claim` claims this machine's preview for the logged-in account, keeps its data, and deploys to it; `fuast deploy --new` leaves the preview alone and deploys the folder as a new app.
- When the preview has expired, `deploy` says so and creates a new one.
- `--no-unclaimed`, or `FUAST_UNCLAIMED=0` in the environment, turns all of this off: without a login, deploy stops with `Not logged in to <platform>. Run \`fuast login\`, or set FUAST_TOKEN for CI.`
- A folder with `fuast.json` is linked to an owned app and always needs a login.

What a preview is:

| | Unclaimed preview | Claimed, restricted | Trusted |
| --- | --- | --- | --- |
| Address | Random `preview-…` slug | The same, or one the claimer picks | The same |
| Who can open it | Anyone with the preview link, after the fuast page | Private to the owner until shared | By sharing mode |
| Identity | Guests only, no sign-in | Accounts and guests, by sharing mode | The same |
| `ctx.email`, `ctx.team`, `ctx.workspace` | Fail with code `unclaimed` | Fail with code `restricted` | Work |
| Schedules and jobs | Registered, not run | Registered, not run | Run |
| Limits | [Preview limits](https://fuast.com/docs/limits.md#unclaimed-previews) | Normal limits | Normal limits |
| Lifetime | 72 hours from the first deploy | Until deleted | Until deleted |

Claiming keeps the app's rows and files and cancels every job that was queued while it was a preview. The preview link, the claim link, and the edit token stop working.

A claimed app is **restricted** until its owner or a workspace admin trusts it with the workspace: the box under Options on the claim page, "Trust this app with my workspace" in the dashboard, or `fuast trust`. A deploy never does that. `deploy` to a restricted app works, prints that workspace features are off and the `fuast trust <slug>` command that turns them on, and `--json` carries `restricted: true`, `restrictedNotice`, and `trust: { command, dashboard }`. See [share](#share) for the commands and [security](https://fuast.com/docs/security.md#restricted-apps) for the reasons behind each rule.

`preview-…` addresses are reserved for unclaimed previews. An account cannot create an app or an alias with such a name.

### Share

```txt
fuast share [slug] --visibility private|workspace|public [--emails a@x.com,b@y.com]
fuast users list [slug]
fuast users approve <email> [slug]
fuast users deny <email> [slug]
fuast users remove <email> [slug]
fuast trust [slug]
fuast untrust [slug]
fuast list
```

See [teams](https://fuast.com/docs/teams.md#sharing). `list` prints every app you can see with its slug, URL, owner, and sharing mode.

- `trust` grants an app your workspace: it may send email, list the workspace's members, read and write team tables (every app in the workspace shares them), and run its schedules and delayed jobs. An app claimed from an [unclaimed preview](#deploy-without-an-account) starts without that, and deploying to it never changes it: `trust`, the dashboard button, or the box on the claim page does. It needs the login of the app's owner or a workspace admin; deploy tokens (`--app`) and preview edit tokens cannot do it. Trust an app when you know who wrote its code.
- `untrust` takes it away again, from any app. The app keeps working with its own data; email, team tables, and the member list are refused with code `restricted`, and its schedules and jobs stop until it is trusted.
- `fuast inspect` and `fuast status` say when an app is restricted.

### Account and tokens

```txt
fuast login [--token <token>] [--email <email> --password <password>]
fuast logout
fuast whoami
fuast auth status
fuast auth as <name>          local dev identity
fuast auth reset
fuast token create --name <name> [--app <slug>]
fuast token list
fuast token revoke <id>
```

- `login` prints a short code and opens a browser approval page. A person signs in, checks the code, and approves. The CLI polls for up to 10 minutes, then saves the session in `~/.fuast/config.json`. Set `BROWSER=none` to print the URL without opening a browser. `--token` checks an existing token with the platform and saves it; a rejected token is not saved. `--email` with `--password` (or `FUAST_PASSWORD`) logs in without a browser, for test platforms.
- `auth status` prints the platform, the logged-in account, and the local dev identity. `whoami` prints the account and exits 1 when there is none. Both say so when the current folder has an unclaimed preview.
- `token create` prints a token once. Without `--app` it is a personal token that can do anything you can. With `--app` it can only deploy and inspect that app. Use it as `FUAST_TOKEN` in CI.

### Inspect and operate

```txt
fuast inspect [slug|url] [--port 3000] [--usage] [--query <name>] [--inspect-token <token>] [--json]
fuast run <name> [arg...] [slug|url] [--port 3000] [--as <name> | --guest | --anon] [--json]
fuast run schedule <name> [slug|url] [--port 3000] [--json]
fuast jobs list [slug] [--port 3000] [--status <status>] [--limit 100]
fuast jobs cancel <jobId> [slug] [--port 3000]
fuast db list [slug] [--port 3000]
fuast db dump [slug] [--port 3000] [--table <name>]
fuast db export [slug] [--port 3000] [--out backup.json]
fuast db import <file> [slug] [--port 3000]
fuast logs [slug] [--port 3000] [--follow] [--limit 100]
fuast storage list [slug] [--port 3000] [--prefix <p>] [--limit <n>]
fuast storage get <key> [slug] [--port 3000] [--out <file>]
fuast storage put <file> [slug] [--port 3000] [--public] [--content-type <type>]
fuast storage delete <key> [slug] [--port 3000]
fuast domains add <host> [slug]
fuast domains list [slug]
fuast domains check <host> [slug]
fuast domains remove <host> [slug]
```

- `run` calls one query, mutation, or action and prints the result as JSON. Each argument is one JSON value (`'"milk"'`, `3`, `'{"text":"milk"}'`); a last argument that is not JSON is the slug or URL. The kind comes from the app's manifest. Locally it runs as your dev identity (`fuast auth as`), or as `--as <name>`, `--guest`, or `--anon` for one call. Against a slug it runs as your logged-in account with the same sharing rules and quotas as the browser. `--json` prints `{ ok, kind, name, result, ms, logs, metrics }` with the call's log entries and read metrics. A handler error exits with code 1. See [test and debug](https://fuast.com/docs/testing.md#call-handlers-headlessly).
- `run schedule <name>` runs a cron schedule now as the system identity; its next run time does not change. Hosted, it needs the app owner or a workspace admin.
- `jobs list` prints durable jobs (`ctx.scheduler`): pending and running ones by due time, then recent finished ones, with attempts and errors. `--status pending|running|done|failed|canceled` filters. `jobs cancel <jobId>` cancels a pending job and exits 1 when it is no longer pending. See [jobs](https://fuast.com/docs/jobs.md).
- `inspect --query <name>` prints the database read metrics recorded for that query, grouped by run: totals per run (rows read, rows returned, bytes, index scans) and each run's database calls with their index and full-scan flag.
- `inspect` prints the app name, URL, deploy id, sharing, handlers by kind, schema with indexes and row counts, endpoints, schedules with next run times, tools, and database read metrics. `--usage` adds daily counters and stored totals.
- `db list` prints table names and row counts. `db dump` prints rows, bounded by the 1000-row return limit. `db export` walks every table in pages and writes a complete JSON backup atomically. `db import` loads a backup into empty tables (owner only).
- `logs` prints the newest 100 entries (`--limit` changes it). `--follow` keeps polling and prints new entries as they arrive.
- Inspection commands send your fuast token only to the platform API origin. For an app served from its own subdomain, pass `--inspect-token <token>` (or set `FUAST_INSPECT_TOKEN`) with a token from `fuast token create --name inspect --app <slug>`.
- `domains add app.example.com` connects a custom domain. It prints the two DNS records to create (a `TXT` record at `_fuast.app.example.com` and a `CNAME` to the platform), and fuast checks them in the background until the domain is live. `domains check <host>` checks now and exits 1 until the domain is active. `ls` and `rm` are aliases of `list` and `remove`. See [custom domains](https://fuast.com/docs/domains.md).
- `domains add my-tool` (a name without a dot) adds `my-tool.<apps domain>` as an extra hostname for the app.

## Inspection routes

`fuast dev` and hosted apps serve the same `GET` routes with the same JSON. The CLI reads them, and you can `curl` them directly.

| Route | Returns |
| --- | --- |
| `/__fuast/manifest` | Name, deploy id, URL, `runtimeVersion`, `artifactHash`, `inspectPolicy`, domains, handlers by kind, schema, index state (`database.indexes`, `failedIndexes`, `fullScanWarning`), endpoints, schedules, tools, auth flags, favicon, limits, read metrics. Add `?query=<name>` for `queryExecution` (what `inspect --query` prints). |
| `/__fuast/usage` | `limits`, `state` (`stateRows`, `stateBytes`), `usage` (today's `requests`, `mutations`, `email`). |
| `/__fuast/url` | `{ basePath, url }`. |
| `/__fuast/db/tables` | Table names, row counts, index state. |
| `/__fuast/db` | Bounded row dump per table, with `truncated`. |
| `/__fuast/db/export` | Backup metadata. Add `?table=<name>&schemaHash=<hash>&cursor=<c>` for pages. |
| `/__fuast/logs` | Recent log entries `{ at, level, message, data, source, handler }`. |
| `/__fuast/storage` | Stored objects with size, type, visibility, and uploader. |
| `/__fuast/tools` | WebMCP tools with kind, description, and argument schema. |
| `/__fuast/jobs` | Durable jobs `{ jobs, counts, nextRunAt }`. `?status=` and `?limit=` filter. `POST /__fuast/jobs/<id>/cancel` cancels a pending job and `POST /__fuast/schedules/<name>/run` runs a schedule now (owner or workspace admin). |

Locally they are open. `fuast dev` binds to `127.0.0.1` by default. Hosted, they require the app owner or a workspace admin, by app session cookie or `Authorization: Bearer <token>`.

`/__fuast/manifest` has `restricted: true` for a [restricted app](https://fuast.com/docs/security.md#restricted-apps).

An unclaimed preview has no owner. Its `GET` inspection routes answer the preview's edit token in the `x-fuast-edit-token` header, on the app's own origin and at `GET <platform>/api/deploy/unclaimed/inspect/<route>` (what the CLI uses). The token is never accepted in a URL, and the `POST` routes are not available until the app is claimed.

One more route is for the app's own client, not for inspection: hosted `GET /__fuast/auth/token` returns `{ token, expiresAt }`, a fresh identity token for the caller's same-origin app session (401 without a session, 403 without access). `fuast/client` calls it to refresh `getIdentity().token`. See [endpoint credentials](https://fuast.com/docs/auth.md#endpoint-credentials).

## HTTP call API

```http
POST <app url>/api/call
Content-Type: application/json
Authorization: Bearer <fuast token>        (or the app session cookie)

{"kind": "query" | "mutation" | "action", "name": "todos", "args": []}
```

- `args` is the array of positional arguments after `ctx`. For a handler with tool `args`, send one object: `"args": [{"text": "milk"}]`.
- Success: HTTP 200 with the handler's JSON result.
- Handler error: HTTP 500 with `{ "error": "<message>", "code"?: "<code>" }`.
- Not signed in: 401 `{ "error": "...", "code": "sign_in_required" }`. No access: 403 `{ "code": "forbidden" }`. Quota: 429, see [limits](https://fuast.com/docs/limits.md#reading-a-quota-error).

`GET <app url>/api/me` returns the identity the app sees for the caller.

The same sharing rules and quotas apply as in the browser. A token with `--app` scope only works for its app.

## Realtime protocol

Clients talk to `<app url>/__fuast/ws` with JSON messages that have an `op`. `fuast/client` handles this. It is documented for debugging, not as an app API.

| Direction | `op` | Fields |
| --- | --- | --- |
| client | `query.subscribe` | `id`, `name`, `args`, `patches?` (`true` to receive `query.patch`) |
| client | `query.unsubscribe` | `id` |
| client | `mutation.run`, `action.run` | `id`, `name`, `args`. At most 4 run at once per connection; later ones queue in order. |
| client | `client.timings` | Client-side timing samples. No reply. |
| client | `ping` | |
| server | `hello` | `session`, `deployId`, `features` |
| server | `query.result` | `id`, `data`, `version?` |
| server | `query.patch` | `id`, `base`, `version`, `operations`: JSON patch against version `base`. |
| server | `query.error` | `id`, `error`, `code?` |
| server | `mutation.result`, `action.result` | `id`, `ok`, `data?`, `error?`, `code?` |
| server | `refresh` | `deployId`. A new deploy is live. |
| server | `error` | `error`, `code`, `quota?`. Malformed messages get code `invalid_request`. |

The server closes the socket with code `4401` when the session ends (sign-out) and `4403` when the caller loses access to the app. `fuast dev` and hosted apps speak the same protocol.

### Operate a platform

For the person who runs a fuast platform. These commands use the platform's operator API, which exists only when the platform sets `FUAST_OPERATOR_TOKEN`.

```txt
fuast operator overview
fuast operator apps [--workspace <id|domain>] [--search <text>] [--unclaimed] [--limit 50] [--cursor <cursor>]
fuast operator reports [--limit 100]
fuast operator release-preview <slug>
fuast operator users [--search <text>] [--limit 100] [--cursor <cursor>]
fuast operator workspaces [--search <text>] [--limit 100] [--cursor <cursor>]
fuast operator delete-app <slug> [--yes]
fuast operator verify-user <email>
fuast operator audit [--limit 100] [--cursor <cursor>]
fuast operator agents [--limit 100]
fuast operator disconnect-agent <id>
fuast operator agent-audit [--limit 100] [--cursor <cursor>]
```

- The CLI reads the operator token from the `FUAST_OPERATOR_TOKEN` environment variable. There is no flag for it and it is never saved to `~/.fuast/config.json`. It is sent only to the platform origin (`FUAST_API` or the saved login), over https unless the platform is on this machine. `fuast help` lists these commands only when the variable is set.
- `overview` prints counts of users, workspaces, apps, and deploys, plus stored file bytes and the size of all app databases on disk.
- `apps` lists every app on the platform, newest first, with its owner, workspace, sharing, status, row and byte usage, last deploy, and custom domains. `--workspace` takes a workspace id or an email domain. `--search` matches the slug, name, or owner email. When there is more, the output ends with the `--cursor` value for the next page.
- `apps --unclaimed` lists only previews deployed without an account, with their status (`unclaimed`, `suspended`, `expired`), expiry, and report count. `delete-app` takes a preview down like any app.
- `reports` lists the previews people reported, with each report's address and reason. A preview that enough different addresses report is suspended: it stops serving until you run `release-preview <slug>` (reports are kept) or `delete-app <slug>`, or until it expires.
- `users` and `workspaces` list accounts (email, verified, workspace, role, app count) and workspaces (owner, members, apps, storage).
- `delete-app` deletes an app with its database, stored files, deploys, schedules, and custom domains. It is the same deletion the app's owner can do, and there is no undo. It shows what will be deleted and asks you to type the slug. `--yes` skips the question.
- `verify-user` marks an account's email as verified, for someone whose verification email never arrived. It does what the emailed link does, so the account joins its company workspace under the usual rules. Only do this for an address you know the person controls.
- `audit` prints what was done with the operator token: action, target, time, address, and result.
- `agents` lists the agents people connected through the [MCP server](https://fuast.com/docs/mcp.md) across every account. `disconnect-agent` ends one connection, and `agent-audit` prints what agents did through MCP.
- The operator API cannot sign in as a user, create tokens for a user, or read app data, env values, or secrets. See [security](https://fuast.com/docs/security.md#platform-operators).

## Environment variables

### App env

`.env.fuast.server` holds `KEY=value` lines for `ctx.env`. Lines starting with `#` are comments. `fuast dev` reads it on every reload, and `fuast deploy` uploads it, replacing the previous deploy's file values. Values set in the dashboard Env tab override file values and survive deploys. Use the dashboard for production secrets you do not want in a file. Never commit the file. Env values never reach client code.

### CLI env

| Name | Effect |
| --- | --- |
| `FUAST_TOKEN` | Token from `fuast token create`. Used instead of the saved login, for CI. |
| `FUAST_API` | Platform origin. Defaults to `https://fuast.com`. |
| `FUAST_UNCLAIMED` | `0` makes `fuast deploy` need a login, like `--no-unclaimed`. By default a deploy without a login creates an [unclaimed preview](#deploy-without-an-account). |
| `FUAST_OPERATOR_TOKEN` | Operator token for `fuast operator` commands, for people running their own platform. Read from the environment only. |

```sh
FUAST_TOKEN=<token> bunx @revnu/fuast deploy
```

### Platform environment

For people running their own fuast platform:

| Name | Effect |
| --- | --- |
| `FUAST_BASE_URL` | Public origin of the platform. |
| `FUAST_APPS_DOMAIN` | Serve each app at `<slug>.<domain>`. Required in production. Set to `off` for `/a/<slug>/` path mode. |
| `FUAST_ALLOW_SAME_ORIGIN_APPS` | `1` lets a production platform run in path mode, where apps share the platform origin. Only for platforms where every app is trusted. See [security](https://fuast.com/docs/security.md#app-origins-and-cookies). |
| `FUAST_TRUST_PROXY` | `1` to read client IPs from proxy headers for rate limits. |
| `FUAST_TRUSTED_PROXY_SECRET` | Secret the web front end sends when it forwards app-subdomain requests. Unset: forwarded app headers are honoured only from loopback. |
| `FUAST_RUNTIME_SECRET` | Shared secret between platform and runtime (formerly `FUAST_EXECUTOR_SECRET`). Required in production: the platform and runtime refuse to start with the default. |
| `FUAST_PERSONAL_DOMAINS` | Extra comma-separated email domains treated as personal (a workspace of one). |
| `FUAST_OPERATOR_TOKEN` | Turns on the operator API (`/api/operator/*`, used by `fuast operator`). At least 32 random characters, for example `openssl rand -hex 32`. Unset: the routes do not exist. A production platform refuses to start with a shorter value. See [operate a platform](#operate-a-platform). |
| `FUAST_UNCLAIMED_DEPLOYS` | `off` refuses deploys without an account (`403`, with a message that says to run `fuast login`). Previews that already exist keep working until they are claimed or expire. On by default, and only on platforms with `FUAST_APPS_DOMAIN`. The caps and quotas of previews are `FUAST_UNCLAIMED_*` variables, listed in [limits](https://fuast.com/docs/limits.md#unclaimed-previews). |
| `FUAST_CUSTOM_DOMAIN_TARGET` | Hostname (or comma-separated hostnames/IPs) users point custom domains at, e.g. your Railway public domain. See [custom domains](https://fuast.com/docs/domains.md#running-your-own-platform). |
| `FUAST_CUSTOM_DOMAIN_TLS` | `manual` (default) or `railway` (default when `RAILWAY_API_TOKEN` is set): who issues certificates for custom domains. |
| `FUAST_MCP` | `off` turns the [MCP server](https://fuast.com/docs/mcp.md) off: `/mcp`, `/oauth/*`, and the OAuth metadata answer 404, and tokens issued to connected agents stop working. On by default. |
| `FUAST_MCP_ALLOWED_ORIGINS` | Comma-separated web origins that may call `/mcp` from a browser page, with a bearer token. Empty by default: other websites are refused. See [security](https://fuast.com/docs/security.md#mcp-and-oauth). |
| `FUAST_MCP_ANON_PER_MINUTE`, `FUAST_MCP_USER_PER_MINUTE` | MCP requests per minute without an account, per client address (60), and with one, per account (600). |
| `FUAST_MCP_ACCESS_TTL_SECONDS`, `FUAST_MCP_REFRESH_TTL_DAYS` | Lifetime of the access tokens (3600 seconds) and refresh tokens (30 days) of connected agents. |
| `FUAST_CUSTOM_DOMAINS` | `off` turns custom domains off. Also `FUAST_CUSTOM_DOMAIN_LIMIT` (per app, 5), `FUAST_CUSTOM_DOMAIN_PENDING_DAYS` (7), `FUAST_WEB_HOSTS` (extra dashboard hostnames). |

Limit defaults can be overridden by environment variables on the platform and runtime. Each variable, and the process that reads it, is listed in [limits](https://fuast.com/docs/limits.md#self-hosted-overrides).

## Deploys and data

- Deploys are immutable. Data is not tied to a deploy: every deploy of an app shares its database, storage, logs, env, and schedules.
- Archive and terminate keep data. Restore resumes an archived app. Terminated apps cannot be restored.
- `fuast db export` is the backup. There is no automatic point-in-time restore.
