# Command-line reference

_Trail / Reference_

> Every trail command and flag, with defaults, exit status, what goes to stdout and stderr, and the complete list of build-time errors and warnings.

Trail has two commands, `build` and `serve`, plus clap's own `help`. Everything below is the complete flag surface; there are no others.

## Synopsis

```text
trail build [OPTIONS] [ROOT]
trail serve [OPTIONS] [ROOT]
trail help  [COMMAND]
```

`trail --version` prints the version; `trail --help` and `trail <command> --help` print usage.

## trail build

Builds the site into the output directory.

| Argument | | |
|---|---|---|
| `[ROOT]` | path | The site root — the directory holding `trail.toml`. Default `.` |

| Flag | Value | |
|---|---|---|
| `--out <DIR>` | path | Where the site is written. Default `ROOT/dist`. Excluded from the site scan, so a build into a directory inside the root is safe. |
| `--allow-dangling-links` | — | Downgrade missing `~link` and `related:` targets, missing images, and unresolvable `§` citations from errors to warnings. **Ambiguous references stay fatal.** |
| `--strict` | — | Fail the build when any article has no `description:`. Alias pages are exempt. |
| `--render-llms-full` | — | Also write each unit's `print.md` as `llms-full.txt` beside it. |
| `--cbpath <PATH>` | one path segment | Also publish [a cache-busting copy](/trail/building/the-cache-busting-copy.md) of the whole site under `PATH`, with every link inside it rewritten to stay there. |

On success:

```text
built 1227 pages (8 products) → ./dist
```

With `--cbpath`, a second line names the copy:

```text
built 1227 pages (8 products) → ./dist
cache-busting copy → ./dist/8f3a91c/
```

The page count is the site's; the copy holds the same number again and is not added in.

The count is HTML pages: articles, product and anthology landings, book covers, `/print` pages, the front page and `404.html`. Markdown mirrors, `llms.txt`, `site.json`, the sitemap and the search bundle are not counted.

## trail serve

Builds the site, serves the output over HTTP, and rebuilds on change. Takes every `build` flag, plus:

| Flag | Value | |
|---|---|---|
| `--addr <ADDR>` | socket address | Default `127.0.0.1:8724`. |

```text
serving ./dist at http://127.0.0.1:8724 (watching . for changes)
rebuilt 1227 pages
```

Behaviour:

- Changes are debounced for 200 ms; changes inside the output directory, and paths with a dot-prefixed component, are ignored.
- Pages are served with a live-reload script that reloads them over server-sent events after each successful rebuild. **`trail build` output never contains it.**
- Misses serve the built `404.html` with a real 404 status.
- A failed initial build or rebuild prints the error and keeps serving the last good output — the process does not exit.

## Exit status and streams

| | |
|---|---|
| Exit `0` | The build succeeded. |
| Exit non-zero | The build failed; the error is on stderr. |
| stdout | The build summary line, and `serve`'s serving/rebuild lines. |
| stderr | Errors and warnings. |

`trail serve` runs until interrupted.

## Warnings

These print on stderr and do **not** fail the build:

| Warning | |
|---|---|
| `image '…' is not referenced by any article and was not published` | The file stays out of the output. |
| `in article '…': link '~…' matches no page in product '…'` | Only with `--allow-dangling-links`. |
| `in article '…' (related): …` | A `related:` reference that did not resolve, with `--allow-dangling-links`. |
| `in article '…': image '…' not found …` | With `--allow-dangling-links`. |
| `initial build failed (serving previous output): …` | `serve` only. |
| `rebuild failed (still serving the last good build): …` | `serve` only. |

## Errors

Loading errors stop at the first problem, with the chain of contexts:

```text
Error: loading product 'pekit'

Caused by:
    0: loading topic in '2--recipes.topic'
    1: in article '100--anatomy.md'
    2: updated: '2026-5-1' is not an ISO date (expected YYYY-MM-DD)
```

Link errors are collected across the whole site and reported together:

```text
Error: 3 broken links:
in article '/pekit/recipes/anatomy': link '~pekit/running/invokation' matches no page in product 'pekit'
…
```

### The common ones

| Message | |
|---|---|
| `unexpected file '…' in site root: only trail.toml is allowed` | The root takes `trail.toml`, `*.product/`, the output dir, and files named by the config. |
| `unexpected directory '…' in site root: only *.product directories are allowed` | |
| `unexpected grouping type '.…' on '…' in a product (expected .antho, .book, .topic or .shelf)` | |
| `a shelf cannot contain another shelf ('…')` | |
| `grouping directory '…' is missing its '<order>--' prefix` | |
| `'…' has a non-numeric order prefix '…'` | |
| `'…' is not a valid slug (lowercase kebab-case)` | |
| `items '…' and '…' share order N` | Two siblings with the same order prefix. |
| `duplicate article slug '…'` | Also raised for shelf contents flattened into their parent. |
| `chapter '…' contains no articles` | A book chapter must lead somewhere. |
| `'print' is a reserved slug (every section has a /print page)` | |
| `'…' is a reserved product slug (it collides with the '/…' output directory)` | `assets` and `pagefind`. |
| `… has no frontmatter (must start with '---')` | |
| `… has unterminated frontmatter` | |
| `parsing frontmatter of …` | Bad YAML, a missing required key, or an unknown key. |
| `updated: '…' is not an ISO date (expected YYYY-MM-DD)` | |
| `color '…' is not a #rrggbb hex color` | |
| `accent_dark without accent: set the base accent color too` | |
| `edit_url must contain a {path} placeholder …` | |
| `featured product '…' does not exist (no ….product directory)` | |
| `favicon names '…', which does not exist in the site root` | Likewise `custom_css` and `head_html`. |
| `link '~…' is ambiguous; candidates: …` | Never downgradable. |
| `unknown admonition type '[!…]' (expected NOTE, TIP, IMPORTANT, WARNING or CAUTION)` | |
| `unclosed ':::tabs' block (close it with ':::')` | And the other tab-group errors. |
| `inline_ref phrase '…' is claimed by both … and …` | |
| `'… §…' does not match any section of '…'` | Downgradable. |
| `image '…': ~references name pages, not files — use a path relative to the article` | |
| `--cbpath '…' is already a path in the site; the copy would bury it …` | Checked before anything is built. |
| `--cbpath '…' must be a single path segment, with no '/'` | Likewise the empty, dot-leading and bad-character forms. |

## Installing

Trail is a single Rust binary. Build it from its source directory:

```console
$ cargo build --release
```

The result is `target/release/trail`. It carries the templates, stylesheet, fonts, syntax definitions, search engine and diagram renderer inside it, so the binary alone is the whole tool — copy it onto your `PATH` and nothing else needs installing.

Related content:

- [Building and serving](/trail/building/build-and-serve.md)
- [The output surface](/trail/building/the-output-surface.md)
- [trail.toml reference](/trail/reference/trail-toml.md)
- [Directory and file names](/trail/reference/directory-names.md)
