---
title: CLI Reference
description: Command-line interface for EmDash CMS.
---

import { Aside } from "@astrojs/starlight/components";

The EmDash CLI provides commands for managing an EmDash CMS instance — database setup, type generation, content CRUD, schema management, media, and more.

## Installation

The CLI is included with the `emdash` package:

```bash
npm install emdash
```

Run commands with `npx emdash` or add scripts to `package.json`. The binary is also available as `em` for brevity.

## Authentication

Commands that talk to a running EmDash instance (everything except `init`, `seed`, `export-seed`, and `secrets`) resolve authentication in this order:

1. **`--token` flag** — explicit token on the command line
2. **`EMDASH_TOKEN` env var**
3. **Stored credentials** from `~/.config/emdash/auth.json` (saved by `emdash login`)
4. **Dev bypass** — if the URL is localhost and no token is available, automatically authenticates via the dev bypass endpoint

Most commands accept `--url` (default `http://localhost:4321`) and `--token` flags. When targeting a local dev server, no token is needed.

## Common Flags

These flags are available on all remote commands:

| Flag      | Alias | Description                 | Default                  |
| --------- | ----- | --------------------------- | ------------------------ |
| `--url`   | `-u`  | EmDash instance URL       | `http://localhost:4321`  |
| `--token` | `-t`  | Auth token                  | From env/stored creds    |
| `--json`  |       | Output as JSON (for piping) | Auto-detected from TTY   |

## Output

When stdout is a TTY, the CLI pretty-prints results with consola. When piped or when `--json` is set, it outputs raw JSON to stdout — suitable for `jq` or other tools.

## Commands

### `emdash init`

Initialize the database with core schema and optional template data.

```bash
npx emdash init [options]
```

#### Options

| Option       | Alias | Description            | Default           |
| ------------ | ----- | ---------------------- | ----------------- |
| `--database` | `-d`  | Database file path     | `./data.db`       |
| `--cwd`      |       | Working directory      | Current directory  |
| `--force`    | `-f`  | Re-run schema and seed | `false`           |

#### Behavior

1. Reads `emdash` config from `package.json`
2. Creates the database file if needed
3. Runs core migrations (creates system tables)
4. Runs template `schema.sql` if configured
5. Runs template `seed.sql` if configured

<Aside>
	If the database already contains collections, `init` skips schema and seed unless `--force` is
	used.
</Aside>

### `emdash dev`

Start the development server with automatic database setup.

```bash
npx emdash dev [options]
```

#### Options

| Option       | Alias | Description                              | Default           |
| ------------ | ----- | ---------------------------------------- | ----------------- |
| `--database` | `-d`  | Database file path                       | `./data.db`       |
| `--types`    | `-t`  | Generate types from remote before starting | `false`         |
| `--port`     | `-p`  | Dev server port                          | `4321`            |
| `--cwd`      |       | Working directory                        | Current directory  |

#### Examples

```bash
# Start dev server
npx emdash dev

# Custom port
npx emdash dev --port 3000

# Generate types from remote before starting
npx emdash dev --types
```

#### Behavior

1. Checks for and runs pending database migrations
2. If `--types` is set, generates TypeScript types from a remote instance (URL from `EMDASH_URL` env or `emdash.url` in `package.json`)
3. Starts Astro dev server with `EMDASH_DATABASE_URL` set

### `emdash types`

Generate TypeScript types from a running EmDash instance's schema.

```bash
npx emdash types [options]
```

#### Options

| Option     | Alias | Description              | Default              |
| ---------- | ----- | ------------------------ | -------------------- |
| `--url`    | `-u`  | EmDash instance URL    | `http://localhost:4321` |
| `--token`  | `-t`  | Auth token               | From env/stored creds |
| `--output` | `-o`  | Output path for types    | `.emdash/types.ts` |
| `--cwd`    |       | Working directory        | Current directory    |

#### Examples

```bash
# Generate types from local dev server
npx emdash types

# Generate from remote instance
npx emdash types --url https://my-site.pages.dev

# Custom output path
npx emdash types --output src/types/emdash.ts
```

#### Behavior

1. Fetches the schema from the instance
2. Generates TypeScript type definitions
3. Writes types to the output file
4. Writes `schema.json` alongside for reference

### `emdash login`

Log in to an EmDash instance using OAuth Device Flow.

```bash
npx emdash login [options]
```

#### Options

| Option  | Alias | Description           | Default                |
| ------- | ----- | --------------------- | ---------------------- |
| `--url` | `-u`  | EmDash instance URL | `http://localhost:4321` |

#### Behavior

1. Discovers auth endpoints from the instance
2. If localhost and no auth configured, uses dev bypass automatically
3. Otherwise initiates OAuth Device Flow — displays a code and opens your browser
4. Polls for authorization, then saves credentials to `~/.config/emdash/auth.json`

Saved credentials are used automatically by all subsequent commands targeting the same instance.

### `emdash logout`

Log out and remove stored credentials.

```bash
npx emdash logout [options]
```

#### Options

| Option  | Alias | Description           | Default                |
| ------- | ----- | --------------------- | ---------------------- |
| `--url` | `-u`  | EmDash instance URL | `http://localhost:4321` |

### `emdash whoami`

Show the current authenticated user.

```bash
npx emdash whoami [options]
```

#### Options

| Option    | Alias | Description           | Default                |
| --------- | ----- | --------------------- | ---------------------- |
| `--url`   | `-u`  | EmDash instance URL | `http://localhost:4321` |
| `--token` | `-t`  | Auth token            | From env/stored creds  |
| `--json`  |       | Output as JSON        |                        |

Displays email, name, role, auth method, and instance URL.

### `emdash content`

Manage content items. All subcommands use the remote API via `EmDashClient`.

#### `content list <collection>`

```bash
npx emdash content list posts
npx emdash content list posts --status published --limit 10
```

| Option     | Description          |
| ---------- | -------------------- |
| `--status` | Filter by status     |
| `--limit`  | Maximum items        |
| `--cursor` | Pagination cursor    |

#### `content get <collection> <id>`

```bash
npx emdash content get posts 01ABC123
npx emdash content get posts 01ABC123 --raw
```

| Option  | Description                                      |
| ------- | ------------------------------------------------ |
| `--raw` | Return raw Portable Text (skip markdown conversion) |

The response includes a `_rev` token — pass it to `content update` to prove you've seen what you're overwriting.

#### `content create <collection>`

```bash
npx emdash content create posts --data '{"title": "Hello"}'
npx emdash content create posts --file post.json --slug hello-world
cat post.json | npx emdash content create posts --stdin
```

| Option     | Description                    |
| ---------- | ------------------------------ |
| `--data`   | JSON string with content data  |
| `--file`   | Read data from a JSON file     |
| `--stdin`  | Read data from stdin           |
| `--slug`   | Content slug                   |
| `--status` | Initial status (draft, published) |

Provide data via exactly one of `--data`, `--file`, or `--stdin`.

#### `content update <collection> <id>`

Like a file editor that requires you to read before you write — you must provide the `_rev` token from a prior `get` to prove you've seen the current state. This prevents accidentally overwriting changes you haven't seen.

```bash
# 1. Read the item, note the _rev
npx emdash content get posts 01ABC123

# 2. Update with the _rev from step 1
npx emdash content update posts 01ABC123 \
  --rev MToyMDI2LTAyLTE0... \
  --data '{"title": "Updated"}'
```

| Option   | Description                            |
| -------- | -------------------------------------- |
| `--rev`  | Revision token from `get` (required)   |
| `--data` | JSON string with content data          |
| `--file` | Read data from a JSON file             |

If the item has changed since your `get`, the server returns 409 Conflict — re-read and try again.

#### `content delete <collection> <id>`

```bash
npx emdash content delete posts 01ABC123
```

Soft-deletes the content item (moves to trash).

#### `content publish <collection> <id>`

```bash
npx emdash content publish posts 01ABC123
```

#### `content unpublish <collection> <id>`

```bash
npx emdash content unpublish posts 01ABC123
```

#### `content schedule <collection> <id>`

```bash
npx emdash content schedule posts 01ABC123 --at 2026-03-01T09:00:00Z
```

| Option | Description                     |
| ------ | ------------------------------- |
| `--at` | ISO 8601 datetime (required)    |

#### `content restore <collection> <id>`

```bash
npx emdash content restore posts 01ABC123
```

Restores a trashed content item.

### `emdash schema`

Manage collections and fields.

#### `schema list`

```bash
npx emdash schema list
```

Lists all collections.

#### `schema get <collection>`

```bash
npx emdash schema get posts
```

Shows a collection with all its fields.

#### `schema create <collection>`

```bash
npx emdash schema create articles --label Articles
npx emdash schema create articles --label Articles --label-singular Article --description "Blog articles"
```

| Option             | Description                     |
| ------------------ | ------------------------------- |
| `--label`          | Collection label (required)     |
| `--label-singular` | Singular label                  |
| `--description`    | Collection description          |

#### `schema delete <collection>`

```bash
npx emdash schema delete articles
npx emdash schema delete articles --force
```

| Option    | Description         |
| --------- | ------------------- |
| `--force` | Skip confirmation   |

Prompts for confirmation unless `--force` is set.

#### `schema add-field <collection> <field>`

```bash
npx emdash schema add-field posts body --type portableText --label "Body Content"
npx emdash schema add-field posts featured --type boolean --required
```

| Option       | Description                                                                                      |
| ------------ | ------------------------------------------------------------------------------------------------ |
| `--type`     | Field type: string, text, number, integer, boolean, datetime, image, reference, portableText, json (required) |
| `--label`    | Field label (defaults to field slug)                                                             |
| `--required` | Whether the field is required                                                                    |

#### `schema remove-field <collection> <field>`

```bash
npx emdash schema remove-field posts featured
```

### `emdash media`

Manage media items.

#### `media list`

```bash
npx emdash media list
npx emdash media list --mime image/png --limit 20
```

| Option     | Description              |
| ---------- | ------------------------ |
| `--mime`   | Filter by MIME type      |
| `--limit`  | Number of items          |
| `--cursor` | Pagination cursor        |

#### `media upload <file>`

```bash
npx emdash media upload ./photo.jpg
npx emdash media upload ./photo.jpg --alt "A sunset" --caption "Taken in Bristol"
```

| Option      | Description    |
| ----------- | -------------- |
| `--alt`     | Alt text       |
| `--caption` | Caption text   |

#### `media get <id>`

```bash
npx emdash media get 01MEDIA123
```

#### `media delete <id>`

```bash
npx emdash media delete 01MEDIA123
```

### `emdash search`

Full-text search across content.

```bash
npx emdash search "hello world"
npx emdash search "hello" --collection posts --limit 5
```

| Option         | Alias | Description          |
| -------------- | ----- | -------------------- |
| `--collection` | `-c`  | Filter by collection |
| `--limit`      | `-l`  | Maximum results      |

### `emdash taxonomy`

Manage taxonomies and terms.

#### `taxonomy list`

```bash
npx emdash taxonomy list
```

#### `taxonomy terms <name>`

```bash
npx emdash taxonomy terms categories
npx emdash taxonomy terms tags --limit 50
```

| Option     | Alias | Description       |
| ---------- | ----- | ----------------- |
| `--limit`  | `-l`  | Maximum terms     |
| `--cursor` |       | Pagination cursor |

#### `taxonomy add-term <taxonomy>`

```bash
npx emdash taxonomy add-term categories --name "Tech" --slug tech
npx emdash taxonomy add-term categories --name "Frontend" --parent 01PARENT123
```

| Option     | Description                              |
| ---------- | ---------------------------------------- |
| `--name`   | Term label (required)                    |
| `--slug`   | Term slug (defaults to slugified name)   |
| `--parent` | Parent term ID (for hierarchical taxonomies) |

### `emdash menu`

Manage navigation menus.

#### `menu list`

```bash
npx emdash menu list
```

#### `menu get <name>`

```bash
npx emdash menu get primary
```

Returns the menu with all its items.

### `emdash seed`

Apply a seed file to the database. This command works directly on a local SQLite file (no running server needed).

```bash
npx emdash seed [path] [options]
```

#### Arguments

| Argument | Description       | Default               |
| -------- | ----------------- | --------------------- |
| `path`   | Path to seed file | `.emdash/seed.json` |

#### Options

| Option             | Alias | Description                             | Default                     |
| ------------------ | ----- | --------------------------------------- | --------------------------- |
| `--database`       | `-d`  | Database file path                      | `./data.db`                 |
| `--cwd`            |       | Working directory                       | Current directory           |
| `--validate`       |       | Validate only, don't apply              | `false`                     |
| `--no-content`     |       | Skip sample content                     | `false`                     |
| `--on-conflict`    |       | Conflict handling: skip, update, error  | `skip`                      |
| `--uploads-dir`    |       | Directory for media uploads             | `.emdash/uploads`         |
| `--media-base-url` |       | Base URL for media files                | `/_emdash/api/media/file` |
| `--base-url`       |       | Site base URL (for absolute media URLs) |                             |

#### Seed File Resolution

The command looks for seed files in this order:

1. Positional argument (if provided)
2. `.emdash/seed.json` (convention)
3. Path from `package.json` `emdash.seed` field

### `emdash export-seed`

Export database schema and content as a seed file. Works directly on a local SQLite file.

```bash
npx emdash export-seed [options] > seed.json
```

#### Options

| Option           | Alias | Description                                          | Default           |
| ---------------- | ----- | ---------------------------------------------------- | ----------------- |
| `--database`     | `-d`  | Database file path                                   | `./data.db`       |
| `--cwd`          |       | Working directory                                    | Current directory |
| `--with-content` |       | Include content (all or comma-separated collections) |                   |
| `--no-pretty`    |       | Disable JSON formatting                              | `false`           |

#### Output Format

The exported seed file includes:

- **Settings**: Site title, tagline, social links
- **Collections**: All collection definitions with fields
- **Taxonomies**: Taxonomy definitions and terms
- **Menus**: Navigation menus with items
- **Widget Areas**: Widget areas and widgets
- **Content** (if requested): Entries with `$media` references and `$ref:` syntax for portability

### `emdash secrets generate`

Generate an `EMDASH_ENCRYPTION_KEY` for your deployment. The key is used to
encrypt plugin secrets at rest.

```bash
npx emdash secrets generate
```

Prints the new key to stdout. Pipe it into your secret store, or write it
straight to a dev file with `--write`:

```bash
npx emdash secrets generate --write .dev.vars
npx emdash secrets generate --write .env
```

`--write` refuses to overwrite an existing entry without `--force`.
Replacing a key in a deployment with existing encrypted data will leave
those secrets unreadable, so the protection is intentional.

### `emdash secrets fingerprint <key>`

Print the 8-character fingerprint (kid) of a key without exposing its
value. Useful in CI for verifying the right key was deployed:

```bash
npx emdash secrets fingerprint emdash_enc_v1_...
```

## Generated Files

### `.emdash/types.ts`

TypeScript interfaces generated by `emdash types`:

```ts
// Generated by EmDash CLI
// Do not edit manually - run `emdash types` to regenerate

import type { PortableTextBlock } from "emdash";

export interface Post {
	id: string;
	title: string;
	content: PortableTextBlock[];
	publishedAt: Date | null;
}
```

### `.emdash/schema.json`

Raw schema export for tooling:

```json
{
  "version": "a1b2c3d4",
  "collections": [
    {
      "slug": "posts",
      "label": "Posts",
      "fields": [...]
    }
  ]
}
```

## Environment Variables

| Variable                  | Description                              |
| ------------------------- | ---------------------------------------- |
| `EMDASH_DATABASE_URL`     | Database URL (set automatically by `dev`) |
| `EMDASH_TOKEN`            | Auth token for remote operations          |
| `EMDASH_URL`              | Default remote URL for `types` and `dev --types` |
| `EMDASH_ENCRYPTION_KEY`   | Key for encrypting plugin secrets at rest. Operator-provided — never stored in the database. Generate with `emdash secrets generate`. |
| `EMDASH_PREVIEW_SECRET`   | Optional override for preview HMAC secret. When unset, EmDash generates and persists one in the options table. |
| `EMDASH_IP_SALT`          | Optional override for the commenter-IP hash salt. When unset, EmDash generates and persists one in the options table. |
| `EMDASH_AUTH_SECRET`      | Legacy. Used as the IP-salt source if set, so existing installs keep stable commenter-IP hashes across upgrade. New installs should not set this. |

## Package Scripts

```json
{
	"scripts": {
		"dev": "emdash dev",
		"init": "emdash init",
		"types": "emdash types",
		"seed": "emdash seed",
		"export-seed": "emdash export-seed",
		"db:reset": "rm -f data.db && emdash init"
	}
}
```

## Exit Codes

| Code | Description                              |
| ---- | ---------------------------------------- |
| `0`  | Success                                  |
| `1`  | Error (configuration, network, database) |
