> This page is for version Nightly (default).
> For other versions, use one of these documentation indexes:
> - Nightly (default): https://docs.nvidia.com/nemo/labs-voice-agent/nightly/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/nemo/labs-voice-agent/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/nemo/labs-voice-agent/_mcp/server.

# Building the Docs

This site uses [Fern](https://buildwithfern.com). Page content lives in `docs/` as Markdown, and the Fern
configuration lives in `docs/fern`. Run the commands on this page from `docs/fern/` or the repository root
with `npm --prefix docs/fern run <script>`.

## Prerequisites

Before you build or preview the documentation, review the requirements for the task you plan to run:

| Requirement | Why |
|---|---|
| Node.js 20 or newer, installed with a version manager | Required for every documentation task. Continuous integration (CI) pins Node 20. Use Node 22.13 or newer for `npm run dev`. |
| Docker | Only for `npm run generate:library:local`, which parses the Python package to build the application programming interface (API) reference. Nothing else needs it. |
| A Fern account with NVIDIA organization access | Only for the development server, hosted previews, and publishing. `docs.yml` sets `global-theme: nvidia`, which the Fern command-line interface (CLI) fetches at serve time. |

The Node.js version affects tasks as follows:

- **CI validation:** `docs/fern/package.json` declares `engines.node >= 20`, and CI pins Node 20 for
  `npm run check`. Node 18 fails with `ReferenceError: crypto is not defined`.
- **Development server:** Use Node 22.13 or newer for `npm run dev`. The server installs the current `pnpm`
  release, which declares `node >= 22.13`.
- **Installation:** Use `nvm` or another version manager. The development server installs `pnpm` globally.
  Installation fails with `EACCES` when npm uses a root-owned global prefix such as `/usr/local`.

The Fern CLI is **not** installed globally. Every npm script uses `npm exec` to fetch and run the exact
version pinned in `docs/fern/fern.config.json`, so local runs match CI.

## Scripts

Use these scripts from `docs/fern` to generate, validate, preview, or publish the site:

| Command | Purpose |
|---|---|
| `npm run nav:gen` | Regenerates both navigation files from `nav.json`. Run after any page add/remove/rename. |
| `npm run nav:check` | Exits non-zero if the generated nav files are stale. |
| `npm run check` | Runs `fern check` to validate `docs.yml` and the nightly navigation. No Docker, login, or generated API reference is required. |
| `npm run login` | Authenticates the CLI so it can fetch the NVIDIA theme. |
| `npm run dev` | Local dev server at `http://localhost:3000`. |
| `npm run generate:library:local` | Builds the Python API reference from the local checkout (Docker). |
| `npm run generate:library` | Builds the API reference from the GitHub source Fern clones. Requires login. |
| `npm run preview` | Hosted preview build. Needs `FERN_TOKEN`. |

## Navigation Is Generated — Do Not Hand-Edit

Fern requires two navigation files with *different* path conventions, and only one of them is
validated by `fern check`:

| File | Path Convention | Consumer |
|---|---|---|
| `docs/fern/versions/nightly.yml` | Relative to itself (`../../<page>`) | Every build. Validated by `fern check`. |
| `docs/index.yml` | Relative to `docs/` (bare paths) | Only `publish-fern-docs.yml` at release time. **Not** CI-validated. |

Both are generated from the single source of truth, `docs/fern/nav.json`, by
`docs/fern/scripts/gen-nav.mjs`. Manual edits can make a page disappear from one channel while it remains
visible in the other.

To add a page:

```bash
# 1. Create the page under docs/, copying the SPDX header block from any existing page.
#    (The header is an MDX comment: {/* ... */} — keep it verbatim as the first lines.)

# 2. Add an entry to docs/fern/nav.json under the right section.

# 3. Regenerate both nav files.
cd docs/fern
npm run nav:gen

# 4. Validate.
npm run check
```

`gen-nav.mjs` also asserts that every page referenced by `nav.json` exists on disk. If a page is missing, the
script lists it explicitly. This message is clearer than the corresponding `fern check` or lychee output.
Commit `nav.json`, `versions/nightly.yml`, and `index.yml` together.

## Generating the Python API Reference

The `Full-Library-Reference` section under `docs/fern/product-docs/` is generated, gitignored, and
rebuilt on every publish. You do not need it locally for `fern check` to pass. Generate it only when you
need to view the API pages in the development server.

Generating from a local checkout requires a temporary edit to `docs/fern/docs.yml`: uncomment the
`nemo-voice-agent-local` library block.

```yaml
  nemo-voice-agent-local:
    input:
      path: ../../nemo_voice_agent
    output:
      path: ./product-docs/nemo-labs-voice-agent/Full-Library-Reference
    lang: python
```

```bash
cd docs/fern
# 1. Uncomment the nemo-voice-agent-local block in docs.yml.
npm run generate:library:local
# 2. Comment it back out before running check / dev / preview / publish.
```

Re-commenting is mandatory: `fern docs dev` rejects path-backed libraries during config load with
`Library 'nemo-voice-agent-local' uses 'path' input which is not yet supported`. The generated pages
under `product-docs/` survive the re-comment, so the dev server still serves them.

`generate:library:local` then runs `npm run sanitize:generated`. That script rewrites MDX-invalid JavaScript
XML (JSX) attributes that the Python generator emits for Pydantic field types. If you run the generator directly,
also run the sanitizer.

## Running the Dev Server

To authenticate Fern and start the local documentation server, run:

```bash
cd docs/fern
npm run login   # once, or whenever the theme fetch 403s
npm run dev
```

If login fails with an organization access error, sign in to the
[Fern dashboard](https://dashboard.buildwithfern.com) with an account that has NVIDIA organization access,
and then retry.

## Authoring Rules CI Enforces

Three gates run on any pull request (PR) that touches `docs/` (`fern-docs-ci.yml`):

1. **Every raw HTML `img` tag must be self-closing** anywhere under `docs/`. Prefer plain Markdown
   images. If you must use HTML, close the tag with a trailing slash.
2. **`fern check`** must pass.
3. **lychee `--offline`** over `docs/**/*.md`. Every relative link target must exist on disk. There
   is no ignore file—a link to a repository source file that is not a documentation page fails the build.
   Name source files in inline code instead of linking to them.

Beyond the gates, two authoring conventions matter:

- Write pages as `.md`. The `.mdx` extension is reserved for generated output.
- Fern renders `.md` through MDX, so a bare `{`, `}`, or `<` outside a fenced code block breaks the
  build. Put YAML braces, OmegaConf interpolation, type generics, and comparison operators inside
  fenced code blocks or inline backticks.

`fern-docs-preview.yml` runs when a pull request head is pushed to a bot-mirrored
`pull-request/<number>` branch. It builds a hosted preview and posts the link as a pull request comment
when `PUBLISH_FERN=true` and the pull request changes `docs/**` or
`.github/workflows/fern-docs-ci.yml`. The workflow reads
the documentation from the mirror branch, uses the Fern configuration and generated Markdown sanitizer
from `main`, and pins the Fern CLI version in the workflow.

## Publishing

**Merging a documentation change to `main` publishes the nightly channel live.** `publish-fern-docs.yml` fires
on any push to `main` that touches `docs/**`, gated on the repository variable `PUBLISH_FERN=true`.
There is no manual approval step—review the preview before merging.

Releases are separate. For a published GitHub Release or a manual dispatch with a tag, the same workflow
freezes that tag's documentation into a versioned channel. It registers the channel in `docs.yml`, prunes to
the three most recent versions, and opens a pull request to persist the registry change back to `main`.
Pre-release tags publish but skip version registration.

## Related

Use these pages for the repository contribution workflow and test suites:

- [Contributing](/nemo/labs-voice-agent/resources/contribute/contributing): Branch, lint, and pull request conventions for the repository.
- [Testing](/nemo/labs-voice-agent/resources/contribute/testing): How to run the pytest suites.