# docsfy

> Turn any Git repo into a polished, shareable documentation site in minutes

---

Source: generate-your-first-docs-site.md

# Generate Your First Docs Site

You want a working docs site fast, without wiring up the stack manually first. This guide uses the built-in Docker path so you can start the app, sign in as `admin`, and generate documentation for a repository right away.

## Prerequisites

- Docker with Compose
- A new `ADMIN_KEY` with at least 16 characters
- A Git repository URL over HTTPS or SSH
- A supported AI provider that can run inside the container; the image includes `claude`, `gemini`, and `cursor`

## Quick Example

```bash
cp .env.example .env
mkdir -p data
```

```dotenv
ADMIN_KEY=replace-with-a-strong-secret
SECURE_COOKIES=false
```

```bash
docker compose up --build -d
curl http://localhost:8000/health
```

1. Open `http://localhost:8000/login`.
2. Sign in with username `admin` and the `ADMIN_KEY` value from `.env`.
3. Click `New Generation`.
4. Enter `https://github.com/myk-org/for-testing-only`.
5. Leave `Branch` as `main`.
6. Leave `Provider` as `cursor`.
7. In `Model`, type `gpt-5.4-xhigh-fast`.
8. Click `Generate`.
9. When the status becomes `Ready`, click `View Documentation`.

> **Note:** `SECURE_COOKIES=false` is the right setting for plain local `http://localhost`. If you move docsfy behind HTTPS, turn it back on.

> **Tip:** The `Model` field accepts typed values, so you can enter a model even when no suggestions are shown yet.

```mermaid
flowchart LR
  A[Copy .env.example] --> B[Set ADMIN_KEY]
  B --> C[Start docker compose]
  C --> D[Sign in as admin]
  D --> E[Create generation]
  E --> F[Wait for Ready]
  F --> G[Open the docs site]
```

## Step-by-Step

1. Create your local config.

```bash
cp .env.example .env
```

```dotenv
ADMIN_KEY=replace-with-a-strong-secret
SECURE_COOKIES=false
```

`ADMIN_KEY` is required at startup, and the built-in `admin` login uses that value as its password.

> **Warning:** `ADMIN_KEY` must be at least 16 characters long or the server will not start.

> **Warning:** Keep `ADMIN_KEY` in a local `.env` or secret store, not in git-tracked files.

2. Start docsfy with Docker Compose.

```bash
mkdir -p data
docker compose up --build -d
```

```bash
curl http://localhost:8000/health
```

A successful response means the app is up. The app listens on `http://localhost:8000`, and `./data` keeps your database and generated sites between restarts.

3. Sign in as `admin`.

1. Open `http://localhost:8000/login`.
2. Enter username `admin`.
3. Enter the `ADMIN_KEY` value from `.env`.
4. Click `Sign In`.

After login, the dashboard opens and the `New Generation` flow is available immediately.

4. Generate your first site.

1. Click `New Generation`.
2. In `Repository URL`, enter `https://github.com/myk-org/for-testing-only`.
3. Keep `Branch` as `main`.
4. Leave `Provider` as `cursor`.
5. In `Model`, type `gpt-5.4-xhigh-fast`.
6. Click `Generate`.

If another provider is already working in the container, pick that provider instead and type the model you want to use.

> **Note:** Repository URLs can use HTTPS or SSH. For a first run, a public HTTPS repo is the simplest path.

5. Open the result.

The detail view updates in real time while the site is being built. When the status changes to `Ready`, click `View Documentation` to open the generated site in a new tab.

See [Track Generation Progress](track-generation-progress.html) for the live status view and activity log. See [View, Download, and Publish Docs](view-download-and-publish-docs.html) for downloading or publishing the output.

<details><summary>Advanced Usage</summary>

To keep the stack running in the background and watch logs only when you need them:

```bash
docker compose logs -f
```

To stop the stack without deleting your data:

```bash
docker compose down
```

To run the same image without Compose:

```bash
docker build -t docsfy .
docker run --rm \
  -p 8000:8000 \
  --env-file .env \
  -v "$(pwd)/data:/data" \
  docsfy
```

`./data` persists your database and generated sites, so rebuilding or restarting the container does not wipe previous output.

> **Warning:** The built-in `admin` account does not change its password from the dashboard. To change it, update `ADMIN_KEY` in `.env` and restart the container. See [Manage Users, Roles, and Access](manage-users-roles-and-access.html) for shared-instance guidance.

See [Generate Documentation](generate-documentation.html) for more generation options. See [Regenerate for New Branches and Models](regenerate-for-new-branches-and-models.html) when you want another branch or model. See [Install and Run docsfy Without Docker](install-and-run-docsfy-without-docker.html) if you want a non-container setup.

</details>

## Troubleshooting

- The container exits immediately: check that `ADMIN_KEY` is set and at least 16 characters long.
- The login page loads but you cannot stay signed in on `http://localhost:8000`: set `SECURE_COOKIES=false`, then restart the container.
- A generation switches to `Error` right away: the selected provider or model is not ready in the container. Try a working provider/model pair or see [Fix Setup and Generation Problems](fix-setup-and-generation-problems.html).
- A branch name is rejected: use a single branch segment such as `main`, `dev`, or `release-1.x`. Branch names with `/` are rejected.
- A repository URL is rejected: use a Git URL over HTTPS or SSH, not a bare local path.

## Related Pages

- [Generate Documentation](generate-documentation.html)
- [Track Generation Progress](track-generation-progress.html)
- [View, Download, and Publish Docs](view-download-and-publish-docs.html)
- [Regenerate for New Branches and Models](regenerate-for-new-branches-and-models.html)
- [Install and Run docsfy Without Docker](install-and-run-docsfy-without-docker.html)

---

Source: configure-ai-providers.md

# Configure AI Providers

You want `docsfy` to reach a working AI provider CLI so documentation generation can start and finish without immediate provider errors. The important part is doing the install and sign-in in the same runtime environment as `docsfy-server`, because that server process makes the actual Claude, Gemini, and Cursor calls.

## Prerequisites

- Access to the machine or container that runs `docsfy-server`
- Internet access for provider CLI installation and sign-in
- A `docsfy` `user` or `admin` account to test a generation
- If you want to verify from the terminal, a configured `docsfy` CLI profile. See [Manage docsfy from the CLI](manage-docsfy-from-the-cli.html) for details.

## Quick Example

```bash
docsfy generate https://github.com/myk-org/for-testing-only \
  --provider cursor \
  --model gpt-5.4-xhigh-fast \
  --watch
```

If the `cursor` CLI is installed and authenticated where `docsfy-server` runs, `docsfy` moves on to cloning and page generation. Replace the repository URL with your own repository and swap in `claude` or `gemini` if that is the provider you configured.

> **Note:** If the `docsfy` CLI is not configured yet, run `docsfy config init` first. See [Manage docsfy from the CLI](manage-docsfy-from-the-cli.html) for details.

## Step-by-Step

```mermaid
flowchart LR
    A[Browser or local docsfy CLI] --> B[docsfy-server]
    B --> C[Claude, Gemini, or Cursor CLI]
    C --> D[Documentation generation]
```

1. Configure the provider on the server side of this flow.

| You are configuring | Where it matters | Why |
| --- | --- | --- |
| `docsfy` login or CLI profile | browser or local `docsfy` CLI | lets you reach the `docsfy` server |
| Provider CLI login | same machine and runtime user as `docsfy-server` | lets generation actually run |

> **Warning:** If your browser or local `docsfy` CLI talks to a remote `docsfy` server, install and authenticate the provider CLI on that remote server, not only on your laptop.

2. Install the provider CLI you plan to use.

The bundled `docsfy` container image already includes all three supported providers. If you run `docsfy` outside that image, these are the installer commands used by the project:

| Provider | Install command |
| --- | --- |
| `claude` | `curl -fsSL https://claude.ai/install.sh \| bash` |
| `cursor` | `curl -fsSL https://cursor.com/install \| bash` |
| `gemini` | `npm install -g @google/gemini-cli` |

Only `claude`, `gemini`, and `cursor` are accepted provider names. Make sure the same runtime user that starts `docsfy-server` can run the provider CLI from `PATH`.

3. Authenticate the provider CLI as that same runtime user.

- Open a shell in the environment where `docsfy-server` runs.
- Start the provider CLI and complete its normal sign-in or credential setup there.
- Confirm the same runtime user can still run the provider afterward.

> **Note:** `docsfy` authentication and provider authentication are separate. Signing in to `docsfy` does not sign the provider CLI in for you.

> **Warning:** Do not store provider tokens or other secrets in Git-tracked files.

4. Set server defaults if you want requests without an explicit provider or model to fall back to known values.

```env
AI_PROVIDER=cursor
AI_MODEL=gpt-5.4-xhigh-fast
AI_CLI_TIMEOUT=60
```

> **Note:** These settings choose defaults only. They do not authenticate Claude, Gemini, or Cursor for you.

Restart `docsfy-server` after changing these values. See [Configuration Reference](configuration-reference.html) for details.

5. Verify with a real generation.

Run the quick example above, or choose the same provider and model in the `New Generation` form in the web app. If provider setup is wrong, `docsfy` fails early instead of continuing to page generation. See [Generate Documentation](generate-documentation.html) for details.

> **Tip:** During setup, pass `--provider` and `--model` explicitly so you know exactly which provider/model pair you are testing.

<details><summary>Advanced Usage</summary>

**Check the server's provider list and remembered models**

```bash
docsfy models
docsfy models --provider cursor
```

This shows the fixed provider list, the current server defaults, and models remembered from successful generations. It is useful for discovery, but it is not a live health check for provider installation or login.

> **Tip:** If the model picker is empty or a new model is missing, type the model name manually. The dashboard accepts free-form model input and starts suggesting that model after a successful generation.

**Use the bundled container image**

The bundled image already installs Claude, Gemini, and Cursor. You still need to complete provider login inside that running environment before generation will work.

**Increase timeout for slower environments**

```env
AI_CLI_TIMEOUT=120
```

Raise this value if provider CLI calls time out on slower machines or larger repositories.

**Cursor trust handling**

When you choose `cursor`, `docsfy` adds Cursor's `--trust` flag automatically.

</details>

## Troubleshooting

- A run fails almost immediately: the provider CLI is usually missing, signed out, or using a model your account cannot access. Fix that on the `docsfy-server` host, then retry.
- The provider appears in the dashboard but generation still fails: the provider picker always offers `claude`, `gemini`, and `cursor`, so seeing the provider in the UI does not prove the CLI is installed or authenticated.
- No models are suggested: type the model name manually and rerun. Suggested models come from successful generations, not from a live provider catalog.
- New default settings seem ignored: restart `docsfy-server` after changing `AI_PROVIDER`, `AI_MODEL`, or `AI_CLI_TIMEOUT`.
- You need deeper diagnosis: see [Fix Setup and Generation Problems](fix-setup-and-generation-problems.html).

## Related Pages

- [Install and Run docsfy Without Docker](install-and-run-docsfy-without-docker.html)
- [Configuration Reference](configuration-reference.html)
- [Generate Documentation](generate-documentation.html)
- [Manage docsfy from the CLI](manage-docsfy-from-the-cli.html)
- [Fix Setup and Generation Problems](fix-setup-and-generation-problems.html)

---

Source: install-and-run-docsfy-without-docker.md

# Install and Run docsfy Without Docker

Use this guide to run `docsfy` directly from a local checkout instead of inside Docker. You will end with a local web app and CLI that both talk to the same server on your machine.

## Prerequisites

- A clone of the repository, with your shell opened at the repo root
- Python `3.12` or newer
- `uv`
- Node.js and `npm`
- `git`
- One supported AI provider CLI installed and authenticated on the same machine that will run `docsfy-server`

## Quick Example

```shell
cp .env.example .env
```

Use these values in `.env`:

```dotenv
ADMIN_KEY=change-this-to-a-16-plus-character-password
AI_PROVIDER=cursor
AI_MODEL=gpt-5.4-xhigh-fast
AI_CLI_TIMEOUT=60
LOG_LEVEL=INFO
DATA_DIR=.data
SECURE_COOKIES=false
```

```shell
uv sync --frozen

cd frontend
npm ci
npm run build
cd ..

uv run docsfy-server
```

In another terminal:

```shell
uv run docsfy --host localhost --port 8000 -u admin -p 'change-this-to-a-16-plus-character-password' health
```

Then open `http://localhost:8000/login` and sign in as `admin` with the same admin password.

> **Note:** This example uses a local `DATA_DIR` so you do not need a writable `/data` directory, and it sets `SECURE_COOKIES=false` so browser login works over plain `http://localhost`.

## Step-by-Step

1. Install the Python dependencies.

```shell
uv sync --frozen
```

Run `uv run ...` commands from the repo root after this so both `docsfy-server` and `docsfy` use the project environment.

2. Create your local `.env` file.

```shell
cp .env.example .env
```

Use these values in `.env`:

```dotenv
ADMIN_KEY=change-this-to-a-16-plus-character-password
AI_PROVIDER=cursor
AI_MODEL=gpt-5.4-xhigh-fast
AI_CLI_TIMEOUT=60
LOG_LEVEL=INFO
DATA_DIR=.data
SECURE_COOKIES=false
```

| Put in `.env` | Set when you start the server |
| --- | --- |
| `ADMIN_KEY`, `AI_PROVIDER`, `AI_MODEL`, `AI_CLI_TIMEOUT`, `LOG_LEVEL`, `DATA_DIR`, `SECURE_COOKIES` | `HOST`, `PORT`, `DEBUG` |

- `ADMIN_KEY` is required, and the server rejects values shorter than 16 characters.
- `DATA_DIR=.data` keeps the database and generated output in a writable local directory.
- `SECURE_COOKIES=false` is the right choice for plain local `http://` use.
- Change `AI_PROVIDER` and `AI_MODEL` now if your machine is set up for `claude` or `gemini` instead of `cursor`.

> **Note:** Start `docsfy-server` from this repo root so it picks up the `.env` file.

3. Build the web app that the server will serve.

```shell
cd frontend
npm ci
npm run build
cd ..
```

This builds the browser UI before the backend starts.

4. Start the server.

```shell
uv run docsfy-server
```

By default, this listens on `127.0.0.1:8000`, so `http://localhost:8000` works locally.

```mermaid
flowchart LR
  A[Create .env] --> B[Build the web app]
  B --> C[Start docsfy-server]
  C --> D[Open localhost:8000/login]
  C --> E[Run docsfy CLI commands]
```

> **Warning:** When you later run generations, the selected provider CLI must already be installed and authenticated for the same machine and user account that started `docsfy-server`.

5. Configure the CLI once and verify the server.

```shell
uv run docsfy config init
```

Use these values when prompted:

- Profile name: `dev`
- Server URL: `http://localhost:8000`
- Username: `admin`
- Password: the same value as `ADMIN_KEY`

Then verify the connection:

```shell
uv run docsfy health
```

You should see the server URL and an `ok` status.

> **Tip:** `docsfy config init` writes `~/.config/docsfy/config.toml` with owner-only permissions automatically.

6. Sign in from the browser.

Open `http://localhost:8000/login`, then sign in as `admin` with the same admin password you used in `.env`.

See [CLI Command Reference](cli-command-reference.html) for details.

<details><summary>Advanced Usage</summary>

**Bind to another address or enable reload**

```shell
HOST=0.0.0.0 PORT=8000 DEBUG=true uv run docsfy-server
```

`HOST`, `PORT`, and `DEBUG` are read when `docsfy-server` starts, so set them in the shell or service manager that launches it.

**Run CLI commands without saving a profile**

```shell
uv run docsfy --host localhost --port 8000 -u admin -p 'change-this-to-a-16-plus-character-password' list
```

This is handy for one-off checks and scripts.

**Use Vite instead of a prebuilt frontend while iterating on the UI**

```shell
DEBUG=true uv run docsfy-server
```

```shell
cd frontend
API_TARGET=http://localhost:8000 npm run dev
```

Open `http://localhost:5173`. The commented `DEV_MODE` line in `.env.example` is for the container entrypoint, not for a direct `uv run docsfy-server` launch.

**Change the default provider, model, or CLI timeout**

```dotenv
AI_PROVIDER=gemini
AI_MODEL=gemini-2.5-pro
AI_CLI_TIMEOUT=120
```

Set these in `.env` when you want different defaults for new runs.

**Optional Mermaid rendering**

```shell
npm install -g @mermaid-js/mermaid-cli@11
```

If `mmdc` is on `PATH`, local renders can prerender Mermaid diagrams. On some Linux hosts you may also need Chromium and a Puppeteer config in `~/.puppeteerrc.json`.

</details>

## Troubleshooting

- If `docsfy-server` exits immediately, make sure `ADMIN_KEY` is set and at least 16 characters long.
- If browser sign-in keeps returning to `/login` on `http://localhost`, set `SECURE_COOKIES=false` in `.env`.
- If the UI says the frontend is not built, or static assets are missing, run `cd frontend && npm ci && npm run build`, then restart `uv run docsfy-server`.
- If you get permission errors under `/data`, point `DATA_DIR` at a writable directory such as `.data` or a folder under your home directory.
- If `uv run docsfy health` says no server is configured, run `uv run docsfy config init`, or pass `--host`, `--port`, `-u`, and `-p` for a one-off command.
- If a generation fails before it starts, install and authenticate the selected provider CLI on the same machine and for the same user that runs `docsfy-server`.

## Related Pages

- [Generate Your First Docs Site](generate-your-first-docs-site.html)
- [Configure AI Providers](configure-ai-providers.html)
- [Generate Documentation](generate-documentation.html)
- [Manage docsfy from the CLI](manage-docsfy-from-the-cli.html)
- [Configuration Reference](configuration-reference.html)

---

Source: generate-documentation.md

# Generate Documentation

Use the dashboard to start a new documentation run for a Git repository, choose the branch, provider, and model you want, and get a browsable docs site from the same screen. This is the quickest path when you want to add a repo and launch generation without switching to a terminal.

## Prerequisites

- Signed in to the dashboard as a `user` or `admin`
- A remote Git repository URL in HTTPS or `git@...` form
- Git access from the machine running docsfy if the repository is private or uses SSH
- A provider and model that your docsfy server can run

## Quick Example

```text
Repository URL: https://github.com/myk-org/for-testing-only
Branch: main
Provider: gemini
Model: gemini-2.5-flash
Force full regeneration: off
```

Click `New Generation`, enter those values, then click `Generate`. If your server uses a different provider or model, swap those two fields and keep the rest of the flow the same.

## Step-by-Step

1. Open the form.  
   In the dashboard sidebar, click `New Generation`.

2. Enter the repository URL.  
   Paste the remote URL for the repository you want to document. docsfy uses the repository name from that URL as the project name, so `https://github.com/myk-org/for-testing-only` appears in the sidebar as `for-testing-only`.

3. Choose the branch.  
   Leave the default `main` branch or type another branch such as `dev`.

> **Warning:** Branch names cannot contain `/`. Use `release-1.x`, not `release/1.x`.

4. Choose the provider and model.  
   Pick one of `claude`, `gemini`, or `cursor`, then choose a model from the suggestions or type one manually. The form starts with `cursor` selected, but you can change it before you submit.

> **Tip:** If the model list is empty, type the model name yourself. Suggestions only appear after successful generations have already recorded known models.

5. Decide whether to force a full rebuild.  
   Leave `Force full regeneration` off for a normal run. Turn it on when you want docsfy to ignore cached pages and rebuild everything from scratch.

6. Start the run.  
   Click `Generate`. The new run is added to the sidebar, the dashboard opens its detail view automatically, and the status changes to `Generating`.

7. Wait for the ready state.  
   When the run finishes, the selected item shows `Ready`. At that point you can open or download the generated site from the same view.

```mermaid
flowchart LR
  A[Click New Generation] --> B[Enter repo URL, branch, provider, and model]
  B --> C[Click Generate]
  C --> D[Dashboard opens the new run]
  D --> E[Generating]
  E --> F[Ready]
```

<details><summary>Advanced Usage</summary>

### Accepted repository URL formats

```text
https://github.com/org/repo
https://github.com/org/repo.git
git@github.com:org/repo.git
```

> **Note:** SSH URLs work only if the machine running docsfy already has Git access to that host.

### Normal run vs forced rebuild

| Setting | When to use it | What to expect |
| --- | --- | --- |
| `Force full regeneration` off | Everyday runs | docsfy can reuse existing work, skip unchanged runs, or update only what changed |
| `Force full regeneration` on | Clean rebuilds, retries after a bad result, or when you want to ignore cached pages | docsfy clears cached page output and rebuilds the docs from scratch |

### Branch and model suggestions

- The branch field suggests branches that already have successful generations for the same repository name.
- The model field suggests models that already have successful generations for the selected provider.
- You can still type a new branch or model even when there is no suggestion.

### Repository URL rules

- Use a standard hosted Git remote in `host/owner/repo` form.
- Do not use a local filesystem path in the dashboard.
- URLs that point to `localhost` or private-network addresses are rejected.

### Terminal alternative

If you prefer to start generations from a shell, see [Manage docsfy from the CLI](manage-docsfy-from-the-cli.html).

</details>

## Troubleshooting

- **The repository URL is rejected:** Use an HTTPS URL like `https://github.com/org/repo.git` or an SSH URL like `git@github.com:org/repo.git`.
- **The branch is rejected:** Remove `/` from the branch name and try a name such as `release-1.x`.
- **No model suggestions appear:** Type the model name manually. Suggestions are learned from successful generations.
- **The run fails during clone:** Make sure the docsfy server can reach the Git host and authenticate to the repository.
- **You get an "already being generated" error:** That exact branch, provider, and model combination is already running. Wait for it to finish, or open it and abort the current run before retrying.
- **You can view docs but cannot start a generation:** Your account is probably `viewer` role. Ask an admin for `user` or `admin` access.
- **The run finishes almost immediately:** docsfy may have detected that the selected commit is already covered. If you need a clean rebuild, start another run with `Force full regeneration` turned on.

## Related Pages

- [Track Generation Progress](track-generation-progress.html)
- [Regenerate for New Branches and Models](regenerate-for-new-branches-and-models.html)
- [View, Download, and Publish Docs](view-download-and-publish-docs.html)
- [Manage docsfy from the CLI](manage-docsfy-from-the-cli.html)
- [Fix Setup and Generation Problems](fix-setup-and-generation-problems.html)

---

Source: track-generation-progress.md

# Track Generation Progress

Keep an eye on a generation so you know whether to wait, retry, or stop it before spending more AI time. This guide shows how to follow a run in the dashboard and from the terminal, and how to tell the difference between a normal long run and a fast "already up to date" result.

## Prerequisites
- A generation has already been started. If not, see [Generate Your First Docs Site](generate-your-first-docs-site.html).
- To abort a run, sign in as a `user` or `admin`. A `viewer` can monitor progress, but cannot stop or regenerate runs.
- For terminal examples, configure `docsfy` to reach your server. See [CLI Command Reference](cli-command-reference.html) and [Configuration Reference](configuration-reference.html).

## Quick Example

```shell
docsfy generate https://github.com/myk-org/for-testing-only --provider gemini --model gemini-2.5-flash --watch
```

```text
Project: for-testing-only
Branch: main
Status: generating
Watching generation progress...
[generating] cloning
[generating] planning
[generating] generating_pages (3 pages)
Generation complete! (9 pages)
```

Use `--watch` when you want live terminal updates until the run finishes as `ready`, `error`, or `aborted`.

## Step-by-step

1. Open the exact variant you want to watch.

   In the dashboard sidebar, expand the repository and branch, then select the provider/model variant. Even when a repository group is collapsed, docsfy still shows how many variants are `ready`, `generating`, `failed`, or `aborted`, which makes active work easy to spot.

2. Check the status badge first.

   | Status | What it means | What to do next |
   | --- | --- | --- |
   | `generating` | The run is still active. | Keep watching, or abort if you started the wrong run. |
   | `ready` | The docs finished successfully. | Open or download the result. See [View, Download, and Publish Docs](view-download-and-publish-docs.html). |
   | `error` | The run failed. | Regenerate after fixing the problem. |
   | `aborted` | A user stopped the run. | Review the message, then regenerate or delete the variant. |

3. Read the activity log and progress bar together.

```mermaid
flowchart LR
  A[cloning] --> B{What kind of run is this?}
  B -->|Fresh work| C[planning]
  B -->|Reuse earlier work| D[incremental_planning]
  B -->|Nothing changed| J[ready: up to date]
  C --> E[generating_pages]
  D --> E
  E --> F[validating]
  F --> G[cross_linking]
  G --> H[rendering]
  H --> I[ready]
```

   | Stage | What you are waiting for |
   | --- | --- |
   | `cloning` | docsfy is preparing the repository source. |
   | `planning` | docsfy is building a fresh documentation plan. |
   | `incremental_planning` | docsfy is deciding what can be reused from an earlier variant. |
   | `generating_pages` | Pages are being written or updated. |
   | `validating` | The generated pages are being checked against the repository. |
   | `cross_linking` | Cross-page links are being added. |
   | `rendering` | The final docs site is being built. |

> **Note:** The progress bar appears only after planning finishes, because docsfy does not know the total page count until the plan exists.

4. Interpret the page counter correctly.

   `X of Y pages` tracks page writing, not the whole run. If the counter reaches `Y of Y` and the status still says `generating`, docsfy is usually finishing `validating`, `cross_linking`, or `rendering`.

> **Tip:** Treat `ready` as the real finish line. The page counter can hit `100%` before the final site is done.

5. Recognize the fast-success case.

   A run can finish quickly without rebuilding everything. When docsfy decides the target docs are already current, the variant still ends as `ready`, and the dashboard shows **Documentation is already up to date.**

6. Abort a run when you need to stop it.

   Open the generating variant, click `Abort Generation`, and confirm the dialog. The variant stays in the list with status `aborted`, so you can review what happened and start again with different settings if needed.

> **Warning:** Aborting discards in-flight progress for that run.

7. Use the finished variant right away.

   When the status changes to `ready`, the detail view shows `View Documentation` and `Download`. See [View, Download, and Publish Docs](view-download-and-publish-docs.html) for the next step.

<details><summary>Advanced Usage</summary>

Monitor a repository from the terminal without opening the dashboard:

```shell
docsfy status for-testing-only
```

Use that when you want to scan every variant for the repository.

```shell
docsfy status for-testing-only --branch main --provider gemini --model gemini-2.5-flash
```

Use the fully specified form when you want one variant's current status, stage, page count, update time, and commit.

Abort a specific running variant from the terminal:

```shell
docsfy abort for-testing-only --branch main --provider gemini --model gemini-2.5-flash
```

This is the safest abort form when the same repository may be running under more than one branch or model.

You can also use the shorter command:

```shell
docsfy abort for-testing-only
```

That shortcut works only when exactly one active variant matches that repository name.

A few progress patterns are worth knowing:

- A non-force regenerate can reuse earlier work, so the log may show `incremental_planning` instead of `planning`.
- Incremental runs can show a page count above `0` sooner than a full regenerate.
- A no-op regenerate ends as `ready` and shows **Documentation is already up to date.**
- If the dashboard stops updating instantly, leave it open for a moment before refreshing; it retries live updates automatically.
- If `--watch` disconnects, rerun `docsfy status ...` to check the latest state.

See [CLI Command Reference](cli-command-reference.html) for more command syntax.

</details>

## Troubleshooting
- **`Abort Generation` is missing:** your account is probably `viewer`. `viewer` can monitor accessible variants, but only `user` and `admin` can stop runs. See [Manage Users, Roles, and Access](manage-users-roles-and-access.html) for details.
- **The counter says `100%` but the run is still `generating`:** page writing is done, but docsfy is still validating, cross-linking, or rendering.
- **`docsfy abort <name>` says more than one variant is active:** rerun the command with `--branch`, `--provider`, and `--model`.
- **Abort says the run already finished or is still in progress:** refresh status, wait a moment, and retry only if the variant is still `generating`.
- **A run changed from `generating` to `error` after a restart:** the server marks interrupted runs as failed instead of leaving them stuck forever. Start the run again.

## Related Pages

- [Generate Documentation](generate-documentation.html)
- [View, Download, and Publish Docs](view-download-and-publish-docs.html)
- [Regenerate for New Branches and Models](regenerate-for-new-branches-and-models.html)
- [Manage docsfy from the CLI](manage-docsfy-from-the-cli.html)
- [WebSocket Reference](websocket-reference.html)

---

Source: regenerate-for-new-branches-and-models.md

# Regenerate for New Branches and Models

You want another docs variant from the same repository so you can cover a different branch or switch models without rebuilding everything by hand. In docsfy, branch changes and model changes behave differently, so the fastest path depends on what you are changing.

## Prerequisites
- A running docsfy instance and a login with `user` or `admin` access. If you still need setup help, see [Generate Your First Docs Site](generate-your-first-docs-site.html) or [Install and Run docsfy Without Docker](install-and-run-docsfy-without-docker.html).
- The repository URL you want to regenerate.
- A provider/model that already works in your environment.
- If you use the CLI, a configured `docsfy` connection.

## Quick Example
```shell
# New branch
docsfy generate https://github.com/myk-org/for-testing-only \
  --branch dev \
  --provider gemini \
  --model gemini-2.5-flash
```

```shell
# New model on the same branch
docsfy generate https://github.com/myk-org/for-testing-only \
  --branch main \
  --provider gemini \
  --model gemini-2.0-flash
```

> **Tip:** The branch and model fields are editable. Suggestions only come from ready variants, so you can type a value even when it is not listed yet.

## Step-by-step
1. Create a new branch variant.

```shell
docsfy generate https://github.com/myk-org/for-testing-only \
  --branch dev \
  --provider gemini \
  --model gemini-2.5-flash \
  --watch
```

Use the same repo URL and change only the branch. In the dashboard, do the same thing from `New Generation`: keep the repository URL, type the new branch, choose the provider/model, and click `Generate`.

> **Note:** If you omit `--branch`, docsfy uses `main`.

2. Create a new model variant on the same branch.

```shell
docsfy generate https://github.com/myk-org/for-testing-only \
  --branch main \
  --provider gemini \
  --model gemini-2.0-flash
```

Keep the branch the same and change the model. In the dashboard, select the ready variant in the sidebar and use `Regenerate Documentation`; that form changes the provider/model but keeps the selected branch.

3. Check the exact variant you started.

```shell
docsfy status for-testing-only \
  --branch main \
  --provider gemini \
  --model gemini-2.0-flash
```

Use the branch, provider, and model together so you do not confuse one variant with another. In the dashboard, a new branch appears under its own `@branch` section in the sidebar.

4. Choose whether you want reuse or a full rebuild.

| Situation | What docsfy does |
| --- | --- |
| Same branch, same model, no force | Updates the existing variant in place. If the repo is unchanged, the run finishes as already up to date. |
| Same branch, same model, `--force` | Rebuilds that variant from scratch. |
| Same branch, different model, no force | Reuses the newest ready variant on that branch when possible, then removes the older source variant after the new one is ready. |
| Same branch, different model, `--force` | Runs a full generation and keeps the existing variant, so both variants can remain. |
| Different branch | Creates a separate branch variant. Reuse starts only after that branch has its own ready variant. |

> **Warning:** A non-force model switch is not additive. If you want to keep both the old and new model variants on the same branch, turn on `Force full regeneration`.

5. Confirm the result in the UI.

Select the ready variant and check its branch, provider/model, page count, and latest commit. If docsfy reused an unchanged variant, the ready view says the documentation is already up to date.

<details><summary>Advanced Usage</summary>

```mermaid
flowchart TD
  A[Start a generation] --> B{Different branch?}
  B -- Yes --> C[Create a separate branch variant]
  C --> D[Later reuse stays on that branch]
  B -- No --> E{Different model or provider?}
  E -- No --> F{Force enabled?}
  F -- Yes --> G[Full rebuild in place]
  F -- No --> H{Repo changed?}
  H -- No --> I[Mark variant up to date]
  H -- Yes --> J[Reuse plan and unchanged pages when possible]
  E -- Yes --> K{Force enabled?}
  K -- Yes --> L[Full rebuild and keep the existing variant]
  K -- No --> M[Reuse the newest ready variant on that branch]
  M --> N{Same commit?}
  N -- Yes --> O[Copy existing docs into the new variant]
  N -- No --> P[Regenerate only what changed when possible]
  O --> Q[Replace the older source variant]
  P --> Q
```

```shell
docsfy models --provider gemini
```

Use this to see the provider list, the server-marked defaults, and model names from completed generations. If you plan to rely on omitted provider/model values, check [Configuration Reference](configuration-reference.html) first.

A few reuse rules matter in practice:

- Reuse is branch-scoped. A ready `main` variant is not used as the starting point for a first-time `dev` variant.
- A same-commit model switch without force can finish by copying the existing docs into the new variant instead of starting over.
- A new commit on the same branch without force can reuse the existing plan and regenerate only changed pages when docsfy can tell what changed.
- A new commit with no file changes is treated as up to date.
- If docsfy cannot figure out what changed or cannot reuse the earlier plan, it falls back to a full regeneration automatically.
- The same replacement rules apply when you switch providers, not just when you switch models.
- In the dashboard, errored and aborted variants reopen with `Force full regeneration` enabled by default.

```shell
docsfy status for-testing-only
```

Use this when you want a full list of branch/provider/model variants for one repository.

> **Warning:** Branch names cannot contain `/`. Use a slash-free name such as `release-1.x`.

For full command syntax, see [CLI Command Reference](cli-command-reference.html). If you automate this outside the CLI, see [HTTP API Reference](http-api-reference.html) and [WebSocket Reference](websocket-reference.html).

</details>

## Troubleshooting
- `Clone failed` or branch not found: verify the branch exists in the remote repository and that the server can access that repository.
- A branch name like `release/v2.0` is rejected: docsfy only accepts branch names without slashes. Use something like `release-v2.0` or `release-1.x`.
- The model you want is not in the dropdown: type it manually or run `docsfy models --provider <provider>`. The suggestion list only includes models from ready variants.
- The regenerate controls are missing: `viewer` access is read-only. See [Manage Users, Roles, and Access](manage-users-roles-and-access.html) for permission changes.
- The previous model disappeared after a switch: that is expected after a non-force model change on the same branch. Rerun with `--force` if you want both variants to remain.

## Related Pages

- [Generate Documentation](generate-documentation.html)
- [Track Generation Progress](track-generation-progress.html)
- [View, Download, and Publish Docs](view-download-and-publish-docs.html)
- [Manage docsfy from the CLI](manage-docsfy-from-the-cli.html)
- [HTTP API Reference](http-api-reference.html)

---

Source: view-download-and-publish-docs.md

# View, Download, and Publish Docs

Open generated docs when you need a fast browser view, then download the same output when you want a stable artifact to review, archive, or host somewhere else. The key choice is whether you want a moving "latest" link or one exact variant pinned to a branch, provider, and model.

## Prerequisites

- A docs variant already exists and is `ready`.
- You can sign in to docsfy in the browser, or you have a valid API key for `Authorization: Bearer ...`.
- If you use the CLI, `docsfy` is already configured to reach your server.

## Quick Example

```text
$SERVER/docs/<project>/
```

```shell
curl -s -L -H "Authorization: Bearer $API_KEY" \
  "$SERVER/api/projects/<project>/<branch>/<provider>/<model>/download" \
  -o "<project>-<branch>-<provider>-<model>-docs.tar.gz"
mkdir -p published-docs
tar -xzf "<project>-<branch>-<provider>-<model>-docs.tar.gz" \
  --strip-components=1 \
  -C published-docs
```

Open the first URL when you just want the newest ready docs you can access. Use the download command when you want one exact build that you can extract and publish as a normal static site.

## Step-by-Step

1. Choose whether you want a moving link or a pinned build.

| If you want... | Open in the browser | Download the site | Downloaded file |
|---|---|---|---|
| The latest ready docs you can access | `/docs/<project>/` | `/api/projects/<project>/download` | `<project>-docs.tar.gz` |
| One exact build | `/docs/<project>/<branch>/<provider>/<model>/` | `/api/projects/<project>/<branch>/<provider>/<model>/download` | `<project>-<branch>-<provider>-<model>-docs.tar.gz` |

> **Warning:** The short project routes move when a newer ready variant becomes available. Use the full variant URL when you need a stable bookmark, review link, or published release artifact.

2. Open the docs in the browser.

```text
$SERVER/docs/<project>/
$SERVER/docs/<project>/<branch>/<provider>/<model>/
```

If you are using the web app, open the ready variant and click **View Documentation**. Use the full variant URL when the branch, provider, and model must stay fixed.

3. Download the exact site you want to publish.

```shell
curl -s -L -H "Authorization: Bearer $API_KEY" \
  "$SERVER/api/projects/<project>/<branch>/<provider>/<model>/download" \
  -o "<project>-<branch>-<provider>-<model>-docs.tar.gz"
```

If you prefer the web app, click **Download** on the ready variant. Exact downloads are the safest choice for publishing because they stay pinned to the build you selected.

> **Note:** Exact variant downloads return `400` until that variant is `ready`. The short project download returns `404` if there is no ready variant it can use.

4. Extract the archive into a publishable folder.

```shell
mkdir -p published-docs
tar -xzf "<project>-<branch>-<provider>-<model>-docs.tar.gz" \
  --strip-components=1 \
  -C published-docs
ls published-docs
```

After extraction, keep the generated bundle together. The folder includes `index.html`, generated page HTML files, `assets/`, `search-index.json`, `llms.txt`, and `llms-full.txt`.

5. Publish the extracted folder like any other static site.

```mermaid
flowchart LR
  A["Choose a ready variant"] --> B{"Latest or exact?"}
  B -->|Latest| C["Open the latest docs route"]
  B -->|Exact| D["Download the exact variant archive"]
  D --> E["Extract the archive"]
  E --> F["Upload the extracted folder"]
  F --> G["Serve index.html"]
```

Upload the contents of `published-docs/` to your static host or web server, and use `index.html` as the entry point. There is no separate external publish command in docsfy; once extracted, the output is a normal static site.

> **Tip:** The generated site includes a `.nojekyll` file, so you can publish it as-is on GitHub Pages-style static hosting.

> **Warning:** Once you publish the files outside docsfy, docsfy's sign-in and access checks no longer protect them. Use your host, CDN, or proxy for access control if the published docs must stay private.

<details><summary>Advanced Usage</summary>

### Download with the CLI

```shell
docsfy download <project>
docsfy download <project> --branch <branch> --provider <provider> --model <model>
docsfy download <project> --branch <branch> --provider <provider> --model <model> --output ./published-docs
```

Use `docsfy download <project>` for the latest ready archive. Add `--branch`, `--provider`, and `--model` together to pin the download to one exact variant. With `--output`, the CLI extracts the archive instead of keeping the `.tar.gz`.

> **Note:** The CLI extracts the archive as-is. If you use `--output`, the extracted files stay inside the archive's top-level folder, so point your static host at that folder or move its contents up one level.

### Admin owner disambiguation

```text
$SERVER/docs/<project>/<branch>/<provider>/<model>/?owner=<username>
$SERVER/api/projects/<project>/<branch>/<provider>/<model>/download?owner=<username>
```

If an admin has the same project and variant name under multiple owners, use the full variant route and add `?owner=<username>` to choose the right one. The short "latest" routes do not let you pick an owner.

### Keep the full published bundle

When you publish elsewhere, upload the generated files together rather than picking only `index.html`. In particular:

- Keep `assets/` with the HTML files.
- Keep `search-index.json` if you want built-in search to work.
- Keep `llms.txt` and `llms-full.txt` if you want the published LLM-friendly copies.
- Keep the generated `*.md` page files if you want the links inside `llms.txt` to keep working.

### Exact URLs and branch names

The branch name is part of the exact variant URL. Use path-safe branch names such as `main`, `dev`, or `release-1.x`; slash-style branch names are not supported in these routes.

See [HTTP API Reference](http-api-reference.html) for raw endpoint details and [CLI Command Reference](cli-command-reference.html) for every CLI flag.

</details>

## Troubleshooting

- Getting redirected to `/login` or seeing `401`:
  sign in to the web app first, or send `Authorization: Bearer <api-key>` with direct requests.
- Getting `404` from a docs or download URL:
  there may be no ready variant for that route, or you may not have access to that project's docs.
- Seeing `400 Variant not ready` on an exact download:
  wait until generation finishes, then retry the same exact URL.
- Published pages load without styling or search:
  upload the whole extracted folder, not just `index.html`.
- The CLI says you must specify `--branch`, `--provider`, and `--model` together:
  provide all three to target one exact variant, or omit all three to use the latest ready archive.

See [Fix Setup and Generation Problems](fix-setup-and-generation-problems.html) for broader auth, access, and readiness issues.

## Related Pages

- [Track Generation Progress](track-generation-progress.html)
- [Generate Documentation](generate-documentation.html)
- [Manage Users, Roles, and Access](manage-users-roles-and-access.html)
- [Manage docsfy from the CLI](manage-docsfy-from-the-cli.html)
- [HTTP API Reference](http-api-reference.html)

---

Source: manage-users-roles-and-access.md

# Manage Users, Roles, and Access

Use this page to add teammates, give them the right level of access, rotate passwords when access changes, and share only the docs each person should see. This lets readers open finished docs without giving everyone permission to generate, delete, or administer projects.

## Prerequisites

- An admin account. Use the built-in `admin` account or a user you created with the `admin` role.
- If you want to use the CLI, set it up once with `docsfy config init`. See [Manage docsfy from the CLI](manage-docsfy-from-the-cli.html).
- The project you want to share must already exist. See [Generate Documentation](generate-documentation.html).

## Quick example

```bash
docsfy admin users create alice --role viewer
docsfy admin access grant my-repo --username alice --owner admin
docsfy admin access list my-repo --owner admin
```

This creates a read-only account, shares the `my-repo` docs owned by `admin`, and confirms the grant. Replace `admin` in `--owner admin` with the username that owns the project in your deployment.

## Step-by-step

1. Choose the role first.

| Role | Use it for | Can generate, delete, or abort docs? | Can manage users and access? |
| --- | --- | --- | --- |
| `admin` | People who administer docsfy | Yes | Yes |
| `user` | People who create and manage their own docs | Yes | No |
| `viewer` | People who only need to read docs | No | No |

Role controls what someone can do. Project access controls which docs they can see.

```mermaid
flowchart LR
  A[Assign role] --> B[What the user can do]
  C[Grant project access] --> D[Which docs the user can open]
```

> **Tip:** If someone only needs to read generated docs, start with `viewer`.

2. Create the user.

```bash
docsfy admin users create alice --role viewer
docsfy admin users list
```

Run this as an admin. docsfy shows the generated password once, so save it and share it securely with the user.

> **Warning:** Save the generated password immediately. docsfy does not show it again after creation.

Admins can also create users from the dashboard's `Users` section.

> **Note:** The same secret is used as the dashboard password and the CLI password/API key.

3. Grant access to the project.

```bash
docsfy admin access grant my-repo --username alice --owner admin
docsfy admin access list my-repo --owner admin
```

Use the project owner's username in `--owner`. This matters when more than one person has docs for a project with the same name.

> **Tip:** One access grant covers every branch and every provider/model variant for that owner's project.

After the grant, the user can sign in and open the shared project from the dashboard. See [View, Download, and Publish Docs](view-download-and-publish-docs.html) for opening exact variants or downloading a shared site.

4. Rotate passwords when access changes.

```bash
docsfy admin users rotate-key alice
```

Use this when a password is lost, a person changes teams, or you want to replace a temporary password. docsfy returns the new password immediately, invalidates the old one, and ends the user's current sessions.

Created users can also change their own password from the dashboard sidebar with `Change Password`. After a self-service change, they must sign in again with the new password. Admins can also rotate another user's password from the dashboard's `Users` section.

5. Remove access or remove the user.

```bash
docsfy admin access revoke my-repo --username alice --owner admin
docsfy admin users delete alice --yes
```

Use revoke when the person should keep their account but stop seeing one project's docs. Use delete when the account should be removed entirely.

| If you want to... | Use |
| --- | --- |
| Keep the account but hide one project's docs | `docsfy admin access revoke ...` |
| Remove the account and its access grants | `docsfy admin users delete ... --yes` |

Revoking access removes that project's docs from the user's view. Deleting the user removes the account itself.

<details><summary>Advanced Usage</summary>

Use a specific replacement password instead of an auto-generated one:

```bash
docsfy admin users rotate-key alice --new-key "my-very-secure-password-123"
```

Custom passwords must be at least 16 characters long.

You can also run admin commands without saving a CLI profile:

```bash
docsfy --host localhost --port 8000 -u admin -p "<ADMIN_KEY>" admin users list
```

Two admin account types behave slightly differently:

| Admin type | Sign-in name | Can use admin tools? | Can change its own password from the dashboard? |
| --- | --- | --- | --- |
| Built-in admin | `admin` | Yes | No |
| Admin user you created | Their username | Yes | Yes |

> **Warning:** If you change `ADMIN_KEY`, previously issued passwords for created users stop working. Plan to rotate those passwords too.

Deleting a user is a full cleanup step. It removes the account, ends that user's sessions, removes their access grants, and removes projects they owned.

> **Warning:** You cannot create a user named `admin`, and the currently signed-in admin cannot delete their own account.

</details>

## Troubleshooting

- `No server configured`: run `docsfy config init`, or use `--host`, `--port`, `-u`, and `-p` for a one-off command. See [Manage docsfy from the CLI](manage-docsfy-from-the-cli.html).
- `Project not found` when granting access: make sure the project has already been generated and that `--owner` matches the account that owns it. See [Generate Documentation](generate-documentation.html).
- A `viewer` cannot generate or delete docs: that is expected. Create a `user` instead if they need write access.
- The built-in `admin` account cannot change its own password from the dashboard: change the server's `ADMIN_KEY`, then sign in again.
- Deleting a user fails while they have a generation in progress: wait for the run to finish, or stop it first. See [Track Generation Progress](track-generation-progress.html).
- A user is sent back to the login page right after a password change: that is expected. Sign in again with the new password.

## Related Pages

- [Manage docsfy from the CLI](manage-docsfy-from-the-cli.html)
- [View, Download, and Publish Docs](view-download-and-publish-docs.html)
- [HTTP API Reference](http-api-reference.html)
- [WebSocket Reference](websocket-reference.html)
- [Generate Documentation](generate-documentation.html)

---

Source: manage-docsfy-from-the-cli.md

# Manage docsfy from the CLI

Use the CLI when you want to manage docsfy from a shell, script, or remote session instead of the browser. A saved server profile makes health checks, generations, status lookups, downloads, and admin tasks faster and easier to repeat.

## Prerequisites
- A running docsfy server you can reach
- The `docsfy` command available on your machine
- An API key for that server
- For the built-in admin account, the username is `admin`
- For generation, a server that already has a working AI provider configured

## Quick Example
```shell
docsfy config init
docsfy health
docsfy generate https://github.com/org/my-repo.git --watch
docsfy status my-repo
docsfy download my-repo
```

In this flow, `my-repo` is the project name derived from the Git URL. `download` fetches the newest ready variant you can access.

```mermaid
flowchart LR
  A[Save profile] --> B[Check health]
  B --> C[Start generation]
  C --> D[Watch or check status]
  D --> E[Download docs]
```

## Step-by-Step
1. Save a reusable server profile.

```shell
docsfy config init
docsfy config show
```

When `docsfy config init` asks for `Password`, enter your API key.

A saved profile looks like this:

```toml
[default]
server = "dev"

[servers.dev]
url = "http://localhost:8000"
username = "admin"
password = "<your-dev-key>"
```

The first profile you create becomes the default server. Later profiles are added without changing the existing default.

> **Note:** The CLI stores profiles in `~/.config/docsfy/config.toml`, masks saved passwords in `docsfy config show`, and writes the file with owner-only permissions.

2. Confirm that the CLI can reach the right server.

```shell
docsfy health
```

A healthy server returns a short result like this:

```text
Server: http://localhost:8000
Status: ok
```

Run this first whenever you switch profiles or change environments.

3. Start a generation from a Git remote.

```shell
docsfy generate https://github.com/org/my-repo.git --watch
```

Use a normal HTTPS or SSH Git URL. If you omit `--branch`, docsfy uses `main`. If you omit `--provider` and `--model`, the server uses its current defaults.

Later CLI commands use the repository name, so `https://github.com/org/my-repo.git` becomes `my-repo`.

> **Warning:** `docsfy generate` takes a Git URL, not a local filesystem path.

> **Warning:** Branch names cannot contain `/`. Use names like `release-1.x`, not `release/1.x`.

If you want to generate another branch or model on purpose, see [Regenerate for New Branches and Models](regenerate-for-new-branches-and-models.html).

4. Check what is running and inspect the result.

```shell
docsfy list
docsfy status my-repo
```

`list` gives you a table of accessible variants. Each row is one variant, so the same project can appear more than once.

`status` shows the details for one project, including the owner, current status, page count, last update time, short commit, current stage, and any error message.

If you want one exact variant instead of every variant for the project, pass all three selectors together:

```shell
docsfy status my-repo --branch main --provider cursor --model gpt-5.4-xhigh-fast
```

For the full meaning of statuses and stages, see [Track Generation Progress](track-generation-progress.html).

5. Download the finished site.

```shell
docsfy download my-repo
```

This saves the newest ready variant you can access as a `.tar.gz` file in your current directory.

If you want to unpack it immediately instead:

```shell
docsfy download my-repo --output ./site
```

> **Tip:** Use exact selectors when you need one specific variant instead of the latest ready one.

See [CLI Command Reference](cli-command-reference.html) for the full syntax and every flag.

<details><summary>Advanced Usage</summary>

### Switch servers or override a saved profile

```shell
docsfy config set default.server prod
docsfy --host myhost --port 9000 -u admin -p <your-api-key> health
```

Use saved profiles for everyday work, and one-off flags when you need to hit a different host without changing `~/.config/docsfy/config.toml`.

| If `-p` appears... | It means... |
| --- | --- |
| Before the subcommand, like `docsfy -p <your-api-key> health` | API key/password |
| After `status`, `delete`, `abort`, or `download`, like `docsfy status my-repo -p cursor` | AI provider |

> **Tip:** Global connection flags go before the subcommand.

### Discover providers and models

```shell
docsfy models
docsfy models --provider cursor
docsfy models --json
```

Use `models` when you want to see the server defaults and the model names the server already knows about.

### Work with exact variants, aborts, and deletes

```shell
docsfy abort my-repo
docsfy abort my-repo --branch main --provider cursor --model gpt-5.4-xhigh-fast
docsfy delete my-repo --branch main --provider cursor --model gpt-5.4-xhigh-fast --yes
docsfy delete my-repo --all --yes
```

Use the project-only `abort` form when there is just one active run for that project. If more than one variant is running, rerun the command with `--branch`, `--provider`, and `--model`.

The same exact selector pattern is useful for downloads too:

```shell
docsfy download my-repo --branch main --provider cursor --model gpt-5.4-xhigh-fast --output ./site
```

> **Note:** When you use `--output`, docsfy extracts the archive into a subdirectory inside the target directory.

> **Warning:** Use either `--all` or an exact variant selector with `delete`, not both.

### Use `--owner` when you are an admin

```shell
docsfy status shared-name --branch main --provider claude --model opus --owner alice
docsfy download shared-name --branch main --provider claude --model opus --owner alice
```

`--owner` is mainly for admins who need one specific copy of a project or variant when the same project name exists under multiple owners.

### Run admin tasks

```shell
docsfy admin users list
docsfy admin users create newuser --role user
docsfy admin users rotate-key newuser
docsfy admin access grant my-repo --username newuser --owner admin
docsfy admin access list my-repo --owner admin
docsfy admin access revoke my-repo --username newuser --owner admin
```

Valid roles are `admin`, `user`, and `viewer`. New and rotated API keys are shown once, so save them immediately. Generated keys use the `docsfy_...` format.

Access grants are project-wide for one owner, so granting access to `my-repo` also grants access to that owner's variants of the project.

If you rotate a key for a saved profile, update the profile too:

```shell
docsfy config set servers.dev.password <new-api-key>
```

> **Note:** `viewer` accounts can list, inspect, and download docs they can access, but they cannot generate, abort, or delete.

> **Warning:** The username `admin` is reserved, custom keys must be at least 16 characters long, you cannot delete your own account, and a user cannot be deleted while they still have an active generation.

### Use JSON output for scripts

```shell
docsfy list --json
docsfy status my-repo --json
docsfy admin users list --json
```

Use `--json` when you want stable output for automation instead of the default tables and detail blocks.

</details>

## Troubleshooting
- `No server configured`: run `docsfy config init`, or pass `--host`, `--username`, and `--password` before the subcommand.
- `Server unreachable` or a redirect error: check the saved URL, host, and port with `docsfy config show` and `docsfy health`.
- `Write access required`: you authenticated as a `viewer`; use a `user` or `admin` account for `generate`, `abort`, and `delete`.
- `Variant not ready`: wait until `docsfy status my-repo` shows a ready variant, then download again.
- `Multiple active variants found` or `Multiple owners found`: rerun the command with `--branch`, `--provider`, and `--model`; if you are an admin and need one owner's copy, add `--owner`.
- `Invalid branch name`: rename the branch argument to something like `release-1.x`.
- A Git URL to `localhost` or another private network host is rejected: use a remote the server can reach.

## Related Pages

- [CLI Command Reference](cli-command-reference.html)
- [Configuration Reference](configuration-reference.html)
- [Generate Documentation](generate-documentation.html)
- [Track Generation Progress](track-generation-progress.html)
- [View, Download, and Publish Docs](view-download-and-publish-docs.html)

---

Source: fix-setup-and-generation-problems.md

# Fix Setup and Generation Problems

You want docsfy to accept your sign-in, start a generation, and finish with a ready docs site instead of failing early or getting stuck. Use this page to isolate whether the problem is your server connection, login, provider setup, branch choice, or the exact variant you started.

## Prerequisites
- A running docsfy server.
- Browser credentials or CLI credentials for that server.
- A Git repository URL the server can reach.
- If you use the CLI, either a saved profile from `docsfy config init` or explicit `--host`, `--port`, `-u`, and `-p` flags.
- If you need the clean first-run path instead of troubleshooting, see [Generate Your First Docs Site](generate-your-first-docs-site.html).

## Quick Example
```bash
docsfy health
docsfy models
docsfy generate https://github.com/myk-org/for-testing-only --branch main --provider cursor --model gpt-5.4-xhigh-fast --force --watch
docsfy status for-testing-only -b main -p cursor -m gpt-5.4-xhigh-fast
```

Start with the public `for-testing-only` repository when you need to prove that your setup works independently of your own repo. If this succeeds, your server target, credentials, provider, and branch handling are all working.

```mermaid
flowchart TD
  A[Generation will not start or finish] --> B{Can you reach the server?}
  B -->|No| C[Fix the server URL or start docsfy]
  B -->|Yes| D{Can you sign in?}
  D -->|No| E[Fix credentials or SECURE_COOKIES]
  D -->|Yes| F{Does generation start?}
  F -->|No| G[Check provider, model, branch, and repo URL]
  F -->|Yes| H[Run docsfy status for the exact variant]
  H --> I{Status}
  I -->|generating| J[Wait, or abort the exact variant]
  I -->|error| K[Read the error and rerun with --force after fixing the cause]
  I -->|ready| L[Open or download the docs]
```

## Step-by-Step
### 1. Confirm The Server
```bash
docsfy config show
docsfy health
```

If `docsfy config show` says the config is missing, run `docsfy config init` or use explicit connection flags on each command. If `docsfy health` says the server is unreachable, fix the saved URL or start the server; if it reports a redirect, the CLI is pointed at the wrong host or port.

### 2. Fix Sign-In First
| Sign-in type | Username field | Password field |
| --- | --- | --- |
| Admin | `admin` | `ADMIN_KEY` |
| Named user | your username | your API key |

For admin access, the username must literally be `admin`. For named users, the username must match the owner of the API key, even though the browser label says `Password`.

> **Warning:** If browser sign-in seems to work and then sends you back to `/login` on plain `http://localhost`, set `SECURE_COOKIES=false` and restart the server. Keep `SECURE_COOKIES=true` anywhere you serve docsfy over HTTPS.

> **Warning:** If admin access never works and the server will not start cleanly, `ADMIN_KEY` is missing or too short. It must be set and must be at least 16 characters.

> **Note:** The browser and CLI do not share auth state. A working browser session does not fix a broken CLI profile, and a working CLI command does not refresh an expired browser session.

### 3. Verify The Provider And Model
```bash
docsfy models
docsfy generate https://github.com/myk-org/for-testing-only --branch main --provider cursor --model gpt-5.4-xhigh-fast
```

Use only `claude`, `gemini`, or `cursor` as provider names. If you leave provider or model unset, docsfy uses the server defaults; in the default configuration, those are `cursor` and `gpt-5.4-xhigh-fast`.

If a generation fails almost immediately, fix the selected provider on the server before retrying. A remembered model name in the UI or CLI is not proof that the provider CLI is still installed, authenticated, or allowed to use that model.

On a brand-new server, `docsfy models` can show `(no models used yet)` and still be healthy.

> **Tip:** If provider startup is slow rather than broken, increase `AI_CLI_TIMEOUT` before retrying.

### 4. Use A Valid Branch
```bash
docsfy generate https://github.com/myk-org/for-testing-only --branch dev --provider cursor --model gpt-5.4-xhigh-fast
```

| Use this | Not this | Why |
| --- | --- | --- |
| `main` | `.hidden` | Branches must start with a letter or number. |
| `release-1.x` | `release/1.x` | Slashes are rejected. |
| `v2.0.1` | `../etc/passwd` | Traversal-like names are rejected. |

If the branch name passes validation but clone still fails, check that the branch actually exists in the remote repo. If you do not pass `--branch`, docsfy uses `main`.

### 5. Check The Exact Variant
```bash
docsfy status for-testing-only -b main -p cursor -m gpt-5.4-xhigh-fast
docsfy abort for-testing-only -b main -p cursor -m gpt-5.4-xhigh-fast
docsfy generate https://github.com/myk-org/for-testing-only --branch main --provider cursor --model gpt-5.4-xhigh-fast --force --watch
```

Always inspect the exact `branch/provider/model` variant you are fixing. That matters because the same project can have multiple variants at once.

If you see `already being generated`, let the active run finish or abort that exact variant before retrying. If you see `error`, fix the cause shown in the `Error` field and then retry with `--force` for a clean rebuild.

> **Tip:** Where a run stops tells you what to fix next. Failures before `cloning` are usually provider problems, failures during `cloning` are usually repo URL or branch problems, and later failures are best diagnosed from the variant's `Error` message.

See [CLI Command Reference](cli-command-reference.html) for the full command syntax.

<details><summary>Advanced Usage</summary>

### Local Settings To Recheck
```env
ADMIN_KEY=<a-secret-with-at-least-16-characters>
AI_PROVIDER=cursor
AI_MODEL=gpt-5.4-xhigh-fast
AI_CLI_TIMEOUT=60
SECURE_COOKIES=false
```

Use `SECURE_COOKIES=false` only for plain local HTTP. Keep it `true` when docsfy is served over HTTPS.

### When `--watch` Is The Only Thing Failing
```bash
docsfy generate https://github.com/myk-org/for-testing-only --branch main --provider cursor --model gpt-5.4-xhigh-fast --watch
docsfy status for-testing-only -b main -p cursor -m gpt-5.4-xhigh-fast
```

`--watch` depends on live WebSocket updates. If watch times out or disconnects, the server-side generation can still continue, so fall back to `docsfy status` and only treat it as a failed generation if the variant itself reaches `error` or `aborted`.

See [WebSocket Reference](websocket-reference.html) if you are debugging custom live-status clients.

### When An Admin Sees Owner Ambiguity
```bash
docsfy status for-testing-only -b main -p cursor -m gpt-5.4-xhigh-fast --owner alice
```

If more than one owner has the same project name or the same variant key, add `--owner` when you inspect or abort a variant. This is the fastest fix for `Multiple owners found` errors.

### When You Automate Outside The CLI
Use the same sequence of checks: reach the server, authenticate, start one exact variant, then inspect that exact variant until it reaches `ready`, `error`, or `aborted`. See [HTTP API Reference](http-api-reference.html) for the route list.

### When You Generate From A Local Checkout
Local path generation is an admin-only flow. The path must be absolute, the directory must exist, it must contain `.git`, and the checked-out branch must match the branch you requested.

</details>

## Troubleshooting
| Problem | What to do |
| --- | --- |
| `Invalid username or password` in the browser | Use `admin` only with `ADMIN_KEY`. For named users, use the exact username that owns the API key. |
| Sign-in works once, then you go back to `/login` on `http://localhost` | Set `SECURE_COOKIES=false`, restart the server, and sign in again. |
| `Server unreachable` or `Unable to connect to server` | Fix the server URL, host, or port, or start the server before retrying. |
| The UI says `Frontend not built` | Build the frontend and restart the server. |
| Generation fails almost immediately | Fix the selected provider on the server, pick a different provider or model, or increase `AI_CLI_TIMEOUT`. |
| `Invalid branch name` | Use a branch that starts with a letter or number and only contains letters, numbers, `.`, `_`, or `-`. Replace `/` with `-`. |
| `Variant '...' is already being generated` | Wait for that exact variant to finish, or abort it before retrying. |
| `Multiple active variants found` when aborting by project name | Re-run `docsfy abort` with `-b`, `-p`, and `-m` together. |
| `--watch` fails but the generation may still be running | Use `docsfy status` until the variant reaches `ready`, `error`, or `aborted`. |

## Related Pages

- [Generate Your First Docs Site](generate-your-first-docs-site.html)
- [Configure AI Providers](configure-ai-providers.html)
- [Install and Run docsfy Without Docker](install-and-run-docsfy-without-docker.html)
- [Generate Documentation](generate-documentation.html)
- [Track Generation Progress](track-generation-progress.html)

---

Source: configuration-reference.md

# Configuration Reference

Server settings come from environment variables. CLI profiles live in `~/.config/docsfy/config.toml`. See [Install and Run docsfy Without Docker](install-and-run-docsfy-without-docker.html) for startup steps and [CLI Command Reference](cli-command-reference.html) for CLI syntax.

## `.env`-Backed Server Settings
### `.env`
Environment file used by the server settings model.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `path` | path | `.env` | UTF-8 file loaded by the server settings model. |
| `encoding` | string | `utf-8` | Encoding used when reading `.env`. |
| `extra keys` | behavior | `ignored` | Unknown keys do not fail settings loading. |

```dotenv
ADMIN_KEY=<ADMIN_KEY>
AI_PROVIDER=cursor
AI_MODEL=gpt-5.4-xhigh-fast
AI_CLI_TIMEOUT=60
DATA_DIR=/data
SECURE_COOKIES=true
```

Effect: Supplies the settings-model fields used during application startup and request handling.

> **Note:** `HOST`, `PORT`, `DEBUG`, `DEV_MODE`, and `API_TARGET` are read directly from the process environment, not from the settings model.

### Authentication settings
Admin authentication and browser cookie security.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `ADMIN_KEY` | string | `""` | Required admin secret. Used for admin login, admin Bearer auth, and HMAC hashing of stored user API keys. Startup validation requires at least 16 characters. |
| `SECURE_COOKIES` | boolean | `true` | Controls the `Secure` flag on the `docsfy_session` cookie. |

```dotenv
ADMIN_KEY=<ADMIN_KEY>
SECURE_COOKIES=true
```

Effect: `ADMIN_KEY` must be present for the server to start. `SECURE_COOKIES` changes whether browsers send the session cookie only over HTTPS.

> **Warning:** Changing `ADMIN_KEY` invalidates existing stored user API keys.

### Generation settings
Server-side defaults used when a generation omits provider, model, or timeout, plus the parallel page limit.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `AI_PROVIDER` | enum | `cursor` | Default AI provider. Accepted values: `claude`, `gemini`, `cursor`. |
| `AI_MODEL` | string | `gpt-5.4-xhigh-fast` | Default AI model name. |
| `AI_CLI_TIMEOUT` | integer | `60` | Positive timeout value passed to provider CLI calls when no request-specific value is supplied. |
| `MAX_CONCURRENT_PAGES` | integer | `10` | Maximum parallel AI CLI calls during page generation and validation. Must be greater than `0`. |

```dotenv
AI_PROVIDER=gemini
AI_MODEL=gemini-2.5-pro
AI_CLI_TIMEOUT=120
MAX_CONCURRENT_PAGES=4
```

Effect: Request-specific provider, model, and timeout values override `AI_PROVIDER`, `AI_MODEL`, and `AI_CLI_TIMEOUT`. `MAX_CONCURRENT_PAGES` remains the server-side concurrency cap.

### Storage and logging settings
Runtime storage root and log verbosity.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `DATA_DIR` | path | `/data` | Base directory for `docsfy.db` and per-variant project files. |
| `LOG_LEVEL` | string | `INFO` | Logger level string used by the server. |

```dotenv
DATA_DIR=/srv/docsfy-data
LOG_LEVEL=DEBUG
```

Effect: `DATA_DIR` changes where the database and generated artifacts are stored. `LOG_LEVEL` changes server log verbosity.

## Direct Process Environment
### `docsfy-server` launcher variables
Variables read directly by the `docsfy-server` console entry point.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `HOST` | string | `127.0.0.1` | Bind host used by `docsfy-server`. |
| `PORT` | integer | `8000` | Bind port used by `docsfy-server`. |
| `DEBUG` | string | unset | Set to `true` to start uvicorn with reload enabled. |

```bash
HOST=0.0.0.0 PORT=9000 DEBUG=true docsfy-server
```

Effect: Starts the direct server entry point on the requested host and port, with reload enabled only when `DEBUG=true`.

> **Note:** The checked-in container entrypoint does not read `HOST` or `PORT`; it always starts uvicorn on `0.0.0.0:8000`.

### Container and frontend development variables
Variables used by `entrypoint.sh` and the Vite development server.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `DEV_MODE` | string | unset | When set to `true`, the container entrypoint runs `npm ci`, starts Vite on `5173`, and starts uvicorn with reload on `8000`. |
| `API_TARGET` | URL | `http://localhost:8000` | Backend target used by the Vite dev proxy for `/api`, `/docs`, and `/health`. WebSocket proxying is enabled for `/api`. |
| `vite host` | string | `0.0.0.0` | Fixed Vite development bind host. |
| `vite port` | integer | `5173` | Fixed Vite development port. |

```yaml
services:
  docsfy:
    ports:
      - "8000:8000"
      - "5173:5173"
    environment:
      - DEV_MODE=true
```

```bash
API_TARGET=http://localhost:8000 npm run dev
```

Effect: Enables split frontend/backend development and points the Vite proxy at the chosen backend.

## Default Variant Setting
### `branch`
Default branch used by variant records and filesystem layout when no branch is supplied.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `branch` | string | `main` | Default branch value. It is stored in the database primary key, used in variant URLs, and included in per-variant directory paths. Valid values must match `^[a-zA-Z0-9][a-zA-Z0-9._-]*$`; `/` and `..` are rejected. |

```text
Valid:   release-1.x
Invalid: release/1.x
Default: main
```

Effect: Omitted branch values resolve to `main`.

> **Tip:** Use hyphens instead of slashes in branch names.

## Data Locations
### Runtime storage layout
Persistent files stored under `DATA_DIR`.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `DATA_DIR/docsfy.db` | file | `/data/docsfy.db` | SQLite database containing project metadata, users, project access rules, and hashed sessions. |
| `DATA_DIR/projects/` | directory | `/data/projects/` | Root directory for generated variants. |
| `DATA_DIR/projects/<owner-or-_default>/<project>/<branch>/<provider>/<model>/` | directory | derived | Per-variant root directory. `_default` is used when the owner is empty. |
| `.../site/` | directory | derived | Rendered static site files served under `/docs/...`. |
| `.../cache/pages/` | directory | derived | Cached page markdown used for regeneration. |

```text
/data/projects/alice/my-repo/main/claude/opus/site/index.html
/data/projects/alice/my-repo/main/claude/opus/cache/pages/introduction.md
```

Effect: `docsfy` creates `docsfy.db` and the `projects/` tree under `DATA_DIR` and uses these paths as the authoritative runtime storage location.

### Docker Compose volume
Default host-to-container storage mapping in the checked-in `docker-compose.yaml`.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `host path` | path | `./data` | Host directory mounted into the container. |
| `container path` | path | `/data` | Container path used by the server's default `DATA_DIR`. |
| `env_file` | path | `.env` | Compose file loaded into the container for server settings. |

```yaml
services:
  docsfy:
    volumes:
      - ./data:/data
    env_file:
      - .env
```

Effect: Persists the database and generated docs on the host across container restarts.

> **Note:** If you change `DATA_DIR`, update the volume mount to point at the new container path.

## Browser Session Cookie
### `docsfy_session`
Browser session cookie set on login.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | string | `docsfy_session` | Cookie name. |
| `value` | string | generated | Opaque session token returned on login. |
| `HttpOnly` | boolean | `true` | Client-side JavaScript cannot read the cookie. |
| `SameSite` | string | `strict` | Same-site cookie policy. |
| `Secure` | boolean | `true` | Controlled by `SECURE_COOKIES`. |
| `Max-Age` | integer | `28800` | Session lifetime in seconds (8 hours). |

```http
Set-Cookie: docsfy_session=<token>; HttpOnly; SameSite=strict; Max-Age=28800; Secure
```

Effect: Authenticates protected browser requests and WebSocket connections while the session is valid. The cookie stores an opaque token; the `sessions` table stores a SHA-256 hash of that token. The cookie is deleted on logout and after API key rotation.

> **Warning:** On plain `http://localhost`, browser logins do not persist unless `SECURE_COOKIES=false`.

## CLI Profiles
### `~/.config/docsfy/config.toml`
Local CLI profile file used by `docsfy`.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `directory` | path | `~/.config/docsfy/` | CLI config directory. |
| `file` | path | `~/.config/docsfy/config.toml` | CLI profile file. |
| `[default].server` | string | none | Default profile name used when `--server` is omitted. |
| `[servers.<name>].url` | string | none | Base URL for a named server profile. |
| `[servers.<name>].username` | string | none | Username stored with the profile for connection resolution and display. |
| `[servers.<name>].password` | string | none | API key stored with the profile and sent as the CLI Bearer token. |

```toml
[default]
server = "dev"

[servers.dev]
url = "http://localhost:8000"
username = "admin"
password = "<API_KEY>"
```

Effect: The CLI loads this file to resolve server URL and credentials for commands that connect to a docsfy server. The stored `password` value is what the CLI sends as the Bearer token.

> **Warning:** `password` is stored verbatim in `config.toml`; the CLI relies on file permissions rather than encryption.
> **Note:** The CLI creates `~/.config/docsfy/` with owner-only permissions and writes `config.toml` with owner read/write only.
> **Tip:** `docsfy config set` accepts keys under `default.` and `servers.`.

### Connection resolution
Resolution order used by the CLI when building a server connection.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--server` | CLI flag | none | Selects a named profile from `[servers]`. |
| `--host` | CLI flag | none | Overrides the host portion of the resolved URL. If the selected profile URL begins with `http://`, the CLI keeps `http`; otherwise it uses `https`. |
| `--port` | CLI flag | `8000` when `--host` is set | Overrides or supplies the port for `--host`. |
| `--username` | CLI flag | none | Overrides the resolved username. |
| `--password` | CLI flag | none | Overrides the resolved API key. |
| `[default].server` | config key | none | Fallback profile when `--server` is omitted. |

```bash
docsfy --server prod health
docsfy --host docsfy.example.com --port 8443 -u admin -p <API_KEY> health
```

Effect: Resolution order is CLI flags, then the named profile, then `[default].server`. If no connection can be resolved, or if the selected profile does not exist, the CLI exits with an error. See [CLI Command Reference](cli-command-reference.html) for command syntax.

## Related Pages

- [Install and Run docsfy Without Docker](install-and-run-docsfy-without-docker.html)
- [Configure AI Providers](configure-ai-providers.html)
- [Manage docsfy from the CLI](manage-docsfy-from-the-cli.html)
- [CLI Command Reference](cli-command-reference.html)
- [Fix Setup and Generation Problems](fix-setup-and-generation-problems.html)

---

Source: cli-command-reference.md

# CLI Command Reference

## `docsfy`
**Syntax:** `docsfy [GLOBAL OPTIONS] COMMAND [ARGS]...`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--server`, `-s` | string | none | Use a saved profile from `~/.config/docsfy/config.toml`. |
| `--host` | string | none | Override the profile host. When set, the CLI builds `scheme://host:port`; the scheme comes from the selected profile when available, otherwise `https`. |
| `--port` | integer | `8000` when `--host` is used | Port used with `--host`. |
| `--username`, `-u` | string | none | Override the profile username. |
| `--password`, `-p` | string | none | Override the profile password/API key sent as the bearer token. |

```shell
docsfy --server prod status my-repo
docsfy --host docsfy.example.com --port 443 -u admin -p <API_KEY> health
```

Return value / effect:
- Dispatches to `health`, `config`, `generate`, `list`, `status`, `delete`, `abort`, `download`, `models`, or `admin`.
- With no subcommand, prints help.
- Applies the resolved connection settings to the selected subcommand.

> **Note:** Connection resolution order is explicit CLI flags, then `--server`, then `[default].server` in `~/.config/docsfy/config.toml`, then an error if nothing is configured.

> **Note:** Put global options before the subcommand. After commands such as `status`, `delete`, `abort`, and `download`, `-p` means `--provider`, not API key.

## `docsfy health`
**Syntax:** `docsfy [GLOBAL OPTIONS] health`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| None | - | - | No command-specific parameters or options. Use global connection options before `health`. |

```shell
docsfy --server dev health
docsfy --host localhost --port 8000 -u admin -p <API_KEY> health
```

Return value / effect:
- Calls the server `/health` endpoint.
- Prints `Server: <url>` and `Status: <value>`.
- Exits non-zero if the server is unreachable, returns an HTTP error, or responds with non-JSON content.

## `docsfy config`
`docsfy config` is the CLI configuration command group for `~/.config/docsfy/config.toml`.

| Subcommand | Description |
| --- | --- |
| `docsfy config init` | Create or add a profile interactively. |
| `docsfy config show` | Print saved profiles with masked passwords. |
| `docsfy config set` | Update one dotted key in the config file. |

> **Warning:** `~/.config/docsfy/config.toml` stores raw API keys.

### `docsfy config init`
**Syntax:** `docsfy config init`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `Profile name` | string | `dev` | Profile name stored under `[servers.<profile>]`. |
| `Server URL` | string | none | Full server URL, such as `https://docsfy.example.com`. |
| `Username` | string | none | Username saved with the profile. |
| `Password` | string | none | API key saved in the profile as `password`. Input is hidden. |

```shell
docsfy config init
# Profile name [dev]: prod
# Server URL: https://docsfy.example.com
# Username: admin
# Password: <API_KEY>
```

Return value / effect:
- Creates `~/.config/docsfy/config.toml` if it does not exist.
- Writes the config directory with owner-only permissions and the file with owner read/write permissions.
- Adds `[servers.<profile>]`.
- Sets `[default].server` only when no default profile exists yet.
- Prints `Profile '<profile>' saved to ~/.config/docsfy/config.toml`.

### `docsfy config show`
**Syntax:** `docsfy config show`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| None | - | - | No command-specific parameters or options. |

```shell
docsfy config show
```

Return value / effect:
- Prints the config file path.
- Prints the current default server profile.
- Prints each saved profile with `URL`, `Username`, and a masked `Password`.
- Exits non-zero if the config file is missing or invalid TOML.

### `docsfy config set`
**Syntax:** `docsfy config set KEY VALUE`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `KEY` | string | Required | Dotted config key. Accepted prefixes are `default.` and `servers.`. |
| `VALUE` | string | Required | Value written to the selected key. |

```shell
docsfy config set default.server prod
docsfy config set servers.prod.url https://docsfy.example.com
docsfy config set servers.prod.password <API_KEY>
```

Return value / effect:
- Updates one config value and prints `Updated <KEY>`.
- Creates missing intermediate tables on the path to `KEY`.
- Exits non-zero if the config file does not exist.
- Exits non-zero if `KEY` does not start with `default.` or `servers.`.

> **Warning:** `docsfy config set` writes the TOML key directly. It does not verify that a referenced profile exists.

## Project Commands
> **Note:** `generate`, `delete`, and `abort` require a `user` or `admin` account. `list`, `status`, `download`, and `models` are read-oriented commands.

### `docsfy generate`
**Syntax:** `docsfy [GLOBAL OPTIONS] generate REPO_URL [OPTIONS]`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `REPO_URL` | string | Required | Remote Git URL. Accepted forms include `https://host/org/repo`, `https://host/org/repo.git`, `git@host:org/repo`, and `git@host:org/repo.git`. |
| `--branch`, `-b` | string | `main` | Git branch to generate. Branch names cannot contain `/`. |
| `--provider` | string | server default | AI provider. Valid values in this codebase are `claude`, `gemini`, and `cursor`. |
| `--model`, `-m` | string | server default | AI model name. |
| `--force`, `-f` | boolean | `false` | Force a full regeneration instead of reusing cached artifacts. |
| `--watch`, `-w` | boolean | `false` | Stream live generation progress over WebSocket after the request starts. |

```shell
docsfy generate https://github.com/myk-org/for-testing-only --provider gemini --model gemini-2.5-flash --force
docsfy generate https://github.com/myk-org/for-testing-only --branch dev --provider gemini --model gemini-2.5-flash --force --watch
```

Return value / effect:
- Starts generation and prints `Project`, `Branch`, and `Status`.
- Uses the repository name as the stored project name for later commands.
- With `--watch`, prints progress updates until the variant reaches `ready`, `error`, or `aborted`.
- Exits non-zero on validation, HTTP, or WebSocket errors.

> **Note:** When `--provider` or `--model` is omitted, the server default is used. In this repository, the code defaults are `cursor` and `gpt-5.4-xhigh-fast`, but deployments can override them.

> **Tip:** When using `--watch`, pass both `--provider` and `--model` so the CLI can subscribe to the exact variant immediately.

> **Warning:** The CLI `generate` command accepts remote Git URLs only. Local filesystem paths are not a CLI argument.

> **Warning:** Repository URLs that target `localhost` or private-network addresses are rejected by the server.

### `docsfy list`
**Syntax:** `docsfy [GLOBAL OPTIONS] list [OPTIONS]`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--status` | string | all statuses | Filter by stored status value. The codebase uses `generating`, `ready`, `error`, and `aborted`. |
| `--provider` | string | all providers | Filter by AI provider. |
| `--json` | boolean | `false` | Output JSON instead of the table view. |

```shell
docsfy list --status ready --provider cursor
docsfy list --json
```

Return value / effect:
- Prints a table with `NAME`, `BRANCH`, `PROVIDER`, `MODEL`, `STATUS`, `OWNER`, and `PAGES`.
- `--json` outputs an array of project/variant objects.
- Includes projects the user owns and projects shared with the user.
- Prints `No projects found.` when nothing matches.

### `docsfy status`
**Syntax:** `docsfy [GLOBAL OPTIONS] status NAME [OPTIONS]`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `NAME` | string | Required | Stored project name, such as `for-testing-only`. |
| `--branch`, `-b` | string | all branches | Filter by branch. When combined with `--provider` and `--model`, targets one exact variant. |
| `--provider`, `-p` | string | all providers | Filter by provider. When combined with `--branch` and `--model`, targets one exact variant. |
| `--model`, `-m` | string | all models | Filter by model. When combined with `--branch` and `--provider`, targets one exact variant. |
| `--owner` | string | none | Owner disambiguation for exact variant lookups. |
| `--json` | boolean | `false` | Output JSON instead of plain text. |

```shell
docsfy status for-testing-only
docsfy status for-testing-only --branch main --provider cursor --model gpt-5 --json
```

Return value / effect:
- With only `NAME`, prints all accessible variants for that project.
- With `--branch`, `--provider`, and `--model` together, prints one exact variant.
- Plain text output can include `Status`, `Owner`, `Pages`, `Updated`, short `Commit`, `Stage`, and `Error`.
- `--json` outputs either `{ "name": NAME, "variants": [...] }` or a single variant object.
- Prints `No variants found for '<NAME>'.` when filters remove every result.

> **Note:** `--owner` is used only when `--branch`, `--provider`, and `--model` are all present. It does not change the broad project view.

> **Tip:** `status` accepts partial filters. `--branch` alone, `--provider` alone, and other partial combinations are valid.

### `docsfy delete`
**Syntax:** `docsfy [GLOBAL OPTIONS] delete NAME [OPTIONS]`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `NAME` | string | Required | Stored project name. |
| `--branch`, `-b` | string | none | Variant branch. Must be used with `--provider` and `--model` for exact variant deletion. |
| `--provider`, `-p` | string | none | Variant provider. Must be used with `--branch` and `--model` for exact variant deletion. |
| `--model`, `-m` | string | none | Variant model. Must be used with `--branch` and `--provider` for exact variant deletion. |
| `--owner` | string | none | Required for admin delete requests. Ignored for non-admin users. |
| `--all` | boolean | `false` | Delete all variants for the project within one owner scope. |
| `--yes`, `-y` | boolean | `false` | Skip the confirmation prompt. |

```shell
docsfy delete for-testing-only --branch dev --provider gemini --model gemini-2.0-flash --yes
docsfy delete my-repo --all --yes
```

Return value / effect:
- Deletes one exact variant when `--branch`, `--provider`, and `--model` are all present.
- Deletes all variants for one project/owner scope when `--all` is set.
- Prompts for confirmation unless `--yes` is used.
- Prints `Deleted variant '<name>/<branch>/<provider>/<model>'.` or `Deleted all variants of '<name>'.`
- Exits non-zero if the target is missing, still generating, or invalidly specified.

> **Warning:** Use either `--all` or the full variant selector. Do not combine them.

> **Warning:** Admin deletes require `--owner`, even when the branch, provider, and model are fully specified.

### `docsfy abort`
**Syntax:** `docsfy [GLOBAL OPTIONS] abort NAME [OPTIONS]`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `NAME` | string | Required | Stored project name. |
| `--branch`, `-b` | string | none | Variant branch. Must be supplied together with `--provider` and `--model` for exact variant aborts. |
| `--provider`, `-p` | string | none | Variant provider. Must be supplied together with `--branch` and `--model` for exact variant aborts. |
| `--model`, `-m` | string | none | Variant model. Must be supplied together with `--branch` and `--provider` for exact variant aborts. |
| `--owner` | string | none | Owner disambiguation for exact variant aborts. |

```shell
docsfy abort my-repo
docsfy abort for-testing-only --branch main --provider gemini --model gemini-2.5-flash
```

Return value / effect:
- With only `NAME`, aborts the single active generation for that project name.
- With the full selector, aborts that exact variant.
- Prints `Aborted generation for '<name>'.` or `Aborted generation for '<name>/<branch>/<provider>/<model>'.`
- Successful aborts transition the variant to `aborted`.
- Exits non-zero if the target is not generating, ambiguously specified, or unavailable.

> **Warning:** Project-level abort fails when more than one active variant matches the same project name.

> **Note:** `--owner` only affects fully qualified variant aborts. It does not change project-level aborts.

### `docsfy download`
**Syntax:** `docsfy [GLOBAL OPTIONS] download NAME [OPTIONS]`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `NAME` | string | Required | Stored project name. |
| `--branch`, `-b` | string | none | Variant branch. Must be supplied together with `--provider` and `--model` for exact variant downloads. |
| `--provider`, `-p` | string | none | Variant provider. Must be supplied together with `--branch` and `--model` for exact variant downloads. |
| `--model`, `-m` | string | none | Variant model. Must be supplied together with `--branch` and `--provider` for exact variant downloads. |
| `--owner` | string | none | Owner disambiguation for exact variant downloads. |
| `--output`, `-o` | string | none | Extract into a directory instead of saving a `.tar.gz` archive in the current directory. |

```shell
docsfy download my-repo
docsfy download my-repo --branch main --provider cursor --model gpt-5 --output ./site
```

Return value / effect:
- With no variant selector, downloads the latest ready variant visible to the user.
- With the full selector, downloads that exact variant.
- Without `--output`, saves an archive in the current directory.
- With `--output`, creates the directory if needed, downloads to a temporary archive, and extracts the archive there.
- Prints `Downloaded to <path>` or `Extracted to <dir>`.

Archive naming:
- Project-level download: `<project>-docs.tar.gz`
- Exact variant download: `<project>-<branch>-<provider>-<model>-docs.tar.gz`

> **Warning:** Only `ready` variants can be downloaded.

> **Warning:** Use all three variant selectors together or omit all three.

> **Note:** `--owner` only affects fully qualified variant downloads. It does not change project-level downloads.

### `docsfy models`
**Syntax:** `docsfy [GLOBAL OPTIONS] models [OPTIONS]`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--provider`, `-P` | string | all providers | Filter to one provider. Valid providers in this codebase are `claude`, `gemini`, and `cursor`. |
| `--json`, `-j` | boolean | `false` | Output JSON instead of plain text. |

```shell
docsfy models
docsfy models --provider cursor --json
```

Return value / effect:
- Plain text output groups known models under each provider.
- Marks the current default provider and default model with `(default)`.
- Shows `(no models used yet)` for providers with no completed ready variants.
- `--json` outputs `{ "providers": [...], "default_provider": "...", "default_model": "...", "known_models": {...} }`.
- With `--provider`, output is filtered to one provider. In JSON mode, `default_provider` and `default_model` remain present even when the provider list is filtered.
- Exits non-zero for an unknown provider.

> **Note:** `known_models` is built from ready variants only.

## `docsfy admin`
`docsfy admin` is the admin-only command group.

| Group | Subcommands |
| --- | --- |
| `docsfy admin users` | `list`, `create`, `delete`, `rotate-key` |
| `docsfy admin access` | `list`, `grant`, `revoke` |

> **Warning:** All `admin ...` commands require admin credentials.

### `docsfy admin users list`
**Syntax:** `docsfy [GLOBAL OPTIONS] admin users list [OPTIONS]`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `--json` | boolean | `false` | Output JSON instead of the table view. |

```shell
docsfy admin users list
docsfy admin users list --json
```

Return value / effect:
- Prints a table with `USERNAME`, `ROLE`, and `CREATED`.
- `--json` outputs an array of user objects with `id`, `username`, `role`, and `created_at`.
- Prints `No users found.` when the user list is empty.

### `docsfy admin users create`
**Syntax:** `docsfy [GLOBAL OPTIONS] admin users create USERNAME [OPTIONS]`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `USERNAME` | string | Required | Username to create. Must be 2-50 characters, start with an alphanumeric character, and use only alphanumerics, `.`, `_`, or `-`. |
| `--role`, `-r` | string | `user` | User role. Valid values are `admin`, `user`, and `viewer`. |
| `--json` | boolean | `false` | Output JSON instead of plain text. |

```shell
docsfy admin users create alice --role viewer
docsfy admin users create alice --role user --json
```

Return value / effect:
- Creates the user account.
- Plain text output prints `User created`, `Role`, and `API Key`.
- `--json` outputs `{ "username": "...", "api_key": "...", "role": "..." }`.
- Exits non-zero for invalid usernames, duplicate usernames, reserved usernames, or invalid roles.

> **Warning:** The username `admin` is reserved.

> **Warning:** The API key is shown once. Save it immediately.

### `docsfy admin users delete`
**Syntax:** `docsfy [GLOBAL OPTIONS] admin users delete USERNAME [OPTIONS]`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `USERNAME` | string | Required | Username to delete. |
| `--yes`, `-y` | boolean | `false` | Skip the confirmation prompt. |

```shell
docsfy admin users delete alice --yes
```

Return value / effect:
- Prompts for confirmation unless `--yes` is used.
- Deletes the user account.
- Deletes that user's sessions, owned projects, and related access grants.
- Prints `Deleted user '<username>'.` on success.
- Exits non-zero when the user is missing or cannot be deleted.

> **Warning:** You cannot delete your own admin account.

> **Warning:** A user cannot be deleted while one of their generations is still in progress.

### `docsfy admin users rotate-key`
**Syntax:** `docsfy [GLOBAL OPTIONS] admin users rotate-key USERNAME [OPTIONS]`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `USERNAME` | string | Required | Username whose API key will be rotated. |
| `--new-key` | string | auto-generated key | Set a specific replacement API key. Custom keys must be at least 16 characters long. |
| `--json` | boolean | `false` | Output JSON instead of plain text. |

```shell
docsfy admin users rotate-key alice
docsfy admin users rotate-key alice --new-key "my-very-secure-custom-password-123" --json
```

Return value / effect:
- Generates a new API key when `--new-key` is omitted.
- Uses the supplied key when `--new-key` is present and valid.
- Invalidates the user's existing sessions.
- Plain text output prints `User` and `New API Key`.
- `--json` outputs `{ "username": "...", "new_api_key": "..." }`.

> **Warning:** The new API key is shown once. Save it immediately.

### `docsfy admin access list`
**Syntax:** `docsfy [GLOBAL OPTIONS] admin access list PROJECT [OPTIONS]`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `PROJECT` | string | Required | Stored project name. |
| `--owner` | string | Required | Owner whose project access list should be shown. |
| `--json` | boolean | `false` | Output JSON instead of plain text. |

```shell
docsfy admin access list my-repo --owner admin
docsfy admin access list my-repo --owner admin --json
```

Return value / effect:
- Prints the project name, owner, and current access list.
- Prints `No access grants.` when the list is empty.
- `--json` outputs `{ "project": "...", "owner": "...", "users": [...] }`.

> **Note:** Access grants are project-level and owner-scoped. One grant covers all variants of that project for the specified owner.

### `docsfy admin access grant`
**Syntax:** `docsfy [GLOBAL OPTIONS] admin access grant PROJECT [OPTIONS]`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `PROJECT` | string | Required | Stored project name. |
| `--username` | string | Required | Username to grant access to. |
| `--owner` | string | Required | Owner whose project will be shared. |

```shell
docsfy admin access grant my-repo --username alice --owner admin
```

Return value / effect:
- Grants the named user access to the project for the specified owner.
- The grant applies to all variants of that project for that owner.
- Prints `Granted '<username>' access to '<project>' (owner: <owner>).`
- Exits non-zero if the user does not exist or the project does not exist for that owner.

### `docsfy admin access revoke`
**Syntax:** `docsfy [GLOBAL OPTIONS] admin access revoke PROJECT [OPTIONS]`

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `PROJECT` | string | Required | Stored project name. |
| `--username` | string | Required | Username whose access will be removed. |
| `--owner` | string | Required | Owner whose project access grant will be removed. |

```shell
docsfy admin access revoke my-repo --username alice --owner admin
```

Return value / effect:
- Removes the named user's access grant for that project and owner.
- Prints `Revoked '<username>' access to '<project>' (owner: <owner>).`

## Shared Exit Behavior

| Condition | Effect |
| --- | --- |
| HTTP `4xx` / `5xx` | Prints `Error (<status>): <detail>` to stderr and exits non-zero. |
| HTTP redirect | Prints a redirect error and exits non-zero. |
| `health` connection failure | Prints `Server unreachable: ...` and exits non-zero. |
| `generate --watch` WebSocket timeout or close | Prints an error to stderr and exits non-zero. |
| Confirmation declined | Prints `Aborted.` and exits without changing server state. |

Commands with JSON output:
- `docsfy list`
- `docsfy status`
- `docsfy models`
- `docsfy admin users list`
- `docsfy admin users create`
- `docsfy admin users rotate-key`
- `docsfy admin access list`

## Related Pages

- [Manage docsfy from the CLI](manage-docsfy-from-the-cli.html)
- [Generate Documentation](generate-documentation.html)
- [Track Generation Progress](track-generation-progress.html)
- [View, Download, and Publish Docs](view-download-and-publish-docs.html)
- [Manage Users, Roles, and Access](manage-users-roles-and-access.html)

---

Source: http-api-reference.md

# HTTP API Reference

## Authentication

| Mechanism | Type | Default | Description |
| --- | --- | --- | --- |
| `Authorization: Bearer <token>` | Header | — | Accepted on protected API and docs routes. Use the admin key or a user's API key. |
| `docsfy_session` | Cookie | Set by `POST /api/auth/login` | Browser session cookie. `HttpOnly`, `SameSite=Strict`, max age `28800` seconds. The `Secure` flag follows server cookie settings. |

| Role | Type | Default | Description |
| --- | --- | --- | --- |
| `admin` | Role | — | Full access, including `/api/admin/*`. |
| `user` | Role | New users default here | Read/write access to owned projects and accessible shared projects. |
| `viewer` | Role | — | Read-only access to owned projects and accessible shared projects. Write routes return `403`. |

> **Note:** Public routes are `GET /api/models`, `POST /api/auth/login`, and `POST /api/auth/logout`.

> **Note:** JSON error responses use `{"detail": "..."}`. Unauthenticated `/docs/*` requests return `302 /login` for HTML clients and `401` JSON for non-HTML clients.

> **Note:** A database user with role `admin` has the same API permissions as the `ADMIN_KEY` admin user.

## Common Response Objects

### `ErrorResponse`

| Field | Type | Description |
| --- | --- | --- |
| `detail` | `string` | Human-readable error message for JSON `4xx` and `5xx` responses. |

### `AuthResponse`

| Field | Type | Description |
| --- | --- | --- |
| `username` | `string` | Authenticated username. |
| `role` | `string` | User role: `admin`, `user`, or `viewer`. |
| `is_admin` | `boolean` | `true` for admin users. |

### `Project`

| Field | Type | Description |
| --- | --- | --- |
| `name` | `string` | Project name. |
| `branch` | `string` | Git branch for this variant. |
| `ai_provider` | `string` | AI provider for this variant. |
| `ai_model` | `string` | AI model for this variant. |
| `owner` | `string` | Variant owner username. Legacy ownerless rows use an empty string. |
| `repo_url` | `string` | Repository source string used for generation. This can be a remote Git URL or a server-local absolute path. |
| `status` | `string` | One of `generating`, `ready`, `error`, or `aborted`. |
| `current_stage` | `string \| null` | Current generation stage, or `null`. |
| `last_commit_sha` | `string \| null` | Last generated commit SHA. |
| `last_generated` | `string \| null` | Timestamp of the last successful generation. |
| `page_count` | `integer` | Current page count tracked for the variant. |
| `error_message` | `string \| null` | Latest error or abort message. |
| `plan_json` | `string \| null` | JSON-encoded documentation plan. |
| `created_at` | `string` | Creation timestamp. |
| `updated_at` | `string` | Last update timestamp. |

> **Note:** `current_stage` can be `cloning`, `planning`, `incremental_planning`, `generating_pages`, `validating`, `cross_linking`, `rendering`, `up_to_date`, or `null`. See [Track Generation Progress](track-generation-progress.html) for the stage meanings.

### `ProjectsResponse`

| Field | Type | Description |
| --- | --- | --- |
| `projects` | `Project[]` | Flat list of accessible variants. This is not grouped by project name. |
| `known_models` | `Record<string, string[]>` | Ready model IDs grouped by provider across the server. |
| `known_branches` | `Record<string, string[]>` | Ready branch names grouped by project name. Admins get all ready branches; non-admins get their own ready branches. |

### `User`

| Field | Type | Description |
| --- | --- | --- |
| `id` | `integer` | Numeric user ID. |
| `username` | `string` | Username. |
| `role` | `string` | User role: `admin`, `user`, or `viewer`. |
| `created_at` | `string` | Creation timestamp. |

## Models

### `GET /api/models`

Returns available providers, server defaults, and model IDs seen in ready variants.

**Auth:** None.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| None | — | — | — | This endpoint does not accept parameters. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `{ "providers": string[], "default_provider": string, "default_model": string, "known_models": Record<string, string[]> }` | Returns provider discovery data. |

**Example**
```bash
curl "<SERVER_URL>/api/models"
```

## Auth Routes

### `POST /api/auth/login`

Authenticates a user from a JSON body and sets the `docsfy_session` cookie.

**Auth:** None.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `username` | Body | `string` | — | Username to authenticate. The admin key only works when this value is exactly `admin`. |
| `api_key` | Body | `string` | — | Admin key or user API key. For database users, the key must belong to `username`. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `AuthResponse` | Authenticates the user and sets the `docsfy_session` cookie. |
| `400` | `ErrorResponse` | Invalid JSON body or body is not a JSON object. |
| `401` | `ErrorResponse` | Username and API key did not authenticate. |

**Example**
```bash
curl -i -X POST "<SERVER_URL>/api/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","api_key":"<ADMIN_KEY>"}'
```

### `POST /api/auth/logout`

Clears the current session cookie and deletes the current session if one exists.

**Auth:** None.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| None | — | — | — | This endpoint does not accept parameters. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `{ "ok": true }` | Deletes the current session if present and clears the `docsfy_session` cookie. |

**Example**
```bash
curl -X POST "<SERVER_URL>/api/auth/logout" \
  --cookie "docsfy_session=<COOKIE>"
```

### `GET /api/auth/me`

Returns the authenticated user identity.

**Auth:** Bearer token or `docsfy_session` cookie.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| None | — | — | — | This endpoint does not accept parameters. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `AuthResponse` | Returns the current authenticated user. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

**Example**
```bash
curl "<SERVER_URL>/api/auth/me" \
  -H "Authorization: Bearer <USER_API_KEY>"
```

### `POST /api/auth/rotate-key`

Rotates the authenticated user's API key.

**Auth:** Bearer token or `docsfy_session` cookie.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `new_key` | Body | `string` | Auto-generated | Optional replacement API key. Must be at least 16 characters when supplied. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `{ "username": string, "new_api_key": string }` | Rotates the user's API key, invalidates all sessions for that user, clears the current session cookie, and returns `Cache-Control: no-store`. |
| `400` | `ErrorResponse` | Malformed JSON, non-object body, invalid custom key, or `ADMIN_KEY` admin attempting self-rotation. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

> **Warning:** `new_api_key` is the only copy of the raw key returned by this route.

**Example**
```bash
curl -X POST "<SERVER_URL>/api/auth/rotate-key" \
  -H "Authorization: Bearer <USER_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"new_key":"my-very-secure-custom-password-123"}'
```

## Project Routes

### `GET /api/status`
### `GET /api/projects`

Both paths return the same flat variant list and discovery metadata.

**Auth:** Bearer token or `docsfy_session` cookie.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| None | — | — | — | This endpoint does not accept parameters. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `ProjectsResponse` | Returns accessible variants plus known model and branch metadata. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

> **Note:** `projects[]` is a flat list of variants. Use `GET /api/projects/{name}` to retrieve grouped variants for one project name.

**Example**
```bash
curl "<SERVER_URL>/api/status" \
  -H "Authorization: Bearer <USER_API_KEY>"
```

### `GET /api/projects/{name}`

Returns all accessible variants for one project name.

**Auth:** Bearer token or `docsfy_session` cookie.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `name` | Path | `string` | — | Project name. Must match the stored project name exactly. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `{ "name": string, "variants": Project[] }` | Returns all accessible variants for the project name. |
| `400` | `ErrorResponse` | Invalid project name format. |
| `404` | `ErrorResponse` | No accessible variants found for `name`. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

**Example**
```bash
curl "<SERVER_URL>/api/projects/my-repo" \
  -H "Authorization: Bearer <USER_API_KEY>"
```

### `POST /api/generate`

Queues a new documentation generation job.

**Auth:** `user` or `admin`.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `repo_url` | Body | `string` | `null` | Remote Git URL. Accepted formats are `http(s)://host/owner/repo(.git)` and `git@host:owner/repo(.git)`. Exactly one of `repo_url` or `repo_path` is required. |
| `repo_path` | Body | `string` | `null` | Absolute server-local path to a Git repository. Exactly one of `repo_url` or `repo_path` is required. Admin only. |
| `ai_provider` | Body | `string` | Server default | AI provider. Valid values are `claude`, `gemini`, and `cursor`. |
| `ai_model` | Body | `string` | Server default | AI model ID. |
| `ai_cli_timeout` | Body | `integer` | Server default | Timeout in seconds for each AI CLI call. Must be greater than `0`. |
| `force` | Body | `boolean` | `false` | Forces a full regeneration and ignores cached output. |
| `branch` | Body | `string` | `main` | Git branch to generate. Slashes are rejected; valid names use letters, digits, `.`, `_`, and `-`. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `202` | `{ "project": string, "status": "generating", "branch": string }` | Creates or updates the variant row and starts background generation. |
| `400` | `ErrorResponse` | Runtime validation failed, such as invalid repository path, non-Git path, unsupported repository target, or missing model after defaults are applied. |
| `403` | `ErrorResponse` | Caller is a `viewer`, or a non-admin attempted to use `repo_path`. |
| `409` | `ErrorResponse` | The same `owner/name/branch/provider/model` variant is already generating. |
| `422` | `ErrorResponse` | Request body failed schema validation. |

> **Note:** The project name is derived from the repository URL basename or the local directory name. Extra body fields such as `project_name` are ignored.

**Example**
```bash
curl -X POST "<SERVER_URL>/api/generate" \
  -H "Authorization: Bearer <USER_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "repo_url": "https://github.com/acme/my-repo.git",
    "ai_provider": "claude",
    "ai_model": "opus",
    "branch": "main",
    "force": false
  }'
```

### `GET /api/projects/{name}/{branch}/{provider}/{model}`

Returns one specific variant.

**Auth:** Bearer token or `docsfy_session` cookie.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `name` | Path | `string` | — | Project name. |
| `branch` | Path | `string` | — | Variant branch. |
| `provider` | Path | `string` | — | Variant provider. |
| `model` | Path | `string` | — | Variant model. |
| `owner` | Query | `string` | — | Admin-only disambiguation parameter. Required when the same variant exists under multiple owners. Ignored for non-admin requests. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `Project` | Returns the matching variant. |
| `400` | `ErrorResponse` | Invalid project name format. |
| `404` | `ErrorResponse` | Variant not found or not accessible. |
| `409` | `ErrorResponse` | Multiple owners match the same variant and no `owner` was supplied. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

**Example**
```bash
curl "<SERVER_URL>/api/projects/my-repo/main/claude/opus?owner=alice" \
  -H "Authorization: Bearer <ADMIN_KEY>"
```

### `DELETE /api/projects/{name}/{branch}/{provider}/{model}`

Deletes one specific variant.

**Auth:** `user` or `admin`.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `name` | Path | `string` | — | Project name. |
| `branch` | Path | `string` | — | Variant branch. |
| `provider` | Path | `string` | — | Variant provider. |
| `model` | Path | `string` | — | Variant model. |
| `owner` | Query | `string` | — | Required for admin requests. Non-admin requests always delete only the caller's own variant. Use an empty value only for legacy ownerless variants. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `{ "deleted": "name/branch/provider/model" }` | Deletes the target variant row and its stored artifacts. |
| `400` | `ErrorResponse` | Invalid project name format, or missing `owner` on an admin request. |
| `404` | `ErrorResponse` | Variant not found. |
| `409` | `ErrorResponse` | Variant is currently generating. Abort it first. |
| `403` | `ErrorResponse` | Caller has read-only access. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

**Example**
```bash
curl -X DELETE "<SERVER_URL>/api/projects/my-repo/main/claude/opus?owner=alice" \
  -H "Authorization: Bearer <ADMIN_KEY>"
```

### `POST /api/projects/{name}/abort`

Aborts the single active generation found for a project name.

**Auth:** `user` or `admin`.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `name` | Path | `string` | — | Project name to search for among active generations. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `{ "aborted": string }` | Cancels the active generation for `name`. |
| `400` | `ErrorResponse` | Invalid project name format. |
| `404` | `ErrorResponse` | No active generation matched `name`. |
| `409` | `ErrorResponse` | More than one active variant matched `name`, or cancellation did not complete cleanly. |
| `403` | `ErrorResponse` | Caller has read-only access. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

> **Note:** This route does not disambiguate by `branch`, `provider`, `model`, or `owner`. Use the branch-specific abort route when more than one active variant can exist.

**Example**
```bash
curl -X POST "<SERVER_URL>/api/projects/my-repo/abort" \
  -H "Authorization: Bearer <USER_API_KEY>"
```

### `POST /api/projects/{name}/{branch}/{provider}/{model}/abort`

Aborts one specific active variant.

**Auth:** `user` or `admin`.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `name` | Path | `string` | — | Project name. |
| `branch` | Path | `string` | — | Variant branch. |
| `provider` | Path | `string` | — | Variant provider. |
| `model` | Path | `string` | — | Variant model. |
| `owner` | Query | `string` | — | Admin-only disambiguation parameter. Required when the same active variant exists under multiple owners. Ignored for non-admin requests. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `{ "aborted": "name/branch/provider/model" }` | Cancels the target active generation. |
| `400` | `ErrorResponse` | Invalid project name format. |
| `404` | `ErrorResponse` | No active generation exists for the specified variant. |
| `409` | `ErrorResponse` | Multiple owners matched and no `owner` was supplied, or cancellation is already finishing. |
| `403` | `ErrorResponse` | Caller has read-only access. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

**Example**
```bash
curl -X POST "<SERVER_URL>/api/projects/my-repo/main/claude/opus/abort?owner=alice" \
  -H "Authorization: Bearer <ADMIN_KEY>"
```

### `DELETE /api/projects/{name}`

Deletes all variants for one project name within one owner scope.

**Auth:** `user` or `admin`.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `name` | Path | `string` | — | Project name. |
| `owner` | Query | `string` | — | Required for admin requests. Non-admin requests delete only the caller's own variants. Use an empty value only for legacy ownerless variants. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `{ "deleted": string }` | Deletes all variants of `name` for the target owner scope. |
| `400` | `ErrorResponse` | Invalid project name format, or missing `owner` on an admin request. |
| `404` | `ErrorResponse` | No matching variants found. |
| `409` | `ErrorResponse` | At least one targeted variant is still generating. |
| `403` | `ErrorResponse` | Caller has read-only access. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

**Example**
```bash
curl -X DELETE "<SERVER_URL>/api/projects/my-repo?owner=alice" \
  -H "Authorization: Bearer <ADMIN_KEY>"
```

## Admin Routes

### `POST /api/admin/users`

Creates a new database-backed user and returns the raw API key.

**Auth:** Admin only.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `username` | Body | `string` | — | New username. Must be 2-50 characters using letters, digits, `.`, `_`, or `-`. `admin` is reserved. |
| `role` | Body | `string` | `user` | Role for the new user: `admin`, `user`, or `viewer`. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `{ "username": string, "api_key": string, "role": string }` | Creates the user, generates a raw API key, and returns `Cache-Control: no-store`. |
| `400` | `ErrorResponse` | Malformed JSON, non-object body, missing username, invalid username, reserved username, or invalid role. |
| `403` | `ErrorResponse` | Caller is not an admin. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

> **Warning:** `api_key` is the only copy of the raw key returned by this route.

**Example**
```bash
curl -X POST "<SERVER_URL>/api/admin/users" \
  -H "Authorization: Bearer <ADMIN_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","role":"viewer"}'
```

### `DELETE /api/admin/users/{username}`

Deletes a user account.

**Auth:** Admin only.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `username` | Path | `string` | — | Username to delete. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `{ "deleted": string }` | Deletes the user, invalidates their sessions, deletes their owned projects, and removes related access-control entries. |
| `400` | `ErrorResponse` | Attempted to delete the current admin user. |
| `404` | `ErrorResponse` | User not found. |
| `409` | `ErrorResponse` | The user currently has a generation in progress. |
| `403` | `ErrorResponse` | Caller is not an admin. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

**Example**
```bash
curl -X DELETE "<SERVER_URL>/api/admin/users/alice" \
  -H "Authorization: Bearer <ADMIN_KEY>"
```

### `GET /api/admin/users`

Lists all database users.

**Auth:** Admin only.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| None | — | — | — | This endpoint does not accept parameters. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `{ "users": User[] }` | Returns all users without API key hashes. |
| `403` | `ErrorResponse` | Caller is not an admin. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

**Example**
```bash
curl "<SERVER_URL>/api/admin/users" \
  -H "Authorization: Bearer <ADMIN_KEY>"
```

### `POST /api/admin/projects/{name}/access`

Grants a user access to all variants of one project name for one owner.

**Auth:** Admin only.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `name` | Path | `string` | — | Project name to share. |
| `username` | Body | `string` | — | Username receiving access. |
| `owner` | Body | `string` | — | Owner whose project namespace is being shared. Required. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `{ "granted": string, "username": string, "owner": string }` | Creates the access grant if needed. The grant applies to all variants of `name` owned by `owner`. |
| `400` | `ErrorResponse` | Malformed JSON, non-object body, or missing required fields. |
| `404` | `ErrorResponse` | Target user not found, or project name not found for `owner`. |
| `403` | `ErrorResponse` | Caller is not an admin. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

> **Note:** Grants are project-wide for the `(name, owner)` pair, not variant-specific.

**Example**
```bash
curl -X POST "<SERVER_URL>/api/admin/projects/my-repo/access" \
  -H "Authorization: Bearer <ADMIN_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"username":"bob","owner":"alice"}'
```

### `DELETE /api/admin/projects/{name}/access/{username}`

Revokes a user's access to one project name for one owner.

**Auth:** Admin only.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `name` | Path | `string` | — | Project name. |
| `username` | Path | `string` | — | Username whose grant will be removed. |
| `owner` | Query | `string` | — | Owner whose project namespace is being updated. Required. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `{ "revoked": string, "username": string, "owner": string }` | Removes the access grant if present. |
| `400` | `ErrorResponse` | Missing `owner`. |
| `403` | `ErrorResponse` | Caller is not an admin. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

**Example**
```bash
curl -X DELETE "<SERVER_URL>/api/admin/projects/my-repo/access/bob?owner=alice" \
  -H "Authorization: Bearer <ADMIN_KEY>"
```

### `GET /api/admin/projects/{name}/access`

Lists usernames with access to one project name for one owner.

**Auth:** Admin only.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `name` | Path | `string` | — | Project name. |
| `owner` | Query | `string` | — | Owner whose project namespace is being inspected. Required. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `{ "project": string, "owner": string, "users": string[] }` | Returns the usernames currently granted access for the `(name, owner)` pair. |
| `400` | `ErrorResponse` | Missing `owner`. |
| `403` | `ErrorResponse` | Caller is not an admin. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

**Example**
```bash
curl "<SERVER_URL>/api/admin/projects/my-repo/access?owner=alice" \
  -H "Authorization: Bearer <ADMIN_KEY>"
```

### `POST /api/admin/users/{username}/rotate-key`

Rotates another user's API key.

**Auth:** Admin only.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `username` | Path | `string` | — | Username whose key will be rotated. |
| `new_key` | Body | `string` | Auto-generated | Optional replacement API key. Must be at least 16 characters when supplied. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | `{ "username": string, "new_api_key": string }` | Rotates the user's key, invalidates that user's sessions, and returns `Cache-Control: no-store`. |
| `400` | `ErrorResponse` | Malformed JSON, non-object body, or invalid custom key. |
| `404` | `ErrorResponse` | User not found. |
| `403` | `ErrorResponse` | Caller is not an admin. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

> **Warning:** `new_api_key` is the only copy of the raw key returned by this route.

**Example**
```bash
curl -X POST "<SERVER_URL>/api/admin/users/alice/rotate-key" \
  -H "Authorization: Bearer <ADMIN_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"new_key":"alice-rotated-password-123"}'
```

## Download Routes

### `GET /api/projects/{name}/{branch}/{provider}/{model}/download`

Downloads one specific ready variant as a `.tar.gz` archive.

**Auth:** Bearer token or `docsfy_session` cookie.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `name` | Path | `string` | — | Project name. |
| `branch` | Path | `string` | — | Variant branch. |
| `provider` | Path | `string` | — | Variant provider. |
| `model` | Path | `string` | — | Variant model. |
| `owner` | Query | `string` | — | Admin-only disambiguation parameter. Required when the same variant exists under multiple owners. Ignored for non-admin requests. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | Binary `application/gzip` | Streams the generated site as a tarball. `Content-Disposition` filename format: `<name>-<branch>-<provider>-<model>-docs.tar.gz`. |
| `400` | `ErrorResponse` | Variant exists but is not `ready`. |
| `404` | `ErrorResponse` | Variant not found, not accessible, or site files are missing. |
| `409` | `ErrorResponse` | Multiple owners match the same variant and no `owner` was supplied. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

**Example**
```bash
curl -L "<SERVER_URL>/api/projects/my-repo/main/claude/opus/download?owner=alice" \
  -H "Authorization: Bearer <ADMIN_KEY>" \
  -o my-repo-main-claude-opus-docs.tar.gz
```

### `GET /api/projects/{name}/download`

Downloads the most recently generated accessible ready variant for one project name.

**Auth:** Bearer token or `docsfy_session` cookie.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `name` | Path | `string` | — | Project name. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | Binary `application/gzip` | Streams the latest accessible ready variant as a tarball. `Content-Disposition` filename format: `<name>-docs.tar.gz`. |
| `404` | `ErrorResponse` | No accessible ready variant exists, or the site directory is missing. |
| `409` | `ErrorResponse` | For non-admin callers, multiple accessible owners tied for the newest variant timestamp. |
| `401` | `ErrorResponse` | Missing or invalid authentication. |

> **Note:** This route does not accept `owner`. To target a specific owner, use the variant-specific download route.

**Example**
```bash
curl -L "<SERVER_URL>/api/projects/my-repo/download" \
  -H "Authorization: Bearer <USER_API_KEY>" \
  -o my-repo-docs.tar.gz
```

## Docs Routes

### `GET /docs/{project}/{branch}/{provider}/{model}/{path}`

Serves one file from a specific generated variant.

**Auth:** Bearer token or `docsfy_session` cookie.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `project` | Path | `string` | — | Project name. |
| `branch` | Path | `string` | — | Variant branch. |
| `provider` | Path | `string` | — | Variant provider. |
| `model` | Path | `string` | — | Variant model. |
| `path` | Path | `string` | `index.html` | Relative path inside the generated `site/` directory. Nested paths are allowed. A trailing `/` resolves to `index.html`. |
| `owner` | Query | `string` | — | Admin-only disambiguation parameter. Required when the same variant exists under multiple owners. Ignored for non-admin requests. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | File response | Returns the requested generated file. |
| `403` | `ErrorResponse` | `path` escaped the generated site directory. |
| `404` | `ErrorResponse` | Variant not found, not accessible, or file not found. |
| `409` | `ErrorResponse` | Multiple owners match the same variant and no `owner` was supplied. |
| `401` | `ErrorResponse` | Missing or invalid authentication for non-HTML clients. |
| `302` | Redirect | Unauthenticated HTML clients are redirected to `/login`. |

**Example**
```bash
curl "<SERVER_URL>/docs/my-repo/main/claude/opus/index.html?owner=alice" \
  -H "Authorization: Bearer <ADMIN_KEY>"
```

### `GET /docs/{project}/{path}`

Serves one file from the most recently generated accessible ready variant for a project name.

**Auth:** Bearer token or `docsfy_session` cookie.

**Parameters**

| Name | In | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `project` | Path | `string` | — | Project name. |
| `path` | Path | `string` | `index.html` | Relative path inside the generated `site/` directory. Nested paths are allowed. A trailing `/` resolves to `index.html`. |

**Returns**

| Status | Body | Effect |
| --- | --- | --- |
| `200` | File response | Returns the requested file from the latest accessible ready variant. |
| `403` | `ErrorResponse` | `path` escaped the generated site directory. |
| `404` | `ErrorResponse` | No accessible ready docs exist, or the file was not found. |
| `409` | `ErrorResponse` | For non-admin callers, multiple accessible owners tied for the newest variant timestamp. |
| `401` | `ErrorResponse` | Missing or invalid authentication for non-HTML clients. |
| `302` | Redirect | Unauthenticated HTML clients are redirected to `/login`. |

> **Note:** This route does not accept `owner`. To target a specific owner and variant, use the variant-specific docs route.

**Example**
```bash
curl "<SERVER_URL>/docs/my-repo/index.html" \
  -H "Authorization: Bearer <USER_API_KEY>"
```

## Related Pages

- [WebSocket Reference](websocket-reference.html)
- [Manage Users, Roles, and Access](manage-users-roles-and-access.html)
- [View, Download, and Publish Docs](view-download-and-publish-docs.html)
- [Generate Documentation](generate-documentation.html)
- [Track Generation Progress](track-generation-progress.html)

---

Source: websocket-reference.md

# WebSocket Reference

## Endpoint

### `/api/ws`

**Description:** Real-time endpoint for project sync and generation updates. One connection receives every update visible to the authenticated user. There is no subscribe or filter command.

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `scheme` | `ws` \| `wss` | `wss` on HTTPS, `ws` on HTTP | Transport scheme for the socket URL. |
| `path` | string | `/api/ws` | WebSocket endpoint path. |
| `token` | string | None | Optional query parameter. Accepts the raw `ADMIN_KEY` or a user's API key. No username parameter is used. |
| `docsfy_session` | string | None | Optional cookie. Opaque session token set by `POST /api/auth/login`; `HttpOnly`, `SameSite=Strict`, max age `28800` seconds, `Secure` controlled by `SECURE_COOKIES`. |

```js
// Same-origin browser session
const ws = new WebSocket('wss://docs.example.com/api/ws')

// Direct token auth
const wsWithToken = new WebSocket('wss://docs.example.com/api/ws?token=<API_KEY>')
```

**Return value/effect:**
- Successful connections are accepted and immediately receive a `sync` message.
- Admins receive updates for all variants.
- Other authenticated users receive updates for owned variants plus variants shared with them.
- Access grants and revocations trigger a new `sync` for the affected user.

| Close code | Effect |
| --- | --- |
| `1008` | Authentication failed. |
| `1001` | Heartbeat timeout after 2 missed `pong` replies. |

> **Note:** `/api/ws` authenticates with the `token` query parameter or the `docsfy_session` cookie. It does not read `Authorization` headers.

> **Note:** See [Manage Users, Roles, and Access](manage-users-roles-and-access.html) for access assignment and role details.

> **Warning:** `?token=` places credentials in the URL.

## Message Reference

### `sync`

**Description:** Full replacement snapshot for the authenticated user's visible project list and lookup maps.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | string | `sync` | Message discriminator. |
| `projects` | `Project[]` | `[]` | Visible project variants, sorted by `updated_at` descending. Includes owned variants and any shared variants the user can access. |
| `known_models` | `Record<string, string[]>` | `{}` | Ready model names grouped by provider. Built from ready projects only. |
| `known_branches` | `Record<string, string[]>` | `{}` | Ready branch names grouped by project name. Admins receive all ready branches; non-admins receive owned ready branches only. |

**`sync.projects[]` object**

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | string | n/a | Project name. |
| `branch` | string | `main` | Variant branch. |
| `ai_provider` | string | `""` | Provider name stored on the project record. |
| `ai_model` | string | `""` | Model name stored on the project record. |
| `owner` | string | `""` | Project owner username. |
| `repo_url` | string | n/a | Source repository URL, or the local repository path string when generation used `repo_path`. |
| `status` | string | `generating` | One of `generating`, `ready`, `error`, or `aborted`. |
| `current_stage` | string \| null | `null` | Current stage string, `up_to_date`, or `null`. |
| `last_commit_sha` | string \| null | `null` | Git commit SHA for the variant, when available. |
| `last_generated` | string \| null | `null` | Generation timestamp string in `YYYY-MM-DD HH:MM:SS` format, when available. |
| `page_count` | integer | `0` | Generated page count. |
| `error_message` | string \| null | `null` | Failure or abort text, when available. |
| `plan_json` | string \| null | `null` | Serialized documentation plan JSON string. |
| `created_at` | string | current timestamp | Row creation timestamp. |
| `updated_at` | string | current timestamp | Last update timestamp. |

```json
{
  "type": "sync",
  "projects": [
    {
      "name": "docsfy",
      "branch": "main",
      "ai_provider": "cursor",
      "ai_model": "gpt-5.4-xhigh-fast",
      "owner": "alice",
      "repo_url": "https://github.com/myk-org/docsfy",
      "status": "ready",
      "current_stage": null,
      "last_commit_sha": "abc123def456",
      "last_generated": "2026-04-17 12:34:56",
      "page_count": 14,
      "error_message": null,
      "plan_json": "{\"project_name\":\"docsfy\",\"navigation\":[...]}",
      "created_at": "2026-04-17 12:10:00",
      "updated_at": "2026-04-17 12:34:56"
    }
  ],
  "known_models": {
    "cursor": ["gpt-5.4-xhigh-fast"]
  },
  "known_branches": {
    "docsfy": ["main"]
  }
}
```

**Return value/effect:**
- Sent immediately after a successful connection.
- Sent again after server-side resync events, including terminal status updates, deletes, variant replacement, and access changes.
- Replace local state with the new payload instead of patching individual fields.

> **Warning:** `sync.projects[]` uses `ai_provider` and `ai_model`. `progress` and `status_change` use `provider` and `model`.

### `progress`

**Description:** Variant-scoped in-flight update while generation is still running.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | string | `progress` | Message discriminator. |
| `name` | string | n/a | Project name. |
| `branch` | string | `main` | Variant branch. |
| `provider` | string | n/a | AI provider for the active variant. |
| `model` | string | n/a | AI model for the active variant. |
| `owner` | string | n/a | Project owner username. |
| `status` | string | `generating` | Current run status. The current backend sends `generating` for this message type. |
| `current_stage` | string | Not sent | Current generation stage. |
| `page_count` | integer | Not sent | Generated page count so far. |
| `plan_json` | string \| null | Not sent | Serialized documentation plan JSON string. |
| `error_message` | string \| null | Not sent | In-flight error detail, when supplied by the backend. |

**`current_stage` values**

| Value | Description |
| --- | --- |
| `cloning` | Repository clone or local repository load has started. |
| `planning` | Full planning is running. |
| `incremental_planning` | Incremental planner is deciding which pages to regenerate. |
| `generating_pages` | Page generation is running. |
| `validating` | Post-generation validation is running. |
| `cross_linking` | Cross-linking and internal-link fixes are running. |
| `rendering` | Static site rendering is running. |

```json
{
  "type": "progress",
  "name": "docsfy",
  "branch": "main",
  "provider": "cursor",
  "model": "gpt-5.4-xhigh-fast",
  "owner": "alice",
  "status": "generating",
  "current_stage": "generating_pages",
  "page_count": 3,
  "plan_json": "{\"project_name\":\"docsfy\",\"navigation\":[...]}"
}
```

**Return value/effect:**
- Apply this message to the variant identified by `name`, `branch`, `provider`, `model`, and `owner`.
- During `generating_pages`, `page_count` increments as pages are generated.
- During later stages, `page_count` carries the generated page total.
- `plan_json` is a JSON string, not a parsed object.

> **Tip:** Match `progress` messages by `name`, `branch`, `provider`, `model`, and `owner`.

### `status_change`

**Description:** Variant-scoped terminal update.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | string | `status_change` | Message discriminator. |
| `name` | string | n/a | Project name. |
| `branch` | string | `main` | Variant branch. |
| `provider` | string | n/a | AI provider for the variant. |
| `model` | string | n/a | AI model for the variant. |
| `owner` | string | n/a | Project owner username. |
| `status` | string | n/a | Terminal status. One of `ready`, `error`, or `aborted`. |
| `page_count` | integer | Not sent | Final generated page count, when available. |
| `last_generated` | string \| null | Not sent | Completion timestamp in `YYYY-MM-DD HH:MM:SS` format. Sent on `ready`. |
| `last_commit_sha` | string \| null | Not sent | Commit SHA for the completed or reused variant, when available. |
| `error_message` | string \| null | Not sent | Failure or abort text, when available. |

**`status` values**

| Value | Description |
| --- | --- |
| `ready` | Generation completed successfully or the variant was reused as current. |
| `error` | Generation failed. |
| `aborted` | Generation was cancelled or aborted by the user. |

```json
{
  "type": "status_change",
  "name": "docsfy",
  "branch": "main",
  "provider": "cursor",
  "model": "gpt-5.4-xhigh-fast",
  "owner": "alice",
  "status": "ready",
  "page_count": 14,
  "last_generated": "2026-04-17 12:34:56",
  "last_commit_sha": "abc123def456"
}
```

**Return value/effect:**
- Emitted only for `ready`, `error`, and `aborted`.
- Every `status_change` is followed by a fresh `sync`.
- When a variant is reused without regeneration, the terminal message still uses `status: "ready"`. The following `sync.projects[]` record may show `current_stage: "up_to_date"`.

## Heartbeat

### `ping`

**Description:** Server heartbeat probe.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | string | `ping` | Heartbeat message sent by the server. |

```json
{
  "type": "ping"
}
```

**Return value/effect:**
- Sent every `30` seconds.
- The server waits `10` seconds for the matching `pong`.

### `pong`

**Description:** Client heartbeat reply.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | string | `pong` | Heartbeat reply sent by the client. |

```json
{
  "type": "pong"
}
```

**Return value/effect:**
- Resets the missed-heartbeat counter for the current connection.
- After `2` missed `pong` replies, the server closes the socket with code `1001`.
- Client messages other than valid JSON `{"type":"pong"}` are ignored.

## Related Pages

- [Track Generation Progress](track-generation-progress.html)
- [HTTP API Reference](http-api-reference.html)
- [Manage Users, Roles, and Access](manage-users-roles-and-access.html)
- [Fix Setup and Generation Problems](fix-setup-and-generation-problems.html)
- [Manage docsfy from the CLI](manage-docsfy-from-the-cli.html)

---
