# CLI reference

```sh
npx @impetik/xeer --help
npx @impetik/xeer --version
```

Inside a project that has run `npm install`, `npx xeer <command>` resolves the local copy, and the
scaffolded `npm run dev` / `check` / `build` / `test` scripts work too. Prefer it on your `PATH`?
`npm install --global @impetik/xeer`, and drop the `npx` from every command on this page.

Every command accepts `--json`, which prints a single structured envelope instead of prose (or, for the
long-running commands, a stream of newline-delimited events). See [Building with AI
agents](/guides/agents).

Where a command takes `[directory]`, it defaults to the current directory. Where it takes
`[app|directory]`, you may name a deployed app by its **name**, its **appId**, or its **URL** — or omit it,
in which case the app the current directory is linked to is used.

## Global flags

| Flag | |
| --- | --- |
| `--json` | Structured output. |
| `--version`, `-v` | Print the installed version. |
| `--help`, `-h` | Print the command list. |
| `--control-url <url>` | Talk to a different Xeer control plane. Must be HTTPS except on loopback. `XEER_CONTROL_PLANE_URL` does the same. |

## Build and check

### `xeer new`

```sh
xeer new <directory> [--template notes|todo|blog|personal-site] [--json]
```

Scaffolds a complete working app: manifest, project identity, typed server, Preact client, styles, a
favicon, and a passing test suite. The target directory must be empty or nonexistent. The project name is
derived from the directory name, lowercased and slugified.

`--template` defaults to `notes`:

| Template | |
| --- | --- |
| `notes` | Per-user notes: one table, create and delete, ownership proven in tests. |
| `todo` | A per-user task list: add, tick off, delete, and an open count read through an index. |
| `blog` | Posts anyone can read and only their author can delete, with a list and a read view. |
| `personal-site` | A few static pages from one shared content module, and no database at all. |

Every template checks, tests, and builds clean, and **none of them scaffolds sign-in UI** — every visitor
already has a verified identity, and the scaffolded README explains the opt-in path to a named account.
See [Auth](/guides/auth). With `--json`, `result.template` reports which one was written.

### `xeer check`

```sh
xeer check [directory] [--json]
```

Validates `xeer.app.json` and analyses both source graphs — entrypoints resolve, the server export is
statically readable, no client module imports server code, routes do not collide. Runs nothing. This is
the fast inner-loop command; exit 0 means the project is well-formed.

### `xeer build`

```sh
xeer build [directory] [--json]
```

Compiles a content-addressed build artifact — client bundle, server bundle, assets, and a manifest — into
`.xeer/build/<hash>/`, then verifies it. An artifact the verification refuses is never reported as a
successful build. Prints the `artifactId` (a `sha256:…` value); the same source and manifest always
produce the same id.

### `xeer doctor`

```sh
xeer doctor [directory] [--json]
```

Diagnoses your local environment rather than your code: Node version, platform support, whether the
build dependencies resolve, generated configuration, and local state. Reports each check as
pass/warn/fail/skip. Run this first when something works on one machine and not another.

## Run locally

### `xeer dev`

```sh
xeer dev [directory] [--host <host>] [--port <port>] [--json]
```

Starts a development server with your client and server running together. Client edits hot-reload; server
edits recompile and restart behind a health check with automatic rollback if the new version does not come
up. Defaults to `127.0.0.1` and a free port — pass `--port` to pin it.

Uses [local personas](/guides/local-development#local-personas) instead of real sign-in, and a local database that
persists between runs.

### `xeer preview`

```sh
xeer preview [directory] [--host <host>] [--port <port>] [--json]
```

Verifies and runs the artifact `xeer build` produced, **reading only the artifact's own contents and never
your source**. The closest local approximation of production. Its state is separate from `xeer dev`'s.

### `xeer test`

```sh
xeer test [directory] [--host <host>] [--json]
```

Builds the project, type-checks `tests/**/*.test.ts` against the generated contract, boots the verified
artifact with fresh isolated state, runs every test in registration order, and tears down. Exit 1 on any
failure. See [Testing](/guides/testing).

## Sign in

These sign **you** in, so that you can deploy. They have nothing to do with your application's own users
— see [Auth](/guides/auth).

### `xeer auth login`

```sh
xeer auth login [--control-url <url>] [--no-open] [--json]
```

Browser-approved sign-in. Prints a verification URL and a confirmation code, then opens your browser
(`--no-open` leaves that to you) and polls until you approve or the request expires. **Approve only if the
browser shows that exact code.** The credential is written to `~/.xeer/credentials/cli.json` with `0600`
permissions; `XEER_CONFIG_HOME` overrides the directory.

### `xeer auth status`

```sh
xeer auth status [--json]
```

Verifies the stored credential and prints who you are signed in as, when it expires, and which control
plane it is for:

```text
Signed in as you@example.com.
Credential expires 2026-08-24T09:41:12.883Z.
Control plane: https://control.xeer.run
```

When the stored credential pins somewhere other than the default, the last line says so — which is how
you find out why a deploy went somewhere unexpected:

```text
Control plane: https://staging-control.xeer.run (non-default)
```

`xeer deploy` prints the same warning to stderr before it builds. Fails with `XE5002` when you are not
signed in.

### `xeer auth logout`

```sh
xeer auth logout [--json]
```

Revokes the credential at the control plane and deletes the local file.

### `xeer auth as` / `xeer auth clear`

```sh
xeer auth as <alice|bob> [directory] [--workspace <id>]… [--json]
xeer auth clear [directory] [--json]
```

**Unrelated to signing in.** These select the **local development persona** — a pretend app user —
`xeer dev` starts with, stored per project in `.xeer/auth.local.json`. They touch no account and no
credential.

`--workspace` is repeatable and each value may be comma-separated. It is a startup default only: a
browser tab that has already chosen a persona keeps it, and `xeer test` ignores the file entirely because
tests name their persona per call. See [Local
personas](/guides/local-development#local-personas).

## Ship

### `xeer deploy`

```sh
xeer deploy [directory] [--environment prod|preview] [--control-url <url>] [--json]
```

Builds, verifies, and deploys. Targets **production** unless you pass `--environment preview`, which
deploys to a side-by-side URL with its own separate, disposable data. Waits for the deployed app and
confirms platform health, assets, and one authenticated request before reporting success — a deploy that
serves assets but fails authenticated requests fails with `XE5104`.

`--environment` accepts `prod`, `production`, or `preview`. Reports how many environment values it
injected.

### `xeer promote`

```sh
xeer promote [app|directory] [--from-artifact <artifactId>] [--control-url <url>] [--json]
```

Makes what preview is serving live on production — **the same bundle, not a rebuild** — using production's
own environment values. `--from-artifact` promotes a specific retained version instead. Refused with
`XE5175` when the app has nothing on preview yet.

### `xeer rollback`

```sh
xeer rollback [app|directory] <artifactId> [--environment prod|preview] [--control-url <url>] [--json]
```

Redeploys an artifact the app already deployed successfully. Take the id from the `ARTIFACT` column of
`xeer deployments`. Records a **new** deployment rather than rewriting history, and injects environment
values **as they are now**, not as they were. Either argument order works — an artifact id is recognisable
on sight. Refused with `XE5164` for an artifact this app never deployed.

### `xeer deployments`

```sh
xeer deployments [app|directory] [--limit <n>] [--control-url <url>] [--json]
```

Deployment history for one app, newest first: environment, status (`pending`, `ready`, `failed`),
timestamp, failure reason, and the full `artifactId`. A `*` marks the deployment currently serving each
environment — the latest *completed* one. `--limit` takes a positive integer up to 200.

### `xeer link`

```sh
xeer link [directory] [--app <appId>] [--new] [--control-url <url>] [--json]
```

With no arguments, lists the apps you own. `--app` points this directory at one of them by writing
`xeer.project.json` — **commit it**. `--new` deliberately forks the checkout to a brand-new app identity.
`xeer deploy` never invents an identity when `xeer.project.json` is present but unusable; it fails with a
hint pointing here.

### `xeer disable` / `xeer enable`

```sh
xeer disable [app|directory] [--control-url <url>] [--json]
xeer enable  [app|directory] [--control-url <url>] [--json]
```

Takes an app offline immediately and reversibly: requests get `410 Gone` while the app, its data, and its
environment values stay exactly as they are. Both are idempotent, and both keep working when the app
itself is broken.

### `xeer delete`

```sh
xeer delete [app|directory] --confirm <application> [--control-url <url>] [--json]
```

Permanent. Destroys the deployed app and its preview slot along with **every byte of their stored data**,
removes its URLs, and retires the `appId` for good. `--confirm` must be the application's exact name;
without it the CLI refuses locally (`XE5150`) before reading your credential.

## Inspect

### `xeer inspect` / `xeer state` / `xeer logs`

```sh
xeer inspect <preview-url|app> [--control-url <url>] [--json]
xeer state   <preview-url|app> [--control-url <url>] [--json]
xeer logs    <preview-url|app> [--after <cursor>] [--control-url <url>] [--json]
```

`inspect` reads the running app's manifest, `state` its stored records, `logs` its recent log entries
(`--after` takes a cursor from a previous call to page forward).

A local `xeer dev` or `xeer preview` URL is read directly. A **deployed** app — named by name, appId, or
URL — is read through the control plane on your credential: a deployed inspector answers nobody else.

### `xeer state reset`

```sh
xeer state reset [directory] --state <dev|preview|test> --confirm <application> [--json]
```

Clears local state for one mode. `--confirm` must be the exact application name. Prints a recovery path.
Take an [export](#xeer-export-xeer-import) first if the data matters.

## Move data

### `xeer export` / `xeer import`

```sh
xeer export <preview-url|app> [--out <file>] [--control-url <url>] [--json]
xeer export --state <dev|preview> [directory] [--out <file>] [--json]
xeer import <preview-url> <file> [--json]
xeer import --state <dev|preview> [directory] <file> [--json]
```

`--state dev|preview` reads or writes the state of the `xeer dev` / `xeer preview` server running in that
directory — no URL needed. Any other target is a deployed app, read on your credential. Without `--out`,
the document goes to stdout.

The file carries the schema that produced the rows plus every record, so it is readable and diffable on its
own. `import` **replaces** state wholesale in one transaction, and only when the document's schema is the
one currently in force or a compatible predecessor of it. There is no `--force`. `import` writes **local**
dev and preview state only.

`xeer test` state cannot be exported or imported — it is discarded after every run.

## Environment values

### `xeer env`

```sh
xeer env set <NAME> [value] [--environment prod|preview|dev] [--dir <directory>] [--json]
xeer env ls              [--environment prod|preview|dev] [--dir <directory>] [--json]
xeer env rm  <NAME>      [--environment prod|preview|dev] [--dir <directory>] [--json]
xeer env pull            [--environment prod|preview|dev] [--dir <directory>] [--json]
```

`set`, `rm`, and `ls` default to `prod`; `pull` defaults to `dev`. `--environment` accepts `prod`,
`production`, `preview`, `dev`, or `development`. Omit the value on `set` to read it from stdin.

`ls` shows names, sizes, and timestamps — never values. `pull` writes the values for one environment to
`.xeer/env.dev.local.json` with `0600` permissions, adding `.xeer/` to `.gitignore` first.

`env` uses `--dir` rather than a positional directory, because `set NAME [value]` already has an optional
trailing argument and a value is indistinguishable from a path.

Full details, including the naming rules and limits: [Environment variables and
secrets](/guides/env).

## Environment variables the CLI reads

| Variable | |
| --- | --- |
| `XEER_CONTROL_PLANE_URL` | Default control plane origin, same as `--control-url`. |
| `XEER_CONFIG_HOME` | Where the CLI stores your credential. Defaults to `~/.xeer`. |

## Exit codes

| Code | |
| --- | --- |
| `0` | Success. |
| `1` | The operation failed — a diagnostic explains why. |
| `2` | You asked wrongly: a missing argument, an unknown flag, an unusable value. |

The split matters for scripting: exit 2 means "fix the command", exit 1 means "the command was fine and
the platform refused or the project is broken".

## Requirements

- **Node.js 22.12 or newer.** The CLI checks at startup and prints a clear error otherwise.
- npm 11 and newer block native install scripts by default. If `xeer dev` or `xeer build` cannot find its
  runtime, run `npm approve-scripts esbuild workerd` in your project — or install globally with
  `npm install -g --allow-scripts=workerd,esbuild @impetik/xeer`.
- `xeer doctor` reports on both.

## Next

- **[Diagnostics reference](/reference/diagnostics)** — every `XE####` code.
- **[Manifest reference](/reference/manifest)** — every `xeer.app.json` field.
- **[Deploy, preview, promote, roll back](/guides/deploy)** — the shipping commands in context.
