# docsfy

> Turn any Git repository into a polished documentation site in minutes

---

Source: quickstart.md

# Getting Started with docsfy

Generate a polished, browsable documentation site from any Git repository in minutes. This guide walks you through installing docsfy, configuring it, and producing your first set of docs.

## Prerequisites

- **Docker** and **Docker Compose** (for running the server)
- A **Git repository** (public HTTPS URL) you want to document
- (Optional) [uv](https://docs.astral.sh/uv/) for installing the CLI tool

## Quick Start

```bash
git clone https://github.com/myk-org/docsfy.git && cd docsfy
cp .env.example .env   # then set ADMIN_KEY (min 16 chars)
docker compose up       # open http://localhost:8000
```

That's it — the web dashboard is now running at `http://localhost:8000`. Log in with username `admin` and the `ADMIN_KEY` you set, paste a repo URL, and click **Generate**.

## Step-by-Step Setup

### 1. Clone the repository

```bash
git clone https://github.com/myk-org/docsfy.git
cd docsfy
```

### 2. Create your environment file

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

Open `.env` and set the required `ADMIN_KEY` value. This is the master password for the admin account and must be at least 16 characters:

```bash
ADMIN_KEY=your-secure-password-here
```

> **Warning:** Never commit your `.env` file to version control. It contains secrets.

For local HTTP development (not behind HTTPS), also add:

```bash
SECURE_COOKIES=false
```

### 3. Start the server

```bash
docker compose up
```

Docker builds the application image, starts the AI sidecar service, and launches the web server on port **8000**. Wait for the health check to pass, then open your browser to `http://localhost:8000`.

### 4. Log in

On the login screen, enter:

| Field    | Value                        |
|----------|------------------------------|
| Username | `admin`                      |
| Password | The `ADMIN_KEY` from `.env`  |

### 5. Generate your first docs

1. In the dashboard, paste a Git repository URL (e.g., `https://github.com/myk-org/for-testing-only`).
2. Leave **Branch** as `main` (or pick another branch).
3. Click **Generate**.
4. Watch the progress in real time — docsfy clones the repo, plans the documentation structure, generates pages with AI, and renders a static HTML site.

When the status shows **Ready**, click the project name to browse your generated docs.

> **Tip:** docsfy auto-detects the repository type (app, library, framework, or tests) and tailors the documentation structure accordingly. You can override this in the generate form if needed.

## Using the CLI

The CLI lets you do everything from the terminal — generate, check status, download, and manage projects.

### Install the CLI

```bash
uv tool install docsfy
```

### Configure a server profile

```bash
docsfy config init
```

You'll be prompted for:

| Prompt       | Example value              |
|--------------|----------------------------|
| Profile name | `dev`                      |
| Server URL   | `http://localhost:8000`     |
| Username     | `admin`                    |
| Password     | Your `ADMIN_KEY`           |

This saves a profile to `~/.config/docsfy/config.toml`. You can add multiple server profiles (dev, staging, prod) and switch between them with `--server`:

```bash
docsfy --server prod list
```

### Generate docs from the terminal

```bash
docsfy generate https://github.com/org/repo
```

Target a specific branch:

```bash
docsfy generate https://github.com/org/repo --branch dev
```

Watch generation progress in real time:

```bash
docsfy generate https://github.com/org/repo --watch
```

### Check project status

```bash
docsfy list
docsfy status my-repo
```

### Download generated docs

```bash
docsfy download my-repo --output ./my-docs --flatten
```

This extracts the generated HTML site into `./my-docs`, ready to deploy anywhere.

> **Tip:** See [Using the CLI](using-the-cli.html) for the full setup guide and [CLI Command Reference](cli-reference.html) for all available commands and flags.

## Understanding Variants

Every documentation build is a **variant** — a unique combination of project name, branch, AI provider, and AI model. This means you can generate docs for the same repo on different branches or with different AI models and compare the results side by side.

The URL pattern for browsing a specific variant is:

```
http://localhost:8000/docs/{project}/{branch}/{provider}/{model}/
```

For example: `http://localhost:8000/docs/my-repo/main/cursor/gpt-5.4-xhigh-fast/`

If you browse `/docs/{project}/` without specifying a variant, docsfy serves the most recently generated one.

See [Browsing Generated Documentation](browsing-docs.html) for more on navigating and sharing doc URLs.

## Advanced Usage

### Choosing an AI provider and model

docsfy supports three AI providers: **claude**, **gemini**, and **cursor**. The server defaults (set via `AI_PROVIDER` and `AI_MODEL` in `.env`) are used for new generations unless you override them.

From the CLI:

```bash
docsfy generate https://github.com/org/repo --provider claude --model claude-sonnet-4-20250514
```

To see available providers and models:

```bash
docsfy models
```

See [Configuring AI Providers](configuring-ai-providers.html) for details on provider setup and the sidecar service.

### Force a full regeneration

By default, docsfy performs **incremental updates** — it detects code changes since the last generation and only regenerates affected pages. To force a complete rebuild:

```bash
docsfy generate https://github.com/org/repo --force
```

Or check the **Force** checkbox in the web dashboard.

See [Working with Incremental Updates](incremental-updates.html) for how change detection works.

### Specifying the repository type

docsfy auto-detects whether your repo is an app, library, framework, or test suite. You can override this to get better-tailored documentation:

```bash
docsfy generate https://github.com/org/repo --repo-type library
```

Valid types: `app`, `library`, `framework`, `tests`.

### Managing users

Create additional user accounts with the CLI:

```bash
docsfy admin users create alice --role user
```

Roles control access levels:

| Role     | Permissions                              |
|----------|------------------------------------------|
| `admin`  | Full access — manage users, all projects |
| `user`   | Generate, view, and manage own projects  |
| `viewer` | Read-only access to shared projects      |

See [Managing Users and Access Control](managing-users.html) for the full user management guide.

### Running in production

For production deployments with persistent storage, TLS, and custom configuration, see [Deploying with Docker](deployment.html).

## Troubleshooting

**"ADMIN_KEY environment variable is required"**
Set `ADMIN_KEY` in your `.env` file. It must be at least 16 characters.

**Login works but the session drops immediately**
If you're running over plain HTTP (not HTTPS), set `SECURE_COOKIES=false` in `.env`. Secure cookies are rejected by browsers on non-HTTPS connections.

**"Variant is already being generated" (409 error)**
A generation is already running for the same project/branch/provider/model combination. Wait for it to finish, or abort it:

```bash
docsfy abort my-repo --branch main --provider cursor --model gpt-5.4-xhigh-fast
```

**Generation fails with sidecar errors**
The AI sidecar service must be running and healthy. In Docker, the entrypoint handles this automatically. Check the container logs for `[sidecar] Sidecar is ready`. If the sidecar isn't starting, verify your AI provider credentials are configured correctly — see [Configuring AI Providers](configuring-ai-providers.html).

**Branch names with slashes are rejected**
Branch names cannot contain `/` because they appear as URL path segments. Use hyphens instead (e.g., `release-1.x` instead of `release/1.x`).

## Next Steps

- [Generating Documentation](generating-docs.html) — detailed guide on all generation options
- [Managing Projects and Variants](managing-projects.html) — list, inspect, and delete projects
- [Configuration Reference](configuration-reference.html) — all environment variables and settings
- [Common Workflow Recipes](recipes-common-workflows.html) — CI/CD automation, multi-branch docs, and more

## Related Pages

- [Generating Documentation](generating-docs.html)
- [Deploying with Docker](deployment.html)
- [Using the CLI](using-the-cli.html)
- [Configuration Reference](configuration-reference.html)
- [Managing Projects and Variants](managing-projects.html)

---

Source: deployment.md

# Deploying with Docker

Run docsfy as a self-hosted documentation service so your team can generate and browse AI-powered docs from a shared server, with data that survives container restarts.

## Prerequisites

- Docker Engine 20.10+ and Docker Compose v2
- An `ADMIN_KEY` password (minimum 16 characters) — this is your admin login credential
- At least one AI provider CLI credential available to the container (Cursor is pre-installed in the image)

## Quick Example

```bash
git clone https://github.com/myk-org/docsfy.git
cd docsfy
cp .env.example .env
```

Edit `.env` and set your admin password:

```dotenv
ADMIN_KEY=change-this-to-a-16-plus-character-password
```

```bash
docker compose up -d
```

Open `http://localhost:8000/login` and sign in with username `admin` and your `ADMIN_KEY` value.

## Step-by-Step

### 1. Create the environment file

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

Edit `.env` with your production values:

```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=true
```

> **Warning:** `ADMIN_KEY` is required and must be at least 16 characters. The server will refuse to start without it.


> **Note:** Set `SECURE_COOKIES=false` only when running over plain HTTP (e.g., `http://localhost` for local testing). For any HTTPS deployment, keep it `true`.

See [Configuration Reference](configuration-reference.html) for the full list of environment variables.

### 2. Review the Compose file

The project ships a ready-to-use `docker-compose.yaml`:

```yaml
services:
  docsfy:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8000:8000"
    volumes:
      - ./data:/data
    env_file:
      - .env
    environment:
      - ADMIN_KEY=${ADMIN_KEY}
    restart: unless-stopped
```

Key points:

- **Port 8000** — the web UI, API, and generated doc sites are all served here.
- **`./data:/data`** — maps the host `data/` directory into the container at `/data`, where the database and generated documentation are stored.
- **`restart: unless-stopped`** — the container restarts automatically after crashes or host reboots (unless you explicitly stop it).

### 3. Build and start

```bash
docker compose up -d --build
```

The multi-stage build compiles the React frontend, the Pi SDK sidecar, and the Python backend into a single image. The first build takes a few minutes; subsequent builds use Docker layer caching.

### 4. Verify the deployment

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

Expected response:

```json
{"status": "ok"}
```

The built-in health check also verifies the AI sidecar is running. Check container health status with:

```bash
docker compose ps
```

The `STATUS` column should show `healthy` once both the server and sidecar pass their checks (this can take up to 30 seconds on first start).

### 5. Sign in and start generating

Open `http://localhost:8000/login` in your browser. Sign in with:

- **Username:** `admin`
- **Password:** your `ADMIN_KEY` value

You're now ready to generate documentation. See [Generating Documentation](generating-docs.html) for next steps.

## Persistent Storage

All docsfy state lives under a single directory (`/data` inside the container). The volume mount `./data:/data` ensures this data persists across container restarts, rebuilds, and upgrades.

The data directory contains:

| Path | Contents |
|---|---|
| `/data/docsfy.db` | SQLite database — users, projects, access control, sessions |
| `/data/projects/` | Generated documentation sites, page caches, and build artifacts |

> **Tip:** Back up the `data/` directory to preserve your entire docsfy state — database and all generated docs — in a single copy.

### Using a named Docker volume

If you prefer a named volume instead of a bind mount, replace the volumes section:

```yaml
services:
  docsfy:
    volumes:
      - docsfy-data:/data

volumes:
  docsfy-data:
```

## Environment Variable Reference

| Variable | Default | Description |
|---|---|---|
| `ADMIN_KEY` | *(required)* | Admin password (minimum 16 characters) |
| `AI_PROVIDER` | `cursor` | Default AI provider (`claude`, `gemini`, or `cursor`) |
| `AI_MODEL` | `gpt-5.4-xhigh-fast` | Default AI model |
| `AI_CLI_TIMEOUT` | `60` | Timeout in seconds for each AI CLI call |
| `MAX_CONCURRENT_PAGES` | `10` | Maximum parallel AI calls during generation |
| `LOG_LEVEL` | `INFO` | Logging level (`DEBUG`, `INFO`, `WARNING`, `ERROR`) |
| `DATA_DIR` | `/data` | Path inside the container for database and docs |
| `SECURE_COOKIES` | `true` | Set to `false` for HTTP-only deployments |
| `PORT` | `8000` | Server listen port |
| `SIDECAR_PORT` | `9100` | Internal AI sidecar port (rarely needs changing) |

See [Configuration Reference](configuration-reference.html) for details on every setting, and [Configuring AI Providers](configuring-ai-providers.html) for provider-specific setup.

## Advanced Usage

### Changing the exposed port

To run docsfy on a different host port, change the port mapping in `docker-compose.yaml`:

```yaml
ports:
  - "3000:8000"
```

Then access the service at `http://localhost:3000`. The internal `PORT` variable should stay at `8000` unless you have a specific reason to change it.

### Placing behind a reverse proxy

For production HTTPS deployments, put a reverse proxy (nginx, Caddy, Traefik) in front of docsfy. A minimal nginx configuration:

```nginx
server {
    listen 443 ssl;
    server_name docs.example.com;

    ssl_certificate     /etc/ssl/certs/docs.example.com.pem;
    ssl_certificate_key /etc/ssl/private/docs.example.com.key;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # WebSocket support for real-time generation updates
    location /api/ws {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}
```

> **Note:** Keep `SECURE_COOKIES=true` (the default) when serving over HTTPS. The WebSocket endpoint at `/api/ws` requires the `Upgrade` and `Connection` headers to be forwarded.

### Tuning generation performance

For servers with more CPU and memory, increase parallel AI calls:

```dotenv
MAX_CONCURRENT_PAGES=20
AI_CLI_TIMEOUT=120
```

For constrained environments, reduce concurrency:

```dotenv
MAX_CONCURRENT_PAGES=4
```

### Development mode

For local development with hot reload, uncomment the dev options in `docker-compose.yaml`:

```yaml
services:
  docsfy:
    ports:
      - "8000:8000"
      - "5173:5173"
    volumes:
      - ./data:/data
      - ./frontend:/app/frontend
    environment:
      - ADMIN_KEY=${ADMIN_KEY}
      - DEV_MODE=true
```

This starts the Vite dev server on port 5173 alongside the FastAPI backend with auto-reload. Edit frontend files on your host and see changes immediately.

### Upgrading docsfy

```bash
git pull
docker compose up -d --build
```

Your data in `./data` is preserved across rebuilds. The database schema automatically migrates on startup when needed.

> **Tip:** Run `docker compose down` before upgrading if you want a clean restart, but this is not required — `up --build` replaces the running container in place.

## Troubleshooting

- **Container exits immediately** — Check logs with `docker compose logs docsfy`. The most common cause is a missing or too-short `ADMIN_KEY`.

- **Health check fails** — The health check probes both the server (`/health` on port 8000) and the AI sidecar (port 9100). Run `docker compose logs docsfy | grep sidecar` to check if the sidecar started successfully. It can take up to 30 seconds on first boot.

- **Browser login redirects back to `/login`** — If you're accessing over plain HTTP, set `SECURE_COOKIES=false` in `.env` and restart the container.

- **Permission denied on `./data`** — The container runs as a non-root user (UID 1000, GID 0). Ensure the host `data/` directory is writable:
  ```bash
  mkdir -p data && chmod 775 data
  ```

- **Generation fails with provider errors** — Verify your AI provider is configured correctly. See [Configuring AI Providers](configuring-ai-providers.html) for provider-specific requirements.

- **Out of disk space** — Generated documentation and page caches accumulate in `./data/projects/`. Delete old projects through the web dashboard or API to free space. See [Managing Projects and Variants](managing-projects.html) for cleanup options.

## Related Pages

- [Getting Started with docsfy](quickstart.html)
- [Configuration Reference](configuration-reference.html)
- [Configuring AI Providers](configuring-ai-providers.html)
- [Managing Projects and Variants](managing-projects.html)
- [Generating Documentation](generating-docs.html)

---

Source: generating-docs.md

# Generating Documentation

Submit a repository to docsfy and get a complete documentation site generated by AI. This guide walks you through starting a generation job from the web dashboard or CLI, picking the right AI provider and branch, and watching the progress in real time.

## Prerequisites

- A running docsfy server (see [Getting Started with docsfy](quickstart.html))
- A user account with **user** or **admin** role (viewers cannot generate docs)
- The repository you want to document must be accessible via HTTPS or SSH

## Quick Example

**From the CLI:**

```bash
docsfy generate https://github.com/your-org/your-repo
```

That's it. docsfy clones the repo, plans the documentation structure, generates every page with AI, validates the output, and publishes a browsable site — all from a single command.

## Generating via the Web Dashboard

1. **Log in** to the docsfy dashboard at your server URL.
2. Click the **+** button in the sidebar to open the generation form.
3. Enter the **Repository URL** (HTTPS or SSH format).
4. Choose a **Branch** (defaults to `main`). If you've generated docs for this repo before, previously used branches appear as suggestions.
5. Optionally select a **Repository Type** — `app`, `library`, `framework`, or `tests`. Leave it on **Auto-detect** and docsfy will figure it out.
6. Pick a **Provider** and **Model** from the dropdowns. The server's default provider and model are pre-selected.
7. Click **Generate**.

A toast notification confirms that generation has started, and the project immediately appears in the sidebar with a spinning status indicator.

> **Tip:** The form remembers your last repository URL, branch, and repository type across page reloads so you can quickly re-generate after code changes.

## Generating via the CLI

### Basic Usage

```bash
docsfy generate https://github.com/your-org/your-repo
```

The CLI uses the server defaults for AI provider and model. The output shows the project name, branch, status, and a unique **Generation ID** you can use to check progress later.

### Specifying Branch, Provider, and Model

```bash
docsfy generate https://github.com/your-org/your-repo \
  --branch dev \
  --provider claude \
  --model claude-sonnet-4-20250514
```

### Forcing Full Regeneration

By default docsfy performs an incremental update when the documentation already exists — only pages affected by code changes are regenerated. To start from scratch:

```bash
docsfy generate https://github.com/your-org/your-repo --force
```

On the dashboard, check the **Force full regeneration** checkbox before clicking Generate.

### Specifying Repository Type

```bash
docsfy generate https://github.com/your-org/your-repo --repo-type library
```

Valid types are `app`, `tests`, `library`, and `framework`. Each type produces documentation tailored to the repository's purpose — for example, a `library` type emphasizes API reference pages, while an `app` type focuses on usage guides.

## Monitoring Progress in Real Time

### Dashboard Activity Log

When you select a generating project in the sidebar, the detail panel shows a live **Activity Log** that tracks every stage:

| Stage | What's happening |
|---|---|
| **Cloning** | Fetching the repository from Git |
| **Analyzing** | Building a knowledge graph of the codebase |
| **Planning** | AI designs the documentation structure and page list |
| **Generating pages** | Each page is generated individually — the log shows progress like "Generating page 3 of 12: Authentication" |
| **Validating** | Checking generated content against the actual codebase |
| **Completeness check** | Identifying and filling gaps in documentation coverage |
| **Cross-linking** | Adding links between related pages |
| **Rendering** | Building the final HTML documentation site |

A progress bar shows overall completion, and each stage displays a ✓ when done, a spinner when active, or a circle when pending.

### CLI Watch Mode

Add `--watch` to stream progress directly in your terminal:

```bash
docsfy generate https://github.com/your-org/your-repo --watch
```

The CLI connects via WebSocket and prints stage updates as they happen:

```
Project: your-repo
Branch: main
Status: generating
Generation ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Watching generation progress...
[generating] cloning
[generating] analyzing
[generating] planning
[generating] generating_pages (3 pages)
[generating] generating_pages (7 pages)
[generating] validating (12 pages)
[generating] cross_linking (12 pages)
[generating] rendering (12 pages)
Generation complete! (12 pages)
```

### Checking Status After the Fact

If you didn't use `--watch`, check on a generation at any time:

```bash
docsfy status your-repo
```

Or use the Generation ID:

```bash
docsfy status a1b2c3d4-e5f6-7890-abcd-ef1234567890
```

See [Managing Projects and Variants](managing-projects.html) for more on inspecting and managing your projects.

## Aborting a Generation

Changed your mind? Stop a running generation from the dashboard by clicking the **Abort** button in the variant detail panel, or from the CLI:

```bash
docsfy abort your-repo
```

To abort a specific variant:

```bash
docsfy abort your-repo --branch dev --provider claude --model claude-sonnet-4-20250514
```

## Understanding Variants

Every combination of **project name + branch + provider + model** creates a distinct *variant*. You can generate documentation for the same repo with different settings and compare the results.

For example, generating `your-repo` on `main` with `cursor` and again with `claude` produces two independent documentation sites, each accessible at its own URL:

```
/docs/your-repo/main/cursor/gpt-5.4-xhigh-fast/
/docs/your-repo/main/claude/claude-sonnet-4-20250514/
```

See [Browsing Generated Documentation](browsing-docs.html) for details on navigating and sharing doc URLs.

## Advanced Usage

### Branch Name Rules

Branch names cannot contain slashes because they appear as a single URL path segment. Use hyphens instead:

| ✅ Valid | ❌ Invalid |
|---|---|
| `release-1.x` | `release/1.x` |
| `feature-auth` | `feature/auth` |
| `main` | `../main` |

Branch names must start with a letter or digit and can contain letters, digits, dots, hyphens, and underscores.

### Available Providers and Models

List all available AI providers and their models:

```bash
docsfy models
```

Filter by provider:

```bash
docsfy models --provider claude
```

The server exposes three providers: `claude`, `gemini`, and `cursor`. The available models for each provider are discovered automatically from the configured AI sidecar. See [Configuring AI Providers](configuring-ai-providers.html) for setup details.

### Incremental Updates

When you regenerate documentation for a repo that already has docs, docsfy automatically detects what changed since the last generation. Only affected pages are regenerated, making subsequent runs significantly faster.

The incremental pipeline:

1. Compares the current commit with the previously documented commit
2. Identifies which files changed
3. Determines which documentation pages are affected
4. Regenerates only those pages while keeping everything else intact

To bypass incremental mode and regenerate everything, use the `--force` flag.

See [Working with Incremental Updates](incremental-updates.html) for a deeper look at how change detection works.

### Cross-Provider Updates

If you previously generated docs with one provider (e.g., `cursor`) and now generate with a different provider (e.g., `claude`), docsfy reuses the existing documentation artifacts as a starting point. The new provider regenerates only what has changed, and the old variant is automatically replaced once the new one is ready.

### Concurrent Generation Limits

Each variant (project + branch + provider + model) can only have one active generation at a time. If you try to generate a variant that's already in progress, the server returns a `409 Conflict` error. Abort the running generation first, or wait for it to complete.

> **Note:** The server processes multiple page generation requests in parallel (up to 10 by default) to speed up large documentation sites. This is configured server-side and doesn't require any action from you.

## Troubleshooting

**"Variant is already being generated"**
Another generation for the same project/branch/provider/model combination is in progress. Use `docsfy abort <project>` or click Abort in the dashboard, then retry.

**"AI sidecar not available"**
The Pi SDK sidecar service isn't running or isn't reachable. See [Configuring AI Providers](configuring-ai-providers.html) for sidecar setup.

**"Invalid branch name"**
Branch names cannot contain slashes or start with special characters. Use hyphens instead of slashes (e.g., `release-1.x` instead of `release/1.x`).

**"Local repo path access requires admin privileges"**
Only admin users can generate docs from local filesystem paths. Non-admin users must provide a remote repository URL.

**Generation stuck or failed**
Check the Activity Log in the dashboard or run `docsfy status <project>` for the error message. Common causes include AI provider timeouts, network issues reaching the repository, or invalid repository URLs. You can retry with `docsfy generate <url> --force` to start fresh.

## Related Pages

- [Getting Started with docsfy](quickstart.html)
- [Working with Incremental Updates](incremental-updates.html)
- [Configuring AI Providers](configuring-ai-providers.html)
- [Managing Projects and Variants](managing-projects.html)
- [Browsing Generated Documentation](browsing-docs.html)

---

Source: managing-projects.md

# Managing Projects and Variants

You want to view, inspect, download, stop, or clean up the documentation projects docsfy has generated — and manage the individual branch/provider/model **variants** within each project.

## Prerequisites

- A running docsfy server with at least one generated project
- The `docsfy` CLI installed and configured (see [Getting Started with docsfy](quickstart.html)), **or** access to the web dashboard
- A user account with appropriate permissions (viewers can list and download; users and admins can delete and abort)

## Quick Example

List all your projects in one command:

```
docsfy list
```

```
NAME              BRANCH  PROVIDER  MODEL                    STATUS  OWNER   PAGES  GEN ID
for-testing-only  main    cursor    gpt-5.4-xhigh-fast      ready   alice   12     a1b2c3d4-...
my-app            main    claude    claude-sonnet-4-0        ready   alice   8      e5f6a7b8-...
my-app            dev     gemini    gemini-2.5-pro           error   alice   0      c9d0e1f2-...
```

## Understanding Projects and Variants

A **project** corresponds to a Git repository (e.g., `my-app`). Each project can have multiple **variants** — one for every combination of:

- **Branch** (e.g., `main`, `dev`, `release-1.x`)
- **AI provider** (e.g., `claude`, `gemini`, `cursor`)
- **AI model** (e.g., `gpt-5.4-xhigh-fast`, `claude-sonnet-4-0`)

Each variant is identified by a unique **Generation ID** (UUID), which you can use interchangeably with the project name in most CLI commands.

## Listing Projects

### CLI

```
docsfy list
```

Filter by status or provider:

```
docsfy list --status ready
docsfy list --provider claude
```

Get machine-readable output:

```
docsfy list --json
```

### Web Dashboard

Log in to the dashboard. The project tree on the left groups variants by repository, then by branch. Click any variant to see its details in the right panel.

## Inspecting a Project

### CLI

View all variants of a project:

```
docsfy status my-app
```

Inspect a specific variant by providing branch, provider, and model:

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

You can also look up a variant by its Generation ID:

```
docsfy status a1b2c3d4-5678-9abc-def0-123456789abc
```

The output shows key details about each variant:

```
Project: my-app
Variants: 2

  main/cursor/gpt-5.4-xhigh-fast
    ID:      a1b2c3d4-5678-9abc-def0-123456789abc
    Status:  ready
    Owner:   alice
    Pages:   12
    Updated: 2026-06-07 14:30:00
    Commit:  abc1234d

  dev/gemini/gemini-2.5-pro
    ID:      e5f6a7b8-9012-3456-7890-abcdef012345
    Status:  error
    Owner:   alice
    Error:   AI provider timeout after 120s
```

Use `--json` for structured output suitable for scripts:

```
docsfy status my-app --json
```

### Web Dashboard

Click any variant in the project tree. The detail panel shows status, page count, commit SHA, generation cost, repo type, and a link to the source repository.

## Downloading Documentation

Download the generated static site as a `.tar.gz` archive.

### CLI

Download the latest ready variant:

```
docsfy download my-app
```

Download a specific variant:

```
docsfy download my-app --branch main --provider cursor --model gpt-5.4-xhigh-fast
```

Extract directly to a directory instead of saving the archive:

```
docsfy download my-app --output ./docs-site
```

Use `--flatten` to strip the top-level directory wrapper from the archive:

```
docsfy download my-app --output ./docs-site --flatten
```

You can also download by Generation ID:

```
docsfy download a1b2c3d4-5678-9abc-def0-123456789abc --output ./docs-site
```

### Web Dashboard

On the variant detail panel for any **Ready** variant, click the **Download** button to save the `.tar.gz` archive through your browser.

> **Tip:** For recipes on hosting downloaded sites with common static-file servers, see [Common Workflow Recipes](recipes-common-workflows.html).

## Aborting a Generation

Stop an in-progress generation when you realize you submitted the wrong branch or want to free up resources.

### CLI

Abort by project name (works when only one variant is actively generating):

```
docsfy abort my-app
```

Abort a specific variant:

```
docsfy abort my-app --branch dev --provider gemini --model gemini-2.5-pro
```

Abort by Generation ID:

```
docsfy abort a1b2c3d4-5678-9abc-def0-123456789abc
```

The variant status changes to **aborted** and the error message will read "Generation aborted by user."

### Web Dashboard

While a variant is generating, the detail panel shows an **Abort Generation** button. Click it and confirm the prompt. The progress bar stops and the status switches to **Aborted**.

> **Note:** Aborted variants keep any partially generated pages in cache. Regenerating the same variant will attempt an incremental update from where it left off — use **Force full regeneration** to start clean.

## Deleting Projects and Variants

### Deleting a Single Variant

Remove one specific branch/provider/model combination and all its generated files.

**CLI:**

```
docsfy delete my-app --branch main --provider cursor --model gpt-5.4-xhigh-fast
```

Add `--yes` to skip the confirmation prompt (useful in scripts):

```
docsfy delete my-app --branch main --provider cursor --model gpt-5.4-xhigh-fast --yes
```

**Web dashboard:** Click the **Delete** button on the variant detail panel and confirm.

### Deleting All Variants

Remove every variant of a project in one operation.

**CLI:**

```
docsfy delete my-app --all
```

**Web dashboard:** Right-click (or use the menu on) a project in the tree and choose **Delete All Variants**.

> **Warning:** Deletion is permanent. All generated pages, cached content, and metadata for the affected variant(s) are removed from the server.

### Deleting by Generation ID

You can pass a Generation ID instead of a project name. When combined with `--all`, it resolves the UUID to a project name first, then deletes all variants:

```
docsfy delete a1b2c3d4-5678-9abc-def0-123456789abc --all
```

Without `--all`, the UUID resolves to a specific variant's branch, provider, and model:

```
docsfy delete a1b2c3d4-5678-9abc-def0-123456789abc
```

## Variant Statuses

Every variant has one of four statuses:

| Status | Meaning |
|---|---|
| **ready** | Documentation generated successfully and is viewable/downloadable |
| **generating** | Generation is in progress — pages are being planned and written |
| **error** | Generation failed (check the error message for details) |
| **aborted** | Generation was manually cancelled by a user |

## Advanced Usage

### Filtering and Scripting with JSON Output

All listing and status commands support `--json` for programmatic use:

```bash
# Get all ready projects as JSON
docsfy list --status ready --json

# Extract page counts with jq
docsfy list --json | jq '.[] | {name, branch, page_count}'

# Check if a specific variant is ready
docsfy status my-app --branch main --provider cursor --model gpt-5.4-xhigh-fast --json \
  | jq -r '.status'
```

### Admin Operations

Admins see **all** projects across all users. When multiple users own variants of the same project name, use `--owner` to disambiguate:

```
docsfy status my-app --owner alice
docsfy delete my-app --branch main --provider cursor --model gpt-5.4-xhigh-fast --owner alice
docsfy abort my-app --owner bob
```

> **Note:** Admin deletion requires `--owner` (or `?owner=` in the API). This prevents accidental deletion of the wrong user's project.

### Active Generation Conflicts

You cannot delete a variant while it is actively generating. Abort it first:

```
docsfy abort my-app --branch dev --provider gemini --model gemini-2.5-pro
docsfy delete my-app --branch dev --provider gemini --model gemini-2.5-pro --yes
```

Similarly, you cannot start a new generation for a variant that is already generating — the server returns a `409 Conflict`.

### Viewing Documentation Without Downloading

You can browse generated docs directly in your browser at:

```
https://<your-server>/docs/<project>/<branch>/<provider>/<model>/
```

Or let the server serve the latest ready variant automatically:

```
https://<your-server>/docs/<project>/
```

See [Browsing Generated Documentation](browsing-docs.html) for details on search, variant switching, and sharing URLs.

### Regenerating a Variant

From the web dashboard, every ready, errored, or aborted variant shows a **Regenerate** section where you can change the provider, model, or toggle **Force full regeneration** before starting a new run.

From the CLI, use the `generate` command again with the same repo URL and variant parameters. See [Generating Documentation](generating-docs.html) for full details.

## Troubleshooting

**"No active generation for '...'" when aborting**
The generation already finished (or failed) before your abort request arrived. Run `docsfy status <project>` to check the current state.

**"Cannot delete '...' while generation is in progress"**
You must abort the active generation before deleting. Run `docsfy abort <project>` first.

**"Multiple owners found for this variant, please specify owner"**
This happens when an admin queries a project name that exists under multiple owners. Add `--owner <username>` to resolve the ambiguity.

**"Variant not ready" when downloading**
Only variants with status **ready** can be downloaded. Check `docsfy status <project>` and wait for generation to complete, or regenerate if the variant errored.

## Related Pages

- [Generating Documentation](generating-docs.html)
- [Browsing Generated Documentation](browsing-docs.html)
- [Using the CLI](using-the-cli.html)
- [CLI Command Reference](cli-reference.html)
- [Common Workflow Recipes](recipes-common-workflows.html)

---

Source: browsing-docs.md

# Browsing Generated Documentation

You want to read, search, and share the documentation that docsfy generates from your repositories. This guide walks you through the doc site's interface — navigation, search, theme switching, URL patterns, and how to share specific pages with teammates.

## Prerequisites

- A docsfy project with status **Ready** (see [Generating Documentation](generating-docs.html))
- Access to the docsfy server in your browser

## Opening Your Documentation

The fastest way to open a generated doc site is from the dashboard:

1. Log in to the docsfy dashboard
2. Select a project and variant from the sidebar tree
3. Click **View Documentation** — the doc site opens in a new tab

Alternatively, navigate directly by URL:

```
https://your-server/docs/my-project/main/cursor/gpt-5.4-xhigh-fast/
```

The URL pattern is:

```
/docs/{project-name}/{branch}/{provider}/{model}/
```

If you only know the project name, use the short URL to load the latest variant automatically:

```
https://your-server/docs/my-project/index.html
```

> **Tip:** The short URL (`/docs/{project-name}/`) always serves the most recently generated variant. Use the full URL when you need a specific branch or AI provider.

## Navigating the Doc Site

### Sidebar

The left sidebar contains the project name, a tagline, a search box, and a grouped navigation menu. Pages are organized into sections (e.g., "Getting Started", "Guides", "Reference"). The currently active page is highlighted.

On mobile screens, the sidebar is hidden. Tap the **hamburger menu** (☰) in the top bar to open it. Tap the overlay or a link to close it.

### Page Navigation

At the bottom of every page, **Previous** and **Next** links let you step through the documentation in order. Use these to read docs sequentially without returning to the sidebar.

### On This Page (Table of Contents)

On wide screens (1280px+), a right-hand sidebar shows an "On this page" panel that lists section headings. Click any heading to jump to that section. The panel highlights your current position as you scroll.

### Top Bar

The top bar includes:

- **Project name** — links back to the doc site home page
- **Search button** — opens the search modal (see below)
- **GitHub link** — links to the source repository with a live star count
- **Theme toggle** — switches between light and dark mode
- **docsfy badge** — links to the docsfy project

## Searching Documentation

Open the search modal using any of these methods:

- Press **⌘K** (Mac) or **Ctrl+K** (Windows/Linux)
- Click the **⌘K Search** button in the top bar
- Click the search box in the sidebar

Then type your query. Results appear instantly, showing matching page titles and a content preview with the match highlighted in context. The search covers all page titles and content.

Navigate results with:

| Key | Action |
|---|---|
| **↑ / ↓** | Move between results |
| **Enter** | Open the selected result |
| **Esc** | Close the search modal |

> **Note:** Search is entirely client-side — it loads a pre-built index when the page opens, so results appear with zero network latency.

## Switching Themes

Click the **sun/moon toggle** in the top-right corner to switch between light and dark mode. Your preference is saved in the browser and persists across sessions and pages.

The doc site defaults to light mode. If you've previously set a theme in the docsfy dashboard, the doc site will respect that same choice — both share the same saved preference.

## Copying Code Snippets

Hover over any code block to reveal a **Copy** button in the top-right corner. Click it to copy the code to your clipboard. The button briefly shows "Copied!" to confirm.

On touch devices, the Copy button is always visible without hovering.

## Sharing Doc URLs

Every doc page has a stable, shareable URL. To share a specific page with your team, copy the URL from your browser's address bar:

```
https://your-server/docs/my-project/main/cursor/gpt-5.4-xhigh-fast/quickstart.html
```

The URL components break down as:

| Segment | Description |
|---|---|
| `my-project` | Project name |
| `main` | Git branch |
| `cursor` | AI provider used |
| `gpt-5.4-xhigh-fast` | AI model used |
| `quickstart.html` | Page slug |

> **Tip:** To link to a specific section within a page, click the section heading and copy the updated URL with the anchor fragment (e.g., `quickstart.html#installation`).

## Switching Between Variants

A "variant" is a unique combination of branch, AI provider, and model. The same repository can have multiple variants — for example, docs generated from the `main` branch with one model and from the `dev` branch with another.

To switch between variants:

1. Return to the docsfy dashboard
2. Expand the project in the sidebar tree
3. Select the variant you want (grouped by branch)
4. Click **View Documentation**

Each variant has its own URL path, so you can bookmark different variants independently.

## Downloading Documentation

To download a complete doc site as an archive:

- **From the dashboard:** Select a variant and click **Download** to get a `.tar.gz` file
- **By URL:** Navigate to the download endpoint directly:

```
https://your-server/api/projects/my-project/main/cursor/gpt-5.4-xhigh-fast/download
```

The downloaded archive contains all HTML, CSS, JavaScript, and search index files — ready to host as a static site anywhere. See [Managing Projects and Variants](managing-projects.html) for more download options including the CLI.

## AI-Friendly Documentation Files

Every generated doc site includes machine-readable files for AI tools:

| File | Purpose |
|---|---|
| `llms.txt` | Structured index listing all pages with titles, slugs, and descriptions |
| `llms-full.txt` | Complete documentation content concatenated into a single file |

Access these from:

- The **footer** of every page (links to both files)
- The **landing page** banner labeled "AI-friendly documentation"
- Direct URL: `https://your-server/docs/my-project/main/cursor/gpt-5.4-xhigh-fast/llms.txt`

Feed these files to AI assistants, chatbots, or code editors that support the `llms.txt` convention.

## Advanced Usage

### Printing Documentation

The doc site includes print-optimized styles. When you print a page (or save as PDF), the sidebar, top bar, and navigation controls are automatically hidden, leaving clean article content that fills the page width.

### Mobile Browsing

The doc site is fully responsive:

- **Sidebar** collapses into a slide-out drawer, activated by the hamburger menu
- **Table of contents** panel hides on screens narrower than 1280px
- **Code blocks** scroll horizontally if they exceed the viewport width
- **Card grid** on the landing page stacks into a single column

### Accessing Raw Markdown

Every page is also available as raw Markdown by changing the `.html` extension to `.md`:

```
https://your-server/docs/my-project/main/cursor/gpt-5.4-xhigh-fast/quickstart.md
```

This is useful for importing content into other tools or for quick reference without rendering.

## Troubleshooting

**Search returns no results**
The search index loads asynchronously when the page opens. If you search immediately after the page loads on a slow connection, the index may not be ready yet. Wait a moment and try again.

**"File not found" when opening a doc URL**
The variant may have been deleted or regenerated with a different provider/model. Use the short URL (`/docs/{project-name}/`) to load the latest available variant, or check the dashboard for current variants.

**Theme resets on a different browser**
Theme preference is stored in your browser's `localStorage`. Each browser maintains its own setting independently.

**Sidebar scroll position resets**
The sidebar remembers its scroll position within a session. If you open docs in a new tab or window, the sidebar scrolls the active page link into view automatically.

## Related Pages

- [Generating Documentation](generating-docs.html)
- [Managing Projects and Variants](managing-projects.html)
- [AI-Readable Documentation with llms.txt](llms-txt-ai-readable-docs.html)
- [Getting Started with docsfy](quickstart.html)
- [Common Workflow Recipes](recipes-common-workflows.html)

---

Source: managing-users.md

# Managing Users and Access Control

Set up user accounts, control who can access your documentation projects, and keep credentials secure by managing roles, project-level permissions, and API key rotation.

## Prerequisites

- A running docsfy server (see [Getting Started with docsfy](quickstart.html))
- The `ADMIN_KEY` environment variable set on the server (at least 16 characters)
- Admin access — either via the `ADMIN_KEY` or a user account with the `admin` role

## Quick Example

Create a user with the CLI in one command:

```bash
docsfy admin users create alice --role user
```

Output:

```
User created: alice
Role: user
API Key: docsfy_aBcDeFgHiJkLmNoPqRsTuVwXyZ...

Save this API key -- it will not be shown again.
```

## Understanding Roles

docsfy has three roles with different permission levels:

| Role | Generate docs | View docs | Manage users | Manage access |
|------|:---:|:---:|:---:|:---:|
| **admin** | ✅ | ✅ | ✅ | ✅ |
| **user** | ✅ | ✅ | ❌ | ❌ |
| **viewer** | ❌ | ✅ | ❌ | ❌ |

- **admin** — Full access including creating/deleting users, granting project access, and rotating API keys.
- **user** — Can generate, view, delete, and download documentation for their own projects. Cannot perform admin operations.
- **viewer** — Read-only access. Can browse documentation shared with them but cannot generate or modify anything.

## Creating Users

### From the Web Dashboard

1. Log in as an admin.
2. Navigate to the **Users** tab in the admin panel.
3. Enter a username, select a role from the dropdown, and click **Create User**.
4. Copy the generated API key immediately — it is shown only once.

### From the CLI

```bash
docsfy admin users create bob --role viewer
```

The `--role` flag accepts `user`, `viewer`, or `admin`. If omitted, it defaults to `user`.

> **Warning:** The API key is displayed only once at creation time. Store it securely — there is no way to retrieve it later. If lost, you must rotate the key.

### Username Rules

- Must be 2–50 characters long
- Must start with a letter or number
- May contain letters, numbers, dots (`.`), hyphens (`-`), and underscores (`_`)
- The username `admin` is reserved and cannot be used

## Listing Users

### CLI

```bash
docsfy admin users list
```

```
USERNAME    ROLE     CREATED
alice       user     2026-06-01 10:30:00
bob         viewer   2026-06-02 14:15:00
carol       admin    2026-06-03 09:00:00
```

Add `--json` for machine-readable output:

```bash
docsfy admin users list --json
```

### Web Dashboard

The **Users** tab shows all users in a table with their username, role, and creation date.

## Granting Project Access

By default, each user can only see projects they own. Admins can share a project with other users by granting access.

### From the Web Dashboard

1. Go to the **Access** tab in the admin panel.
2. Fill in the **Project Name**, **Username**, and **Owner** fields.
3. Click **Grant Access**.

### From the CLI

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

This gives `bob` read access to all variants (branches, providers, models) of the project `my-repo` owned by `alice`.

> **Note:** Both the user and the project must already exist. The grant command validates this and returns an error if either is not found.

## Revoking Project Access

### From the Web Dashboard

1. Go to the **Access** tab.
2. Under **Lookup Access**, enter the project name and owner, then click **List Access**.
3. Click **Revoke** next to the user you want to remove.

### From the CLI

```bash
docsfy admin access revoke my-repo --username bob --owner alice
```

## Listing Project Access

See who has access to a specific project:

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

```
Project: my-repo
Owner: alice
Users with access: bob, carol
```

Add `--json` for machine-readable output.

## Rotating API Keys

If a key is compromised or a user loses their credentials, rotate their API key. This immediately invalidates the old key and all active sessions for that user.

### Admin Rotating Another User's Key

**CLI:**

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

```
User: bob
New API Key: docsfy_xYzAbCdEfGhIjKlMnOpQrStUvWxYz...

Save this API key -- it will not be shown again.
```

**Web dashboard:** Click **Change Password** next to the user in the **Users** tab. You can optionally provide a custom key or leave the field empty to auto-generate one.

### Users Rotating Their Own Key

Non-admin users can rotate their own key through the API. This clears their current session — they must log in again with the new key.

> **Warning:** The built-in `admin` account (authenticated via `ADMIN_KEY`) cannot rotate keys through the API. Change the `ADMIN_KEY` environment variable and restart the server instead.

### Custom API Keys

You can supply a custom key instead of auto-generating one:

```bash
docsfy admin users rotate-key bob --new-key "my-custom-secure-key-here"
```

Custom keys must be at least 16 characters long.

## Deleting Users

### From the CLI

```bash
docsfy admin users delete carol
```

You will be prompted for confirmation. Add `--yes` to skip:

```bash
docsfy admin users delete carol --yes
```

### From the Web Dashboard

Click **Delete** next to the user in the **Users** tab and confirm the action.

### What Happens When a User Is Deleted

- All active sessions for the user are invalidated immediately
- All projects owned by the user are deleted from the database
- All project files on disk belonging to the user are removed
- All access grants (both granted to and granted by the user) are cleaned up
- A user cannot be deleted while one of their projects has a generation in progress

> **Warning:** User deletion is permanent and cannot be undone. All of their projects and generated documentation will be lost.

## The Admin Account

docsfy has a special built-in admin identity that authenticates with the `ADMIN_KEY` environment variable:

- **Username:** `admin`
- **Credentials:** The value of the `ADMIN_KEY` environment variable
- This account always has full admin privileges
- It cannot be deleted or have its key rotated through the API — change the `ADMIN_KEY` env var and restart the server

You can also create additional users with the `admin` role. These behave identically to the built-in admin for authorization purposes, but their keys are managed through the normal user management workflow.

> **Warning:** Changing the `ADMIN_KEY` environment variable invalidates **all** existing user API key hashes (since `ADMIN_KEY` is used as the HMAC secret for key hashing). After rotating `ADMIN_KEY`, you must rotate every user's API key as well.

## Advanced Usage

### Sessions and Timeouts

- Browser sessions last 8 hours and are stored as HTTP-only cookies
- Rotating a user's API key invalidates all their active sessions
- Expired sessions are cleaned up automatically when the server starts

### Connecting the CLI to a Server

Before running admin commands, configure the CLI with a server profile:

```bash
docsfy config init
```

You'll be prompted for a profile name, server URL, username, and password (API key). See [Using the CLI](using-the-cli.html) for complete setup instructions.

You can also pass credentials directly:

```bash
docsfy --host myserver.example.com --username admin --password $ADMIN_KEY admin users list
```

### JSON Output

All CLI admin commands support `--json` for scripting:

```bash
docsfy admin users create ci-bot --role user --json
```

```json
{
  "username": "ci-bot",
  "api_key": "docsfy_aBcDeFgHiJkLmNoPqRsTuVwXyZ...",
  "role": "user"
}
```

### Access Control Model

docsfy uses an ownership-based access model:

- Every project is owned by the user who generated it
- Owners always have full access to their own projects
- Admins can see and manage all projects regardless of ownership
- Access grants are scoped by **project name + project owner** — granting access gives the user visibility into all variants (branches, AI providers, models) of that project
- When all variants of a project are deleted, associated access grants are automatically cleaned up

## Troubleshooting

**"Username 'admin' is reserved"** — You cannot create a user with the username `admin`. Choose a different name and assign the `admin` role instead.

**"Cannot delete your own account"** — An admin cannot delete the account they are currently logged in with. Have another admin delete it, or use a different admin account.

**"Cannot delete user while generation is in progress"** — Wait for any active documentation generation jobs owned by that user to complete, or abort them first. See [Managing Projects and Variants](managing-projects.html) for how to abort a generation.

**"ADMIN_KEY users cannot rotate keys"** — If you are logged in using the `ADMIN_KEY` environment variable (as the built-in `admin` account), you cannot rotate your key through the API. Update the `ADMIN_KEY` environment variable on the server and restart it.

**"API key must be at least 16 characters long"** — Custom API keys provided via `--new-key` must be at least 16 characters. Use a longer key or omit the flag to auto-generate one.

## Related Pages

- [Getting Started with docsfy](quickstart.html)
- [Using the CLI](using-the-cli.html)
- [CLI Command Reference](cli-reference.html)
- [REST API Reference](api-reference.html)
- [Managing Projects and Variants](managing-projects.html)

---

Source: configuring-ai-providers.md

# Configuring AI Providers

Choose which AI provider and model generates your documentation, tune timeouts, and verify that the Pi SDK sidecar — the service that routes all AI calls — is running correctly.

## Prerequisites

- A running docsfy server (see [Deploying with Docker](deployment.html))
- The Pi SDK sidecar started automatically (bundled in the Docker image)

## Quick Example

Set your default provider and model in the `.env` file:

```bash
AI_PROVIDER=claude
AI_MODEL=claude-sonnet-4-20250514
AI_CLI_TIMEOUT=90
```

Restart the server, and every new generation request will use Claude by default.

## Available Providers

docsfy supports three AI providers:

| Provider | Description |
|----------|-------------|
| `claude` | Anthropic's Claude models |
| `gemini` | Google's Gemini models |
| `cursor` | Cursor agent models (default) |

## Setting Server Defaults

The server-wide default provider and model are configured through environment variables. These apply when a user does not specify a provider or model in their generation request.

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

| Variable | Default | Description |
|----------|---------|-------------|
| `AI_PROVIDER` | `cursor` | Default AI provider for new generations |
| `AI_MODEL` | `gpt-5.4-xhigh-fast` | Default model within the chosen provider |
| `AI_CLI_TIMEOUT` | `60` | Seconds before an individual AI call times out |

> **Note:** Environment variables override any config file values. See [Configuration Reference](configuration-reference.html) for the complete list of settings.

## Choosing a Provider Per Generation

You can override the server default on each generation request — from the dashboard or the CLI.

### From the Web Dashboard

1. Open the dashboard and click **New Generation**.
2. Select a provider from the **Provider** dropdown (`claude`, `gemini`, or `cursor`).
3. Choose a model from the **Model** dropdown, which auto-populates with models available for the selected provider.
4. Click **Generate**.

> **Tip:** The model list is discovered automatically from the sidecar. If the dropdown is empty, the sidecar may not be running — see [Troubleshooting](#troubleshooting) below.

### From the CLI

```bash
docsfy generate https://github.com/org/repo \
  --provider claude \
  --model claude-sonnet-4-20250514 \
  --branch main
```

When `--provider` or `--model` are omitted, the server defaults apply.

## Listing Available Models

See which providers and models the server currently supports.

### CLI

```bash
docsfy models
```

Sample output:

```
Provider: claude
  claude-sonnet-4-20250514
  claude-opus-4-20250514

Provider: gemini
  gemini-2.5-pro

Provider: cursor (default)
  gpt-5.4-xhigh-fast  (default)
```

Filter to a single provider:

```bash
docsfy models --provider gemini
```

Get machine-readable output:

```bash
docsfy models --json
```

For full CLI flag details, see [CLI Command Reference](cli-reference.html).

## How the Pi SDK Sidecar Works

All AI calls from docsfy are routed through the **Pi SDK sidecar**, a lightweight Node.js service that runs alongside the main server. You do not call AI providers directly — the sidecar handles provider routing, authentication, and model discovery.

The flow for every generation:

1. docsfy server receives a generate request with a provider and model.
2. The server sends AI prompts to the sidecar over HTTP on `localhost`.
3. The sidecar routes the call to the correct provider (Claude, Gemini, or Cursor).
4. Responses flow back through the sidecar to the server.

> **Note:** The sidecar starts automatically when using Docker. The entrypoint script launches it in the background, waits for it to be healthy, and then starts the docsfy server. If the sidecar dies, the container shuts down.

### Sidecar Port

The sidecar listens on port **9100** by default. Override it with the `SIDECAR_PORT` environment variable:

```bash
SIDECAR_PORT=9200
```

### Health Check

Both the docsfy server and sidecar have health endpoints. The Docker health check verifies both:

```bash
# Check the main server
curl http://localhost:8000/health

# Check the sidecar
curl http://localhost:9100/health
```

## Advanced Usage

### Parallel Page Generation

docsfy generates multiple documentation pages in parallel. Control the concurrency level with:

```bash
MAX_CONCURRENT_PAGES=10
```

This limits how many simultaneous AI calls run during page generation and validation. Lower this if you hit provider rate limits; raise it for faster generation on providers with high rate limits.

### Multiple Variants

Each combination of **project + branch + provider + model** creates a separate documentation variant. You can generate docs for the same repository using different providers to compare output quality:

```bash
docsfy generate https://github.com/org/repo --provider claude --model claude-sonnet-4-20250514
docsfy generate https://github.com/org/repo --provider gemini --model gemini-2.5-pro
```

Both variants are stored independently and accessible at their own URLs:

```
/docs/{project}/{branch}/{provider}/{model}/
```

See [Managing Projects and Variants](managing-projects.html) for listing, inspecting, and deleting variants.

### Switching Providers for an Existing Project

To regenerate an existing project's docs with a different provider, simply submit a new generation with the desired provider and model. The new variant is stored alongside the old one — nothing is overwritten.

To force a complete regeneration with the same provider (ignoring incremental update logic), add the `--force` flag:

```bash
docsfy generate https://github.com/org/repo --provider cursor --force
```

See [Working with Incremental Updates](incremental-updates.html) for details on when incremental vs. full regeneration applies.

## Troubleshooting

**Model dropdown is empty in the dashboard**

The sidecar may not be running or reachable. Check its health:

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

If the sidecar is down, restart the container. The entrypoint script automatically starts it.

**"Sidecar not available" error during generation**

docsfy checks sidecar availability before starting each generation. If the sidecar is unreachable, the generation fails immediately with an error status. Verify the sidecar is healthy and that `SIDECAR_PORT` matches in both the server and sidecar configuration.

**AI calls timing out**

Increase the timeout:

```bash
AI_CLI_TIMEOUT=120
```

This sets the per-call timeout in seconds. Complex codebases may need longer timeouts for planning and page generation steps.

**Generation is slow**

- Increase `MAX_CONCURRENT_PAGES` to allow more parallel AI calls (default: `10`).
- Check whether your provider has rate limits that are being hit.
- Consider switching to a faster model for large repositories.

## Related Pages

- [Deploying with Docker](deployment.html)
- [Configuration Reference](configuration-reference.html)
- [Generating Documentation](generating-docs.html)
- [Working with Incremental Updates](incremental-updates.html)
- [Managing Projects and Variants](managing-projects.html)

---

Source: incremental-updates.md

# Working with Incremental Updates

Keep your documentation in sync with your codebase without regenerating every page from scratch. When you re-run generation on a repository that already has docs, docsfy automatically detects what changed and updates only the affected pages — saving time and AI costs.

## Prerequisites

- A completed documentation generation for your repository (status: **ready**)
- New commits pushed to the branch you originally generated docs from

## Quick Example

Simply regenerate the same repository without the **Force** checkbox:

```bash
docsfy generate https://github.com/your-org/your-repo --branch main
```

docsfy compares the current commit against the last-generated commit, identifies which files changed, and regenerates only the documentation pages affected by those changes.

## How Incremental Updates Work

When you trigger a generation for a repository that already has documentation, docsfy follows this process:

1. **Clone and compare** — Clones the repository and checks the current commit SHA against the one stored from the last generation
2. **Skip if unchanged** — If the commit SHA matches, docsfy reports "Documentation is already up to date" and finishes immediately
3. **Compute the diff** — If commits differ, docsfy runs a `git diff` between the old and new commits to find changed files
4. **Incremental planning** — The AI reviews the list of changed files against the existing documentation plan and decides which pages need updates
5. **Selective regeneration** — Only the affected pages are regenerated, using the diff content to make targeted edits
6. **Post-processing** — Validation, cross-linking, and rendering run as usual on the final page set

> **Note:** If the diff cannot be computed (e.g., the old commit is unreachable), docsfy falls back to a full regeneration automatically. You never need to intervene.

## Triggering an Incremental Update

### From the Dashboard

1. Open the dashboard and select the variant you want to update in the sidebar
2. In the detail panel, find the **Regenerate** section
3. Leave the **Force full regeneration** checkbox **unchecked**
4. Click **Regenerate**

The activity log will show an `incremental_planning` stage instead of the full `planning` stage, confirming the incremental path is active.

### From the CLI

```bash
docsfy generate https://github.com/your-org/your-repo --branch main --watch
```

The `--watch` flag streams progress to your terminal so you can see each stage as it happens. You'll see output like:

```
[generating] incremental_planning
[generating] generating_pages (3 pages)
[generating] validating
[generating] cross_linking
[generating] rendering
Generation complete! (12 pages)
```

> **Tip:** Use `--watch` to confirm docsfy is using the incremental path. If you see `planning` instead of `incremental_planning`, the full planner ran — check the troubleshooting section below.

## How Pages Are Updated

When the incremental planner identifies pages that need changes, docsfy doesn't simply regenerate those pages from scratch. Instead, it uses a targeted patch approach:

1. The AI reads the existing page content and the relevant portions of the diff
2. It produces a set of **find-and-replace edits** — each specifying an exact block of existing text (`old_text`) and its replacement (`new_text`)
3. docsfy applies these edits to the existing page, preserving all untouched sections

This means your documentation maintains consistent style and structure across updates, with only the affected sections modified.

> **Note:** If the patch-based approach fails for a specific page (e.g., the AI can't locate the text to replace), docsfy automatically falls back to regenerating that page fully. Other pages in the same run are unaffected.

## Generation Stages

During an incremental update, you'll see these stages in the dashboard or CLI:

| Stage | Description |
|---|---|
| `cloning` | Cloning the repository and fetching the old commit for diffing |
| `analyzing` | Building a code knowledge graph for AI context |
| `incremental_planning` | AI determines which pages need updates based on changed files |
| `generating_pages` | Regenerating only the affected pages |
| `validating` | Checking for stale references in updated pages |
| `completeness_check` | Verifying no major new features are undocumented |
| `cross_linking` | Updating cross-references between pages |
| `rendering` | Building the final HTML documentation site |

Compare this to a full generation, which shows `planning` instead of `incremental_planning` and regenerates all pages.

## Forcing a Full Regeneration

Sometimes you want to regenerate everything from scratch — for example, after upgrading your AI provider or when you want a fresh documentation structure.

### From the Dashboard

Check the **Force full regeneration** checkbox before clicking Regenerate.

### From the CLI

```bash
docsfy generate https://github.com/your-org/your-repo --branch main --force
```

The `--force` flag:

- Clears all cached pages for the variant
- Runs the full AI planner to create a new documentation plan
- Regenerates every page from scratch
- Resets the page count to 0 during generation

> **Warning:** Force regeneration is significantly more expensive than an incremental update since it regenerates all pages. Use it only when you need a complete refresh.

## Advanced Usage

### Cross-Provider Updates

When you switch AI providers or models (e.g., from `claude` to `gemini`), docsfy reuses existing documentation content from the previous provider as a starting point:

1. The system finds the most recent ready variant for the same project and branch
2. It copies the cached page content from the existing variant to the new one
3. If the commit SHA is the same, the documentation is marked as up to date immediately
4. If commits differ, it runs the normal incremental update flow using the copied content

This means switching providers doesn't require a full regeneration — you get the benefit of incremental updates even across provider changes.

### Multi-Branch Incremental Updates

Each branch maintains its own commit tracking independently. Generating docs for the `dev` branch doesn't affect the `main` branch's incremental state:

```bash
# These track changes independently
docsfy generate https://github.com/your-org/your-repo --branch main
docsfy generate https://github.com/your-org/your-repo --branch dev
```

See [Generating Documentation](generating-docs.html) for more on branch-based generation.

### Diff Size Limits

Large diffs (over 30,000 characters) are automatically truncated before being sent to the AI. When this happens:

- The AI only considers the visible portion of the diff
- Pages affected by truncated changes may not be updated
- A force regeneration will capture all changes

If you've accumulated many commits since the last generation, consider using `--force` to ensure nothing is missed.

## Troubleshooting

**docsfy runs a full regeneration instead of incremental**

This happens when:
- No previous generation exists for this variant (first run)
- The previous generation didn't complete successfully (no stored commit SHA)
- The old commit can't be fetched for diffing (e.g., force-pushed history)
- The existing documentation plan can't be parsed

In all cases, docsfy falls back gracefully to a full generation. No action needed.

**Incremental update missed some changes**

If a page wasn't updated when it should have been:
- The incremental planner may not have associated the changed files with that page
- The diff may have been truncated (see Diff Size Limits above)
- Run `--force` to regenerate everything

**"Documentation is already up to date" but content seems stale**

This means the commit SHA matches the last generation. The repository hasn't been updated since docs were last generated. Push new commits and try again, or use `--force` to regenerate from the same commit.

See [Managing Projects and Variants](managing-projects.html) for how to inspect variant details including the last commit SHA.

## Related Pages

- [Generating Documentation](generating-docs.html)
- [Configuring AI Providers](configuring-ai-providers.html)
- [Managing Projects and Variants](managing-projects.html)
- [Common Workflow Recipes](recipes-common-workflows.html)
- [Configuration Reference](configuration-reference.html)

---

Source: using-the-cli.md

# Using the CLI

Manage your docsfy server entirely from the terminal — set up server profiles, generate documentation, monitor progress, download results, and administer users without ever opening a browser.

## Prerequisites

- docsfy installed (`pip install docsfy` or `uv pip install docsfy`)
- A running docsfy server you can reach over the network
- Your username and API key (get these from your server admin)

## Quick Example

Set up a server profile and generate docs in three commands:

```shell
docsfy config init
docsfy generate https://github.com/your-org/your-repo
docsfy download your-repo -o ./docs
```

## Setting Up Server Profiles

Before using any command, configure a connection to your docsfy server. The interactive setup walks you through it:

```shell
docsfy config init
```

You'll be prompted for:

- **Profile name** — a short label like `dev` or `prod` (default: `dev`)
- **Server URL** — full URL including scheme, e.g. `https://docsfy.example.com:8000`
- **Username** — your docsfy username
- **Password** — your API key

The first profile you create automatically becomes the default. Configuration is saved to `~/.config/docsfy/config.toml` with restricted file permissions (owner read/write only).

### Viewing Your Configuration

```shell
docsfy config show
```

```
Config file: /home/you/.config/docsfy/config.toml
Default server: dev

[dev] (default)
  URL:      https://docsfy.example.com:8000
  Username: alice
  Password: dk***
```

Passwords are always masked in output.

### Updating Individual Settings

Change any config value without re-running the full setup:

```shell
docsfy config set servers.dev.url https://new-server.example.com:8000
docsfy config set servers.dev.username bob
docsfy config set default.server prod
```

Keys must start with `default.` or `servers.`.

### Multiple Server Profiles

Run `docsfy config init` again to create additional profiles. Switch between them per-command with `--server`:

```shell
docsfy list --server prod
docsfy generate https://github.com/org/repo --server staging
```

Or change your default:

```shell
docsfy config set default.server prod
```

## Generating Documentation

```shell
docsfy generate https://github.com/your-org/your-repo
```

This submits the repository to the server and starts AI-powered documentation generation. The output confirms the request:

```
Project: your-repo
Branch: main
Status: generating
Generation ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
```

The server uses its default AI provider and model. To choose a specific provider and model:

```shell
docsfy generate https://github.com/your-org/your-repo --provider gemini --model gemini-2.5-flash
```

### Targeting a Branch

```shell
docsfy generate https://github.com/your-org/your-repo --branch dev
```

> **Note:** Branch names cannot contain slashes. Use hyphens instead (e.g., `release-1.x` instead of `release/1.x`).

### Specifying Repository Type

docsfy auto-detects whether your repo is an app, library, framework, or test suite. Override the detection if needed:

```shell
docsfy generate https://github.com/your-org/your-repo --repo-type library
```

Valid types: `app`, `library`, `framework`, `tests`.

### Forcing Full Regeneration

By default, docsfy performs incremental updates when docs already exist for a repo. Force a complete regeneration with:

```shell
docsfy generate https://github.com/your-org/your-repo --force
```

See [Working with Incremental Updates](incremental-updates.html) for details on how incremental generation works.

### Watching Progress in Real Time

Add `--watch` to stream generation progress directly in your terminal via WebSocket:

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

```
Project: your-repo
Branch: main
Status: generating
Generation ID: a1b2c3d4-...
Watching generation progress...
[generating] cloning repository
[generating] planning documentation (12 pages)
[generating] generating pages
Generation complete! (12 pages)
```

The command exits automatically when generation finishes, errors out, or is aborted.

## Checking Project Status

### List All Projects

```shell
docsfy list
```

```
NAME          BRANCH  PROVIDER  MODEL              STATUS  OWNER  PAGES  GEN ID
your-repo     main    cursor    gpt-5.4-xhigh-fast ready   alice  12     a1b2c3d4-...
other-repo    dev     gemini    gemini-2.5-flash    ready   bob    8      e5f6a7b8-...
```

Filter by status or provider:

```shell
docsfy list --status ready
docsfy list --provider gemini
```

### Inspect a Specific Project

```shell
docsfy status your-repo
```

```
Project: your-repo
Variants: 2

  main/cursor/gpt-5.4-xhigh-fast
    ID:      a1b2c3d4-e5f6-7890-abcd-ef1234567890
    Status:  ready
    Owner:   alice
    Pages:   12
    Updated: 2026-06-08T14:30:00
    Commit:  abc12345

  dev/gemini/gemini-2.5-flash
    ID:      f9e8d7c6-b5a4-3210-fedc-ba9876543210
    Status:  generating
    Owner:   alice
    Stage:   generating pages
```

Narrow to a specific variant with `--branch`, `--provider`, and `--model`:

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

### Using Generation IDs

Every command that accepts a project name also accepts a generation ID (UUID). This is useful for scripting when you've captured the ID from a `generate` command:

```shell
docsfy status a1b2c3d4-e5f6-7890-abcd-ef1234567890
docsfy download a1b2c3d4-e5f6-7890-abcd-ef1234567890 -o ./docs
```

The CLI automatically resolves the UUID to the corresponding project, branch, provider, and model.

## Downloading Documentation

Download the generated documentation site as a tar.gz archive:

```shell
docsfy download your-repo --branch main --provider cursor --model gpt-5.4-xhigh-fast
```

```
Downloaded to /current/dir/your-repo-main-cursor-gpt-5.4-xhigh-fast-docs.tar.gz
```

### Extracting to a Directory

Use `--output` to extract directly into a folder:

```shell
docsfy download your-repo --branch main --provider cursor --model gpt-5.4-xhigh-fast -o ./docs
```

```
Extracted to ./docs
```

### Flattening the Directory Structure

Archives contain a nested folder. Use `--flatten` to move all files directly into the output directory:

```shell
docsfy download your-repo -b main -p cursor -m gpt-5.4-xhigh-fast -o ./docs --flatten
```

```
Extracted and flattened to ./docs
```

> **Note:** `--flatten` requires `--output`.

## Aborting a Generation

Stop an in-progress generation:

```shell
docsfy abort your-repo --branch main --provider cursor --model gpt-5.4-xhigh-fast
```

Or abort by project name (stops any active generation for that project):

```shell
docsfy abort your-repo
```

## Deleting Projects

### Delete a Specific Variant

```shell
docsfy delete your-repo --branch main --provider cursor --model gpt-5.4-xhigh-fast
```

You'll be asked to confirm. Skip the prompt in scripts with `--yes`:

```shell
docsfy delete your-repo -b main -p cursor -m gpt-5.4-xhigh-fast --yes
```

### Delete All Variants

```shell
docsfy delete your-repo --all
```

> **Warning:** `--all` deletes every variant across all branches, providers, and models for the named project.

## Checking Available AI Models

See which AI providers and models are available on the server:

```shell
docsfy models
```

```
Provider: claude
  claude-sonnet-4-20250514
  claude-opus-4-20250514

Provider: gemini
  gemini-2.5-flash
  gemini-2.5-pro

Provider: cursor (default)
  gpt-5.4-xhigh-fast  (default)
  gpt-5.4-xhigh
```

Filter by provider:

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

## Checking Server Health

Verify connectivity to your docsfy server:

```shell
docsfy health
```

```
Server: https://docsfy.example.com:8000
Status: ok
```

## Admin Commands

Admin commands require an account with the `admin` role. See [Managing Users and Access Control](managing-users.html) for the full workflow.

### Managing Users

```shell
# List all users
docsfy admin users list

# Create a user (role: user, viewer, or admin)
docsfy admin users create alice --role user

# Rotate a user's API key
docsfy admin users rotate-key alice

# Delete a user
docsfy admin users delete alice --yes
```

When creating a user or rotating a key, the new API key is displayed once. Save it immediately — it cannot be retrieved later.

### Managing Project Access

```shell
# List who has access to a project
docsfy admin access list my-project --owner alice

# Grant access
docsfy admin access grant my-project --username bob --owner alice

# Revoke access
docsfy admin access revoke my-project --username bob --owner alice
```

## Advanced Usage

### JSON Output

Most commands support `--json` for machine-readable output, useful in scripts and CI pipelines:

```shell
docsfy list --json
docsfy status your-repo --json
docsfy models --json
docsfy admin users list --json
```

### Overriding Connection Settings per Command

Override any profile setting for a single command without changing your config:

```shell
docsfy list --host docsfy.staging.local --port 9000 --username admin --password my-key
```

The priority order is:

1. Explicit CLI flags (`--host`, `--port`, `--username`, `--password`)
2. Named server profile (`--server prod`)
3. Default profile from config
4. Error if nothing is configured

### CI/CD Integration

In CI environments where interactive `config init` isn't practical, pass credentials directly:

```shell
docsfy generate https://github.com/org/repo \
  --host docsfy.internal.com \
  --port 8000 \
  --username "$DOCSFY_USER" \
  --password "$DOCSFY_API_KEY" \
  --branch "$CI_BRANCH" \
  --force \
  --watch
```

Or create the config file programmatically before running commands. See [Common Workflow Recipes](recipes-common-workflows.html) for complete CI/CD patterns.

### Admin Operations on Other Users' Projects

Admins can operate on any user's projects by specifying `--owner`:

```shell
docsfy status some-project --owner alice
docsfy delete some-project --all --owner alice --yes
docsfy download some-project -b main -p cursor -m gpt-5.4-xhigh-fast --owner alice -o ./docs
```

## Troubleshooting

**"No server configured" error**
Run `docsfy config init` to create your first server profile, or pass `--host` and `--password` directly.

**"Server profile 'X' not found"**
Check available profiles with `docsfy config show`. Profile names are case-sensitive.

**"Error: Server redirected"**
Your server URL may be missing the correct scheme or port. Verify the URL in `docsfy config show` matches your server's actual address.

**"Specify --branch, --provider, and --model together"**
When targeting a specific variant for status, download, delete, or abort, all three flags must be provided together. Use `docsfy status <project>` first to see available variants.

**WebSocket timeout with --watch**
The `--watch` flag times out after 5 minutes of inactivity. For long-running generations, use `docsfy status <project>` to poll instead.

For the full list of every flag and option, see [CLI Command Reference](cli-reference.html). For server-side configuration, see [Configuration Reference](configuration-reference.html).

## Related Pages

- [CLI Command Reference](cli-reference.html)
- [Getting Started with docsfy](quickstart.html)
- [Configuration Reference](configuration-reference.html)
- [Common Workflow Recipes](recipes-common-workflows.html)
- [Managing Projects and Variants](managing-projects.html)

---

Source: llms-txt-ai-readable-docs.md

# AI-Readable Documentation with llms.txt

Make your project's documentation instantly consumable by AI tools, LLM agents, and chatbots by pointing them at the `llms.txt` and `llms-full.txt` files that docsfy generates automatically with every documentation site.

## Prerequisites

- A docsfy documentation site that has been generated and is in **ready** status
- The URL of your documentation site (either the docsfy server URL or a static hosting URL)

## Quick Example

Every generated documentation site includes two machine-readable files at its root. Access them by appending the filename to your doc site URL:

```
# Structured index of all pages
https://your-server/docs/my-project/llms.txt

# Complete documentation in a single file
https://your-server/docs/my-project/llms-full.txt
```

Feed the full documentation to an AI assistant in one command:

```bash
curl -s https://your-server/docs/my-project/llms-full.txt | llm -s "How do I configure authentication?"
```

## What Gets Generated

docsfy produces two complementary files following the [llms.txt standard](https://llmstxt.org/):

| File | Purpose | Best For |
|------|---------|----------|
| `llms.txt` | Structured index with page titles, links, and descriptions | Quick lookup, deciding which pages to read, lightweight context |
| `llms-full.txt` | All page content concatenated into one file | Full-context queries, RAG ingestion, comprehensive analysis |

Both files are plain text and require no authentication beyond what your docsfy server enforces.

## The llms.txt Index File

The `llms.txt` file provides a structured table of contents. Here's what the format looks like:

```
# My Project

> A brief tagline describing the project

## Getting Started

- [Installation](installation.md): How to install and configure the project
- [Quick Start](quickstart.md): Get up and running in five minutes

## API Reference

- [REST API](rest-api.md): Complete endpoint reference
- [Authentication](authentication.md): API keys and token management
```

Each entry includes:

- **Page title** — the human-readable name
- **Link** — relative path to the markdown source (`.md` extension)
- **Description** — one-line summary of the page content

Pages are organized under their navigation group headings, matching the sidebar structure of the HTML documentation site.

## The llms-full.txt Complete File

The `llms-full.txt` file concatenates every page's markdown content into a single file, separated by `---` dividers:

```
# My Project

> A brief tagline describing the project

---

Source: installation.md

# Installation

Follow these steps to install...

---

Source: quickstart.md

# Quick Start

This guide walks you through...

---
```

Each page section starts with a `Source:` line identifying the original file, followed by the full markdown content of that page.

## Accessing llms.txt Files

### By URL

The files live at the root of every documentation site. The URL depends on how you access the docs:

| Access Pattern | llms.txt URL |
|---|---|
| Latest variant | `/docs/{project}/llms.txt` |
| Specific variant | `/docs/{project}/{branch}/{provider}/{model}/llms.txt` |

Replace `llms.txt` with `llms-full.txt` for the complete content file.

```bash
# Latest variant
curl https://your-server/docs/my-project/llms.txt

# Specific branch and AI variant
curl https://your-server/docs/my-project/main/cursor/gpt-5.4-xhigh-fast/llms.txt
```

### From Downloaded Sites

When you download a documentation site using the CLI or dashboard, the archive includes both files at the site root:

```bash
docsfy download my-project --output ./docs
cat ./docs/llms.txt
cat ./docs/llms-full.txt
```

See [Managing Projects and Variants](managing-projects.html) for download instructions.

### HTML Discovery

Every generated page includes `<link>` tags in the HTML `<head>` for automatic discovery:

```html
<link rel="alternate" type="text/plain" title="LLM Documentation Index" href="llms.txt">
<link rel="alternate" type="text/plain" title="LLM Full Documentation" href="llms-full.txt">
```

Links to both files also appear in the footer of every page and in a banner on the documentation homepage. AI crawlers and tools that follow `rel="alternate"` links will find them automatically.

## Using llms.txt with AI Tools

### Claude, ChatGPT, and Other Chat Interfaces

Paste the URL directly into your conversation:

```
Read https://your-server/docs/my-project/llms-full.txt and then answer:
How do I set up webhook notifications?
```

Or download and attach the file if URL fetching isn't supported:

```bash
curl -s https://your-server/docs/my-project/llms-full.txt > project-docs.txt
# Attach project-docs.txt to your chat
```

> **Tip:** Use `llms.txt` (the index) first when you only need to identify which pages are relevant, then fetch specific `.md` files for detail. This saves tokens when working with large documentation sets.

### Cursor, Windsurf, and AI Code Editors

Point your AI coding assistant at the documentation for context-aware code suggestions:

1. Download the full docs file into your project:

   ```bash
   curl -s https://your-server/docs/my-project/llms-full.txt > .llms-full.txt
   ```

2. Reference it in your prompts or add it to your editor's context files

### Command-Line AI Tools

Pipe documentation directly to CLI-based AI tools:

```bash
# Ask a question with full documentation context
curl -s https://your-server/docs/my-project/llms-full.txt | \
  llm -s "Summarize the authentication options"

# Search the index to find relevant pages first
curl -s https://your-server/docs/my-project/llms.txt | grep -i "auth"
```

## Advanced Usage

### RAG Pipeline Ingestion

The `llms-full.txt` file is ideal for loading into a RAG (Retrieval-Augmented Generation) pipeline. Each page is separated by `---` dividers and labeled with `Source:` headers, making it straightforward to split into chunks:

```python
import requests

response = requests.get("https://your-server/docs/my-project/llms-full.txt")
content = response.text

# Split into individual pages
pages = content.split("\n---\n")

for page in pages:
    lines = page.strip().split("\n")
    # Find the Source: line to identify the page
    source = next((l for l in lines if l.startswith("Source:")), None)
    if source:
        filename = source.replace("Source: ", "").strip()
        page_content = "\n".join(lines[lines.index(source) + 1:]).strip()
        # Ingest into your vector store
        # vector_store.add(filename, page_content)
```

### Automating Documentation Fetches in CI/CD

Keep an up-to-date copy of your AI-readable docs alongside your code:

```bash
# In your CI pipeline, after docs are generated:
curl -sf https://your-server/docs/my-project/llms-full.txt -o docs/llms-full.txt
curl -sf https://your-server/docs/my-project/llms.txt -o docs/llms.txt
```

See [Common Workflow Recipes](recipes-common-workflows.html) for more CI/CD automation patterns.

### Multi-Branch Documentation

Each branch variant gets its own independent `llms.txt` and `llms-full.txt` files. This means you can point AI tools at the documentation for a specific branch:

```bash
# Main branch docs
curl https://your-server/docs/my-project/main/cursor/gpt-5.4-xhigh-fast/llms-full.txt

# Dev branch docs
curl https://your-server/docs/my-project/dev/cursor/gpt-5.4-xhigh-fast/llms-full.txt
```

> **Note:** The files reflect only the pages that were successfully generated. If a page was skipped due to an invalid slug, it won't appear in either file.

### Using the Index to Fetch Individual Pages

The `llms.txt` index links to `.md` files. You can fetch individual pages for focused context instead of loading everything:

```bash
# 1. Read the index to find relevant pages
curl -s https://your-server/docs/my-project/llms.txt

# 2. Fetch just the page you need (markdown source)
curl -s https://your-server/docs/my-project/authentication.md
```

This is useful when your documentation is large and you want to minimize token usage. Every page exists as both an `.html` file (for humans) and an `.md` file (for AI tools) at the same URL path.

## Troubleshooting

**Files return 404**
The documentation must be in **ready** status. If generation is still in progress or has failed, the files won't exist yet. Check your project status in the dashboard or with `docsfy list`.

**Files are empty or missing pages**
Pages with invalid slugs are filtered out during rendering. If your `llms.txt` shows fewer pages than expected, check the generation logs for "Skipping invalid slug" warnings.

**Authentication required**
If your docsfy server requires authentication, include your credentials when fetching:

```bash
curl -H "Authorization: Bearer YOUR_API_KEY" \
  https://your-server/docs/my-project/llms-full.txt
```

See [Managing Users and Access Control](managing-users.html) for details on API keys and access permissions.

## Related Pages

- [Browsing Generated Documentation](browsing-docs.html)
- [Managing Projects and Variants](managing-projects.html)
- [Common Workflow Recipes](recipes-common-workflows.html)
- [Generating Documentation](generating-docs.html)
- [Managing Users and Access Control](managing-users.html)

---

Source: recipes-common-workflows.md

# Common Workflow Recipes

Practical, copy-paste patterns for working with docsfy in real-world scenarios.

## Generate Docs for Multiple Branches

Generate documentation for `main` and a release branch from the same repository.

```bash
# Generate docs for the main branch (default)
docsfy generate https://github.com/your-org/your-repo

# Generate docs for a release branch
docsfy generate https://github.com/your-org/your-repo --branch release-2.x
```

Each branch produces its own independent documentation variant. Browse them at `/docs/your-repo/main/cursor/gpt-5.4-xhigh-fast/` and `/docs/your-repo/release-2.x/cursor/gpt-5.4-xhigh-fast/` respectively.

> **Note:** Branch names cannot contain slashes. Use hyphens instead — `release-2.x` not `release/2.x`.

## Force-Regenerate Stale Documentation

Discard all cached pages and rebuild documentation from scratch.

```bash
docsfy generate https://github.com/your-org/your-repo --force
```

The `--force` flag clears the page cache for the variant and runs a full regeneration instead of an incremental update. Use this when you suspect the docs have drifted from the code or after major refactors.

- Combine with `--branch` to force-regenerate a specific branch: `--force --branch dev`
- See [Working with Incremental Updates](incremental-updates.html) for how docsfy decides what to regenerate without `--force`

## Watch Generation Progress in Real Time

Start a generation and stream progress updates to your terminal.

```bash
docsfy generate https://github.com/your-org/your-repo --watch
```

The `--watch` flag opens a WebSocket connection and prints each stage (`cloning`, `analyzing`, `planning`, `generating_pages`, `validating`, `cross_linking`, `rendering`) as it completes. The command exits automatically when generation finishes or fails.

- Without `--watch`, the `generate` command returns immediately with a `generation_id` and the generation continues in the background
- Check status later with: `docsfy status your-repo`

## Download and Host a Static Documentation Site

Download the generated site and serve it locally with any static file server.

```bash
# Download as tar.gz archive
docsfy download your-repo --branch main --provider cursor --model gpt-5.4-xhigh-fast

# Or extract directly into a directory
docsfy download your-repo -b main -p cursor -m gpt-5.4-xhigh-fast \
  --output ./docs-site --flatten

# Serve with Python's built-in HTTP server
cd docs-site && python -m http.server 3000
```

The `--flatten` flag removes the nested archive directory structure so `index.html` sits directly in the output directory. Without `--flatten`, files are extracted under a `your-repo-main-cursor-gpt-5.4-xhigh-fast/` subdirectory.

- The site is fully self-contained — no backend needed to serve it
- See [Managing Projects and Variants](managing-projects.html) for more download options

## Download Docs via the REST API (curl)

Download documentation without the CLI using a direct API call.

```bash
# Download the latest variant of a project
curl -H "Authorization: Bearer $DOCSFY_API_KEY" \
  https://docsfy.example.com/api/projects/your-repo/download \
  -o your-repo-docs.tar.gz

# Download a specific variant
curl -H "Authorization: Bearer $DOCSFY_API_KEY" \
  https://docsfy.example.com/api/projects/your-repo/main/cursor/gpt-5.4-xhigh-fast/download \
  -o your-repo-docs.tar.gz

# Extract
tar xzf your-repo-docs.tar.gz
```

This is useful in CI/CD pipelines or environments where installing the CLI is not practical.

## Automate Doc Generation in CI/CD (GitHub Actions)

Trigger documentation generation on every push to `main`.

```yaml
# .github/workflows/docs.yml
name: Generate Documentation
on:
  push:
    branches: [main]

jobs:
  generate-docs:
    runs-on: ubuntu-latest
    steps:
      - name: Trigger doc generation
        run: |
          RESPONSE=$(curl -s -X POST \
            -H "Authorization: Bearer ${{ secrets.DOCSFY_API_KEY }}" \
            -H "Content-Type: application/json" \
            -d '{
              "repo_url": "https://github.com/${{ github.repository }}",
              "branch": "main"
            }' \
            ${{ vars.DOCSFY_SERVER }}/api/generate)

          echo "$RESPONSE"
          GEN_ID=$(echo "$RESPONSE" | jq -r '.generation_id')
          echo "generation_id=$GEN_ID" >> "$GITHUB_OUTPUT"

      - name: Wait for generation
        run: |
          for i in $(seq 1 60); do
            STATUS=$(curl -s \
              -H "Authorization: Bearer ${{ secrets.DOCSFY_API_KEY }}" \
              ${{ vars.DOCSFY_SERVER }}/api/projects/by-id/$GEN_ID \
              | jq -r '.status')

            echo "Attempt $i: status=$STATUS"

            if [ "$STATUS" = "ready" ]; then
              echo "Documentation generated successfully!"
              exit 0
            elif [ "$STATUS" = "error" ] || [ "$STATUS" = "aborted" ]; then
              echo "Generation failed with status: $STATUS"
              exit 1
            fi

            sleep 10
          done
          echo "Timed out waiting for generation"
          exit 1
        env:
          GEN_ID: ${{ steps.*.outputs.generation_id }}
```

The `/api/generate` endpoint returns a `generation_id` (UUID) that you can poll via `/api/projects/by-id/{generation_id}` to check status. The generation runs asynchronously on the server.

> **Warning:** Store your API key in GitHub Secrets (`DOCSFY_API_KEY`), never in the workflow file. Set your server URL as a repository variable (`DOCSFY_SERVER`).

## Automate Doc Generation in CI/CD (GitLab CI)

Trigger doc generation from a GitLab pipeline.

```yaml
# .gitlab-ci.yml
generate-docs:
  stage: deploy
  image: curlimages/curl:latest
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
  script:
    - |
      RESPONSE=$(curl -s -X POST \
        -H "Authorization: Bearer ${DOCSFY_API_KEY}" \
        -H "Content-Type: application/json" \
        -d "{\"repo_url\": \"${CI_PROJECT_URL}.git\", \"branch\": \"main\"}" \
        ${DOCSFY_SERVER}/api/generate)

      echo "$RESPONSE"
      GEN_ID=$(echo "$RESPONSE" | jq -r '.generation_id')

      for i in $(seq 1 60); do
        STATUS=$(curl -s \
          -H "Authorization: Bearer ${DOCSFY_API_KEY}" \
          ${DOCSFY_SERVER}/api/projects/by-id/${GEN_ID} \
          | jq -r '.status')

        if [ "$STATUS" = "ready" ]; then exit 0; fi
        if [ "$STATUS" = "error" ] || [ "$STATUS" = "aborted" ]; then exit 1; fi
        sleep 10
      done
      exit 1
```

Set `DOCSFY_API_KEY` and `DOCSFY_SERVER` as CI/CD variables in GitLab project settings.

## Generate Docs with a Specific AI Provider

Override the server's default provider and model for a single generation.

```bash
# Use Claude
docsfy generate https://github.com/your-org/your-repo \
  --provider claude --model claude-sonnet-4-20250514

# Use Gemini
docsfy generate https://github.com/your-org/your-repo \
  --provider gemini --model gemini-2.5-pro
```

When you omit `--provider` and `--model`, the server defaults are used (configured via `AI_PROVIDER` and `AI_MODEL` environment variables). Each provider/model combination creates a separate variant.

- See [Configuring AI Providers](configuring-ai-providers.html) for supported providers and model selection
- View available models with `docsfy models`

## Specify the Repository Type

Hint the documentation style by specifying the repository type.

```bash
docsfy generate https://github.com/your-org/your-lib \
  --repo-type library

docsfy generate https://github.com/your-org/your-app \
  --repo-type app
```

Valid types are `app`, `library`, `framework`, and `tests`. When omitted, docsfy auto-detects the type from the repository structure. Setting it explicitly produces documentation tailored to that project type — for example, `library` documentation emphasizes API reference pages while `app` documentation focuses on setup and usage guides.

## Set Up CLI Server Profiles

Configure named server profiles to switch between environments.

```bash
# Interactive setup — creates a profile and sets it as default
docsfy config init

# Add a production profile manually
docsfy config set servers.prod.url https://docsfy.example.com
docsfy config set servers.prod.username admin
docsfy config set servers.prod.password your-api-key-here

# Switch the default profile
docsfy config set default.server prod

# Use a non-default profile for a single command
docsfy --server dev list
```

Configuration is stored in `~/.config/docsfy/config.toml` with file permissions restricted to the owner. See [Using the CLI](using-the-cli.html) for full CLI configuration details.

## Check Status of All Projects

List all projects with optional filtering.

```bash
# List all projects
docsfy list

# Filter by status
docsfy list --status ready
docsfy list --status generating

# Filter by provider
docsfy list --provider cursor

# JSON output for scripting
docsfy list --json | jq '.[] | select(.status == "error") | .name'
```

Use `docsfy status <project-name>` to see detailed information about a specific project and all its variants.

## Look Up a Project by Generation ID

Check the status of a specific generation run using its UUID.

```bash
# Check status by generation ID
docsfy status a1b2c3d4-e5f6-7890-abcd-ef1234567890

# Download by generation ID
docsfy download a1b2c3d4-e5f6-7890-abcd-ef1234567890 --output ./docs

# Abort by generation ID
docsfy abort a1b2c3d4-e5f6-7890-abcd-ef1234567890
```

The `generate` command prints the generation ID when it starts. All project commands (`status`, `download`, `delete`, `abort`) accept a generation ID in place of a project name, automatically resolving the correct variant.

## Delete a Specific Variant

Remove a single branch/provider/model variant without affecting others.

```bash
# Delete a specific variant
docsfy delete your-repo \
  --branch main --provider cursor --model gpt-5.4-xhigh-fast --yes

# Delete ALL variants of a project
docsfy delete your-repo --all --yes
```

Deleting a variant removes it from the database and cleans up its generated files from disk. Active generations must be aborted first — use `docsfy abort your-repo` before deleting.

- See [Managing Projects and Variants](managing-projects.html) for the full lifecycle

## Abort a Stuck Generation

Cancel an in-progress generation that is taking too long.

```bash
# Abort by project name (finds the active variant automatically)
docsfy abort your-repo

# Abort a specific variant
docsfy abort your-repo --branch dev --provider claude --model claude-sonnet-4-20250514
```

The aborted variant is marked with status `aborted`. You can then re-trigger generation with `docsfy generate`.

## Create a User and Grant Project Access

Set up a new user and share an existing project with them.

```bash
# Create a user (save the API key — it's shown only once)
docsfy admin users create alice --role user

# Grant access to a project
docsfy admin access grant your-repo --username alice --owner admin

# Verify access
docsfy admin access list your-repo --owner admin
```

Valid roles are `admin`, `user`, and `viewer`. Viewers have read-only access and cannot trigger generation or delete projects. See [Managing Users and Access Control](managing-users.html) for the full access model.

## Deploy to GitHub Pages After Generation

Download generated docs and publish them to GitHub Pages in CI.

```yaml
# .github/workflows/docs-pages.yml
name: Publish Docs to GitHub Pages
on:
  workflow_dispatch:

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      pages: write
      id-token: write
    environment:
      name: github-pages
    steps:
      - name: Download docs
        run: |
          curl -H "Authorization: Bearer ${{ secrets.DOCSFY_API_KEY }}" \
            ${{ vars.DOCSFY_SERVER }}/api/projects/your-repo/download \
            -o docs.tar.gz
          mkdir -p docs-site
          tar xzf docs.tar.gz -C docs-site --strip-components=1

      - name: Upload Pages artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: docs-site

      - name: Deploy to GitHub Pages
        uses: actions/deploy-pages@v4
```

The `--strip-components=1` removes the top-level archive directory so pages are deployed at the root. The generated site is fully static and works out of the box with GitHub Pages.

## Generate Docs via the REST API (Without CLI)

Trigger generation using `curl` when the CLI is not available.

```bash
# Start generation
curl -X POST \
  -H "Authorization: Bearer $DOCSFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "repo_url": "https://github.com/your-org/your-repo",
    "branch": "main",
    "force": true
  }' \
  https://docsfy.example.com/api/generate

# Response:
# {"project":"your-repo","status":"generating","branch":"main","generation_id":"..."}
```

The `force` field is optional (defaults to `false`). You can also pass `ai_provider`, `ai_model`, and `repo_type` in the request body. See [REST API Reference](api-reference.html) for the full endpoint documentation.

> **Tip:** Pipe `curl` output through `jq` for readable JSON: `curl ... | jq .`

## Verify Server Health

Quickly check that the docsfy server and its AI sidecar are running.

```bash
# Via CLI
docsfy health

# Via curl (no auth required)
curl https://docsfy.example.com/health
# {"status":"ok"}
```

The `/health` endpoint is unauthenticated and returns `{"status": "ok"}` when the server is ready. The Docker healthcheck also validates the Pi SDK sidecar on its port.

## Related Pages

- [Using the CLI](using-the-cli.html)
- [Generating Documentation](generating-docs.html)
- [Managing Projects and Variants](managing-projects.html)
- [REST API Reference](api-reference.html)
- [Configuring AI Providers](configuring-ai-providers.html)

---

Source: api-reference.md

# REST API Reference

Complete reference for all HTTP and WebSocket endpoints exposed by the docsfy server. All endpoints are served under the base URL where `docsfy-server` is running (default: `http://127.0.0.1:8000`).

> **Note:** For environment variables and server configuration, see [Configuration Reference](configuration-reference.html). For CLI usage against these endpoints, see [CLI Command Reference](cli-reference.html).

## Authentication

All `/api/` and `/docs/` endpoints require authentication except where noted. docsfy supports two authentication methods:

| Method | Header / Cookie | Usage |
|---|---|---|
| Bearer token | `Authorization: Bearer <token>` | API clients, CLI |
| Session cookie | `docsfy_session` cookie | Browser (set after login) |

Bearer tokens are either the `ADMIN_KEY` environment variable (for the built-in admin account) or a user-specific API key returned when a user is created.

```bash
# Bearer token authentication
curl -H "Authorization: Bearer YOUR_API_KEY" http://localhost:8000/api/projects

# Session-based authentication (login first, then use cookie)
curl -c cookies.txt -X POST http://localhost:8000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "api_key": "YOUR_ADMIN_KEY"}'

curl -b cookies.txt http://localhost:8000/api/projects
```

### Roles

| Role | Permissions |
|---|---|
| `admin` | Full access: manage users, view all projects, delete any project, access admin endpoints |
| `user` | Create/generate/delete own projects, rotate own key |
| `viewer` | Read-only access to projects shared with them |

> **Note:** The built-in `admin` account authenticates with `ADMIN_KEY`. Database users with `role: admin` also receive full admin privileges. See [Managing Users and Access Control](managing-users.html) for details.

---

## Health Check

### `GET /health`

Returns server health status. No authentication required.

**Response**

```json
{"status": "ok"}
```

---

## Authentication Endpoints

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

Authenticate and receive a session cookie. No authentication required.

**Request Body**

| Field | Type | Required | Description |
|---|---|---|---|
| `username` | string | Yes | Username (`"admin"` for the built-in admin) |
| `api_key` | string | Yes | API key or admin key |

**Response** `200 OK`

```json
{
  "username": "admin",
  "role": "admin",
  "is_admin": true
}
```

A `docsfy_session` cookie is set on the response with the following attributes:

| Attribute | Value |
|---|---|
| `httponly` | `true` |
| `samesite` | `strict` |
| `secure` | Controlled by `SECURE_COOKIES` env var |
| `max_age` | 28800 (8 hours) |

**Errors**

| Status | Condition |
|---|---|
| 400 | Invalid JSON body |
| 401 | Invalid credentials |

```bash
curl -X POST http://localhost:8000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "alice", "api_key": "docsfy_abc123..."}'
```

---

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

Clear the session cookie and delete the server-side session. Requires authentication.

**Response** `200 OK`

```json
{"ok": true}
```

```bash
curl -X POST http://localhost:8000/api/auth/logout -b cookies.txt
```

---

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

Return information about the currently authenticated user. Requires authentication.

**Response** `200 OK`

```json
{
  "username": "alice",
  "role": "user",
  "is_admin": false
}
```

---

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

Rotate the current user's API key. The session is invalidated after rotation — the user must re-login with the new key.

> **Warning:** Users authenticated via `ADMIN_KEY` (the built-in admin) cannot rotate keys through this endpoint. Change the `ADMIN_KEY` environment variable instead.

**Request Body** (optional)

| Field | Type | Required | Description |
|---|---|---|---|
| `new_key` | string | No | Custom API key (minimum 16 characters). If omitted, a key is auto-generated. |

**Response** `200 OK`

```json
{
  "username": "alice",
  "new_api_key": "docsfy_aBcDeFgHiJkLmNoPqRsTuVwXyZ..."
}
```

The response includes `Cache-Control: no-store` to prevent caching of the key.

**Errors**

| Status | Condition |
|---|---|
| 400 | Built-in admin attempting rotation, or custom key too short |

```bash
curl -X POST http://localhost:8000/api/auth/rotate-key \
  -H "Authorization: Bearer docsfy_old_key..."
```

---

## Models & Discovery

### `GET /api/models`

Return available AI providers, server default settings, and discovered models. **No authentication required.**

Models are discovered dynamically from the Pi SDK sidecar service.

**Response** `200 OK`

```json
{
  "providers": ["claude", "gemini", "cursor"],
  "default_provider": "cursor",
  "default_model": "gpt-5.4-xhigh-fast",
  "available_models": {
    "claude": [
      {"id": "claude-sonnet-4-20250514", "provider": "anthropic/claude"}
    ],
    "gemini": [],
    "cursor": [
      {"id": "gpt-5.4-xhigh-fast", "provider": "cursor"}
    ]
  }
}
```

| Field | Type | Description |
|---|---|---|
| `providers` | string[] | List of valid provider names |
| `default_provider` | string | Server's default AI provider |
| `default_model` | string | Server's default AI model |
| `available_models` | object | Models per provider, discovered from sidecar |

```bash
curl http://localhost:8000/api/models
```

> **Tip:** See [Configuring AI Providers](configuring-ai-providers.html) for details on provider setup and the Pi SDK sidecar.

---

## Cost

### `GET /api/cost`

Return total AI generation cost. Admins see all costs; regular users see only their own.

**Response** `200 OK`

```json
{"total_cost_usd": 1.2345}
```

```bash
curl -H "Authorization: Bearer YOUR_KEY" http://localhost:8000/api/cost
```

---

## Projects & Generation

### `GET /api/projects`

List all projects visible to the authenticated user. Also accessible at `GET /api/status` (alias).

Admins see all projects. Regular users see their own projects plus projects shared with them.

**Response** `200 OK`

```json
{
  "projects": [
    {
      "name": "my-repo",
      "branch": "main",
      "ai_provider": "cursor",
      "ai_model": "gpt-5.4-xhigh-fast",
      "owner": "alice",
      "generation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "repo_url": "https://github.com/org/my-repo",
      "status": "ready",
      "current_stage": null,
      "last_commit_sha": "abc1234",
      "last_generated": "2026-06-08 12:00:00",
      "page_count": 12,
      "error_message": null,
      "plan_json": "{...}",
      "repo_type": "library",
      "total_cost_usd": 0.45,
      "created_at": "2026-06-07 10:00:00",
      "updated_at": "2026-06-08 12:00:00"
    }
  ],
  "known_branches": {
    "my-repo": ["main", "dev"]
  },
  "total_cost_usd": 1.23
}
```

| Field | Type | Description |
|---|---|---|
| `projects` | object[] | Array of project variant records |
| `known_branches` | object | Map of project name → list of branches with `ready` variants |
| `total_cost_usd` | float | Total generation cost for the user (or all users if admin) |

**Project variant fields**

| Field | Type | Description |
|---|---|---|
| `name` | string | Project name (extracted from repo URL) |
| `branch` | string | Git branch |
| `ai_provider` | string | AI provider used |
| `ai_model` | string | AI model used |
| `owner` | string | Username of the project owner |
| `generation_id` | string | UUID identifying this generation run |
| `repo_url` | string | Source repository URL |
| `status` | string | One of: `generating`, `ready`, `error`, `aborted` |
| `current_stage` | string \| null | Current generation stage (when status is `generating`) |
| `last_commit_sha` | string \| null | Git commit SHA of last generation |
| `last_generated` | string \| null | Timestamp of last successful generation |
| `page_count` | integer \| null | Number of documentation pages |
| `error_message` | string \| null | Error description (when status is `error` or `aborted`) |
| `plan_json` | string \| null | JSON-encoded documentation plan |
| `repo_type` | string \| null | Detected repository type: `app`, `library`, `framework`, or `tests` |
| `total_cost_usd` | float \| null | AI generation cost for this variant |
| `created_at` | string | Creation timestamp |
| `updated_at` | string | Last update timestamp |

```bash
curl -H "Authorization: Bearer YOUR_KEY" http://localhost:8000/api/projects
```

---

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

Get all variants for a project by name. Returns variants the authenticated user owns plus any shared variants.

**Path Parameters**

| Parameter | Type | Description |
|---|---|---|
| `name` | string | Project name |

**Response** `200 OK`

```json
{
  "name": "my-repo",
  "variants": [
    {
      "name": "my-repo",
      "branch": "main",
      "ai_provider": "cursor",
      "ai_model": "gpt-5.4-xhigh-fast",
      "owner": "alice",
      "status": "ready",
      "page_count": 12
    }
  ]
}
```

**Errors**

| Status | Condition |
|---|---|
| 400 | Invalid project name |
| 404 | Project not found or not accessible |

```bash
curl -H "Authorization: Bearer YOUR_KEY" http://localhost:8000/api/projects/my-repo
```

---

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

Get details for a specific project variant.

**Path Parameters**

| Parameter | Type | Description |
|---|---|---|
| `name` | string | Project name |
| `branch` | string | Git branch |
| `provider` | string | AI provider (`claude`, `gemini`, or `cursor`) |
| `model` | string | AI model identifier |

**Query Parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| `owner` | string | No | Filter by owner (admin only) |

**Response** `200 OK`

Returns a single project variant object (same fields as described in [project variant fields](#project-variant-fields) above).

**Errors**

| Status | Condition |
|---|---|
| 400 | Invalid project name |
| 404 | Variant not found or not accessible |
| 409 | Multiple owners found for this variant (admin must specify `?owner=`) |

```bash
curl -H "Authorization: Bearer YOUR_KEY" \
  http://localhost:8000/api/projects/my-repo/main/cursor/gpt-5.4-xhigh-fast
```

---

### `GET /api/projects/by-id/{generation_id}`

Look up a project variant by its generation UUID.

**Path Parameters**

| Parameter | Type | Description |
|---|---|---|
| `generation_id` | string | UUID in canonical hyphenated format (`8-4-4-4-12`) |

**Response** `200 OK`

Returns a single project variant object.

**Errors**

| Status | Condition |
|---|---|
| 400 | Invalid UUID format |
| 404 | Generation ID not found or not accessible |

```bash
curl -H "Authorization: Bearer YOUR_KEY" \
  http://localhost:8000/api/projects/by-id/a1b2c3d4-e5f6-7890-abcd-ef1234567890
```

---

### `POST /api/generate`

Submit a repository for documentation generation. Returns immediately with `202 Accepted`; generation runs asynchronously in the background.

> **Note:** This endpoint requires `admin` or `user` role. Viewers cannot generate docs.

**Request Body**

| Field | Type | Default | Required | Description |
|---|---|---|---|---|
| `repo_url` | string | — | One of `repo_url` or `repo_path` | Git repository URL (HTTPS or SSH) |
| `repo_path` | string | — | One of `repo_url` or `repo_path` | Absolute path to local git repo (admin only) |
| `ai_provider` | string | Server default | No | AI provider: `claude`, `gemini`, or `cursor` |
| `ai_model` | string | Server default | No | AI model identifier |
| `ai_cli_timeout` | integer | Server default | No | AI call timeout in seconds (must be > 0) |
| `force` | boolean | `false` | No | Force full regeneration, ignoring cache |
| `repo_type` | string | Auto-detected | No | Repository type: `app`, `library`, `framework`, or `tests` |
| `branch` | string | `"main"` | No | Git branch to generate docs from |

**Branch validation:** Must match `^[a-zA-Z0-9][a-zA-Z0-9._-]*$`. Slashes are rejected — use hyphens instead (e.g., `release-1.x` not `release/1.x`).

**Response** `202 Accepted`

```json
{
  "project": "my-repo",
  "status": "generating",
  "branch": "main",
  "generation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "repo_type": null
}
```

**Errors**

| Status | Condition |
|---|---|
| 400 | Invalid provider, empty model, repo path doesn't exist, private/internal URL |
| 403 | `repo_path` used by non-admin, or viewer role |
| 409 | Same variant is already being generated |
| 422 | Validation error (invalid URL format, missing source, etc.) |

```bash
# Generate docs from a GitHub repo
curl -X POST http://localhost:8000/api/generate \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "repo_url": "https://github.com/org/my-repo.git",
    "branch": "main",
    "ai_provider": "cursor",
    "ai_model": "gpt-5.4-xhigh-fast"
  }'

# Force full regeneration
curl -X POST http://localhost:8000/api/generate \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "repo_url": "https://github.com/org/my-repo.git",
    "force": true
  }'

# Generate from a local path (admin only)
curl -X POST http://localhost:8000/api/generate \
  -H "Authorization: Bearer ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"repo_path": "/home/user/projects/my-repo"}'
```

> **Tip:** Monitor generation progress in real time via the [WebSocket endpoint](#websocket-real-time-updates). For incremental update behavior, see [Working with Incremental Updates](incremental-updates.html).

**Generation Stages**

While status is `generating`, the `current_stage` field progresses through:

| Stage | Description |
|---|---|
| `cloning` | Cloning the git repository |
| `analyzing` | Building code knowledge graph |
| `planning` | AI planning documentation structure |
| `incremental_planning` | AI determining which pages need updates |
| `generating_pages` | AI writing documentation pages |
| `validating` | Validating generated content |
| `completeness_check` | Checking for undocumented features |
| `cross_linking` | Adding cross-references between pages |
| `rendering` | Building the HTML documentation site |
| `up_to_date` | No changes detected, docs already current |

---

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

Abort an active generation for the given project. If multiple variants are generating, use the variant-specific abort endpoint instead.

> **Note:** Requires `admin` or `user` role. Non-admin users can only abort their own generations.

**Path Parameters**

| Parameter | Type | Description |
|---|---|---|
| `name` | string | Project name |

**Response** `200 OK`

```json
{"aborted": "my-repo"}
```

**Errors**

| Status | Condition |
|---|---|
| 404 | No active generation for this project |
| 409 | Multiple active variants (use variant-specific abort), or abort still in progress |

```bash
curl -X POST http://localhost:8000/api/projects/my-repo/abort \
  -H "Authorization: Bearer YOUR_KEY"
```

---

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

Abort a specific variant's generation.

**Path Parameters**

| Parameter | Type | Description |
|---|---|---|
| `name` | string | Project name |
| `branch` | string | Git branch |
| `provider` | string | AI provider |
| `model` | string | AI model |

**Query Parameters (admin only)**

| Parameter | Type | Required | Description |
|---|---|---|---|
| `owner` | string | No | Target a specific owner's generation |

**Response** `200 OK`

```json
{"aborted": "my-repo/main/cursor/gpt-5.4-xhigh-fast"}
```

**Errors**

| Status | Condition |
|---|---|
| 404 | No active generation for this variant |
| 409 | Generation already finished, or abort in progress, or multiple owners found |

```bash
curl -X POST \
  http://localhost:8000/api/projects/my-repo/main/cursor/gpt-5.4-xhigh-fast/abort \
  -H "Authorization: Bearer YOUR_KEY"
```

---

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

Delete all variants of a project.

> **Note:** Requires `admin` or `user` role. Cannot delete while any variant is actively generating — abort first.

**Path Parameters**

| Parameter | Type | Description |
|---|---|---|
| `name` | string | Project name |

**Query Parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| `owner` | string | Yes (admin only) | Owner of the project to delete. Use `?owner=` (empty) for legacy unowned projects. Non-admin users always delete their own. |

**Response** `200 OK`

```json
{"deleted": "my-repo"}
```

**Errors**

| Status | Condition |
|---|---|
| 400 | Admin did not specify `?owner=` |
| 404 | Project not found |
| 409 | Generation in progress (abort first) |

```bash
# Non-admin: deletes own project
curl -X DELETE http://localhost:8000/api/projects/my-repo \
  -H "Authorization: Bearer YOUR_KEY"

# Admin: must specify owner
curl -X DELETE "http://localhost:8000/api/projects/my-repo?owner=alice" \
  -H "Authorization: Bearer ADMIN_KEY"
```

---

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

Delete a specific project variant.

**Path Parameters**

| Parameter | Type | Description |
|---|---|---|
| `name` | string | Project name |
| `branch` | string | Git branch |
| `provider` | string | AI provider |
| `model` | string | AI model |

**Query Parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| `owner` | string | Yes (admin only) | Owner of the variant. Non-admins always delete their own. |

**Response** `200 OK`

```json
{"deleted": "my-repo/main/cursor/gpt-5.4-xhigh-fast"}
```

**Errors**

| Status | Condition |
|---|---|
| 400 | Admin did not specify `?owner=` |
| 404 | Variant not found |
| 409 | Generation in progress for this variant |

```bash
curl -X DELETE \
  "http://localhost:8000/api/projects/my-repo/main/cursor/gpt-5.4-xhigh-fast?owner=alice" \
  -H "Authorization: Bearer ADMIN_KEY"
```

---

## Download

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

Download the latest ready variant as a `.tar.gz` archive. Resolves to the most recently generated `ready` variant across all branches and providers.

**Path Parameters**

| Parameter | Type | Description |
|---|---|---|
| `name` | string | Project name |

**Response** `200 OK`

- Content-Type: `application/gzip`
- Content-Disposition: `attachment; filename="<name>-docs.tar.gz"`

**Errors**

| Status | Condition |
|---|---|
| 404 | No ready variant found |

```bash
curl -O -J -H "Authorization: Bearer YOUR_KEY" \
  http://localhost:8000/api/projects/my-repo/download
```

---

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

Download a specific variant as a `.tar.gz` archive.

**Path Parameters**

| Parameter | Type | Description |
|---|---|---|
| `name` | string | Project name |
| `branch` | string | Git branch |
| `provider` | string | AI provider |
| `model` | string | AI model |

**Response** `200 OK`

- Content-Type: `application/gzip`
- Content-Disposition: `attachment; filename="<name>-<branch>-<provider>-<model>-docs.tar.gz"`

**Errors**

| Status | Condition |
|---|---|
| 400 | Variant not ready |
| 404 | Site not found or variant not accessible |

```bash
curl -O -J -H "Authorization: Bearer YOUR_KEY" \
  http://localhost:8000/api/projects/my-repo/main/cursor/gpt-5.4-xhigh-fast/download
```

> **Tip:** See [Common Workflow Recipes](recipes-common-workflows.html) for patterns on downloading and hosting static sites.

---

## Serving Generated Documentation

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

Serve files from a specific variant's generated documentation site.

**Path Parameters**

| Parameter | Type | Description |
|---|---|---|
| `project` | string | Project name |
| `branch` | string | Git branch |
| `provider` | string | AI provider |
| `model` | string | AI model |
| `path` | string | File path within the site (defaults to `index.html`) |

**Response** `200 OK` — The requested file.

**Errors**

| Status | Condition |
|---|---|
| 302 | Unauthenticated browser redirected to `/login` |
| 403 | Path traversal attempt |
| 404 | File or variant not found |

```
http://localhost:8000/docs/my-repo/main/cursor/gpt-5.4-xhigh-fast/
http://localhost:8000/docs/my-repo/main/cursor/gpt-5.4-xhigh-fast/quickstart.html
```

---

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

Serve files from the latest ready variant of a project (auto-resolved).

**Path Parameters**

| Parameter | Type | Description |
|---|---|---|
| `project` | string | Project name |
| `path` | string | File path within the site (defaults to `index.html`) |

```
http://localhost:8000/docs/my-repo/
```

> **Note:** See [Browsing Generated Documentation](browsing-docs.html) for details on navigating the generated sites.

---

## Admin: User Management

All endpoints in this section require **admin** role.

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

Create a new user account. Returns the generated API key.

**Request Body**

| Field | Type | Default | Required | Description |
|---|---|---|---|---|
| `username` | string | — | Yes | 2–50 characters, alphanumeric plus `.`, `-`, `_`. Cannot be `"admin"`. |
| `role` | string | `"user"` | No | One of: `admin`, `user`, `viewer` |

**Response** `200 OK`

```json
{
  "username": "alice",
  "api_key": "docsfy_aBcDeFgHiJkLmNoPqRsTuVwXyZ...",
  "role": "user"
}
```

The response includes `Cache-Control: no-store`.

**Errors**

| Status | Condition |
|---|---|
| 400 | Missing/invalid username, invalid role, username `"admin"` is reserved, duplicate username |
| 403 | Non-admin caller |

```bash
curl -X POST http://localhost:8000/api/admin/users \
  -H "Authorization: Bearer ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"username": "alice", "role": "user"}'
```

---

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

List all user accounts (without API key hashes).

**Response** `200 OK`

```json
{
  "users": [
    {
      "id": 1,
      "username": "alice",
      "role": "user",
      "created_at": "2026-06-08 10:00:00"
    }
  ]
}
```

**Errors**

| Status | Condition |
|---|---|
| 403 | Non-admin caller |

```bash
curl -H "Authorization: Bearer ADMIN_KEY" http://localhost:8000/api/admin/users
```

---

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

Delete a user account. Invalidates all sessions, removes owned projects and their files, and cleans up access grants.

**Path Parameters**

| Parameter | Type | Description |
|---|---|---|
| `username` | string | Username to delete |

**Response** `200 OK`

```json
{"deleted": "alice"}
```

**Errors**

| Status | Condition |
|---|---|
| 400 | Attempting to delete your own account |
| 403 | Non-admin caller |
| 404 | User not found |
| 409 | User has an active generation in progress |

```bash
curl -X DELETE http://localhost:8000/api/admin/users/alice \
  -H "Authorization: Bearer ADMIN_KEY"
```

---

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

Admin-initiated API key rotation for any user.

**Path Parameters**

| Parameter | Type | Description |
|---|---|---|
| `username` | string | Target username |

**Request Body** (optional)

| Field | Type | Required | Description |
|---|---|---|---|
| `new_key` | string | No | Custom API key (minimum 16 characters). Auto-generated if omitted. |

**Response** `200 OK`

```json
{
  "username": "alice",
  "new_api_key": "docsfy_aBcDeFgHiJkLmNoPqRsTuVwXyZ..."
}
```

The response includes `Cache-Control: no-store`.

**Errors**

| Status | Condition |
|---|---|
| 400 | Custom key too short |
| 403 | Non-admin caller |
| 404 | User not found |

```bash
curl -X POST http://localhost:8000/api/admin/users/alice/rotate-key \
  -H "Authorization: Bearer ADMIN_KEY"
```

---

## Admin: Project Access Control

All endpoints in this section require **admin** role.

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

Grant a user access to all variants of a project (scoped to a specific owner's copy).

**Path Parameters**

| Parameter | Type | Description |
|---|---|---|
| `name` | string | Project name |

**Request Body**

| Field | Type | Required | Description |
|---|---|---|---|
| `username` | string | Yes | Username to grant access to |
| `owner` | string | Yes | Owner of the project |

**Response** `200 OK`

```json
{
  "granted": "my-repo",
  "username": "bob",
  "owner": "alice"
}
```

**Errors**

| Status | Condition |
|---|---|
| 400 | Missing username or owner |
| 403 | Non-admin caller |
| 404 | User or project not found |

```bash
curl -X POST http://localhost:8000/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}`

Revoke a user's access to a project.

**Path Parameters**

| Parameter | Type | Description |
|---|---|---|
| `name` | string | Project name |
| `username` | string | Username to revoke |

**Query Parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| `owner` | string | Yes | Owner of the project |

**Response** `200 OK`

```json
{
  "revoked": "my-repo",
  "username": "bob",
  "owner": "alice"
}
```

**Errors**

| Status | Condition |
|---|---|
| 400 | Missing `?owner=` |
| 403 | Non-admin caller |

```bash
curl -X DELETE \
  "http://localhost:8000/api/admin/projects/my-repo/access/bob?owner=alice" \
  -H "Authorization: Bearer ADMIN_KEY"
```

---

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

List users with access to a project.

**Path Parameters**

| Parameter | Type | Description |
|---|---|---|
| `name` | string | Project name |

**Query Parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| `owner` | string | Yes | Owner of the project |

**Response** `200 OK`

```json
{
  "project": "my-repo",
  "owner": "alice",
  "users": ["bob", "carol"]
}
```

**Errors**

| Status | Condition |
|---|---|
| 400 | Missing `?owner=` |
| 403 | Non-admin caller |

```bash
curl "http://localhost:8000/api/admin/projects/my-repo/access?owner=alice" \
  -H "Authorization: Bearer ADMIN_KEY"
```

> **Note:** For more on sharing projects and managing access, see [Managing Users and Access Control](managing-users.html).

---

## WebSocket Real-Time Updates

### `WebSocket /api/ws`

Persistent WebSocket connection for receiving real-time project status updates. Authenticates via query parameter or session cookie.

**Authentication**

| Method | Example |
|---|---|
| Query parameter | `ws://localhost:8000/api/ws?token=YOUR_API_KEY` |
| Session cookie | Connect with `docsfy_session` cookie set |

Unauthenticated connections are closed immediately with code `1008` (Policy Violation).

```javascript
const ws = new WebSocket("ws://localhost:8000/api/ws?token=YOUR_API_KEY");
ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  console.log(msg.type, msg);
};
```

### Message Types

#### `sync`

Full project list snapshot. Sent immediately on connection and whenever project data changes (creation, deletion, access changes).

```json
{
  "type": "sync",
  "projects": [ ... ],
  "known_branches": { "my-repo": ["main", "dev"] },
  "total_cost_usd": 1.23
}
```

The `projects`, `known_branches`, and `total_cost_usd` fields have the same structure as the `GET /api/projects` response.

#### `progress`

Sent during active generation to report stage and page count changes.

```json
{
  "type": "progress",
  "name": "my-repo",
  "branch": "main",
  "provider": "cursor",
  "model": "gpt-5.4-xhigh-fast",
  "owner": "alice",
  "status": "generating",
  "generation_id": "a1b2c3d4-...",
  "current_stage": "generating_pages",
  "page_count": 5,
  "plan_json": "{...}"
}
```

| Field | Type | Description |
|---|---|---|
| `name` | string | Project name |
| `branch` | string | Git branch |
| `provider` | string | AI provider |
| `model` | string | AI model |
| `owner` | string | Project owner |
| `status` | string | Always `"generating"` for progress messages |
| `generation_id` | string | Generation UUID |
| `current_stage` | string \| null | Current generation stage |
| `page_count` | integer \| null | Pages generated so far |
| `plan_json` | string \| null | Documentation plan JSON (sent when plan is ready) |
| `error_message` | string \| null | Error details if applicable |

#### `status_change`

Sent when generation reaches a terminal state (`ready`, `error`, or `aborted`).

```json
{
  "type": "status_change",
  "name": "my-repo",
  "branch": "main",
  "provider": "cursor",
  "model": "gpt-5.4-xhigh-fast",
  "owner": "alice",
  "status": "ready",
  "generation_id": "a1b2c3d4-...",
  "page_count": 12,
  "last_generated": "2026-06-08 12:00:00",
  "last_commit_sha": "abc1234"
}
```

| Field | Type | Description |
|---|---|---|
| `status` | string | One of: `ready`, `error`, `aborted` |
| `page_count` | integer \| null | Final page count |
| `last_generated` | string \| null | Timestamp (only for `ready`) |
| `last_commit_sha` | string \| null | Commit SHA |
| `error_message` | string \| null | Error details (for `error` or `aborted`) |

#### `ping`

Server-sent heartbeat every 30 seconds. Clients must respond with a `pong` message. After 2 missed pongs (within 10-second timeout each), the server closes the connection.

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

**Client pong response:**

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

### Message Visibility

WebSocket messages are scoped to the authenticated user:

- **Admins** receive updates for all projects
- **Project owners** receive updates for their own projects
- **Granted users** receive updates for projects they have access to

> **Note:** See [Generating Documentation](generating-docs.html) for details on monitoring generation progress through the web dashboard.

---

## Error Response Format

All API errors return a JSON body with a `detail` field:

```json
{
  "detail": "Human-readable error description"
}
```

Validation errors (422) from Pydantic return a structured `detail` array:

```json
{
  "detail": [
    {
      "loc": ["body", "repo_url"],
      "msg": "Invalid git repository URL: 'not-a-url'",
      "type": "value_error"
    }
  ]
}
```

### Common HTTP Status Codes

| Code | Meaning |
|---|---|
| 200 | Success |
| 202 | Accepted (generation started asynchronously) |
| 302 | Redirect (unauthenticated browser to login) |
| 400 | Bad request (invalid input) |
| 401 | Unauthorized (missing or invalid credentials) |
| 403 | Forbidden (insufficient permissions) |
| 404 | Not found (or not accessible to the current user) |
| 409 | Conflict (concurrent generation, multiple owners) |
| 422 | Validation error (request body schema mismatch) |
| 500 | Internal server error |

> **Note:** For security, endpoints return `404` instead of `403` when a resource exists but the user lacks access, preventing enumeration of other users' projects.

## Related Pages

- [CLI Command Reference](cli-reference.html)
- [Configuration Reference](configuration-reference.html)
- [Managing Users and Access Control](managing-users.html)
- [Generating Documentation](generating-docs.html)
- [Managing Projects and Variants](managing-projects.html)

---

Source: cli-reference.md

# CLI Command Reference

The `docsfy` CLI manages documentation generation, project lifecycle, and server administration from the terminal. It communicates with a running docsfy server over HTTP and WebSocket.

> **Note:** The CLI requires a running docsfy server. See [Getting Started with docsfy](quickstart.html) for initial setup, or [Using the CLI](using-the-cli.html) for profile configuration walkthrough.

## Global Options

Every `docsfy` command accepts these options. They apply **before** any subcommand.

| Option | Short | Type | Default | Description |
|---|---|---|---|---|
| `--server` | `-s` | `string` | Config default | Server profile name from `~/.config/docsfy/config.toml` |
| `--host` | | `string` | From profile | Server host (overrides profile URL) |
| `--port` | | `int` | `8000` | Server port (used with `--host`) |
| `--username` | `-u` | `string` | From profile | Username for authentication |
| `--password` | `-p` | `string` | From profile | API key for authentication |

**Resolution priority** (highest to lowest):

1. Explicit CLI flags (`--host`, `--port`, `--username`, `--password`)
2. Server profile specified by `--server`
3. Default server profile from config `[default].server`
4. Error if nothing is configured

```bash
# Use the default profile
docsfy list

# Use a named profile
docsfy --server prod list

# Override host and credentials inline
docsfy --host myserver.example.com --port 8000 -u admin -p <API_KEY> list
```

> **Tip:** Run `docsfy config init` to set up a profile so you don't need to pass credentials on every command. See [Configuration Reference](configuration-reference.html) for the config file format.

---

## `docsfy generate`

Generate documentation for a Git repository. Submits a generation request to the server and optionally watches progress in real time via WebSocket.

```
docsfy generate <REPO_URL> [OPTIONS]
```

### Arguments

| Argument | Type | Required | Description |
|---|---|---|---|
| `REPO_URL` | `string` | Yes | Git repository URL (HTTPS or SSH) |

### Options

| Option | Short | Type | Default | Description |
|---|---|---|---|---|
| `--branch` | `-b` | `string` | `main` | Git branch to generate docs from |
| `--provider` | | `string` | Server default | AI provider (`claude`, `gemini`, `cursor`) |
| `--model` | `-m` | `string` | Server default | AI model name |
| `--repo-type` | `-t` | `string` | Auto-detected | Repository type: `app`, `tests`, `library`, `framework` |
| `--force` | `-f` | `bool` | `false` | Force full regeneration, ignoring cache |
| `--watch` | `-w` | `bool` | `false` | Watch generation progress via WebSocket |

### Output

Prints project name, branch, status, and generation ID to stdout. With `--watch`, streams progress updates to stderr until generation completes or fails.

```
Project: my-project
Branch: main
Status: generating
Generation ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
```

### Examples

```bash
# Basic generation with defaults
docsfy generate https://github.com/org/repo

# Generate for a specific branch and provider
docsfy generate https://github.com/org/repo -b dev --provider claude -m claude-sonnet-4-20250514

# Force full regeneration and watch progress
docsfy generate https://github.com/org/repo -f -w

# Specify repository type explicitly
docsfy generate https://github.com/org/repo -t library

# SSH URL
docsfy generate git@github.com:org/repo.git -b release-1.x
```

> **Note:** Branch names cannot contain slashes. Use hyphens instead (e.g., `release-1.x` instead of `release/1.x`). Branch validation pattern: `^[a-zA-Z0-9][a-zA-Z0-9._-]*$`

### Exit Codes

| Code | Meaning |
|---|---|
| `0` | Generation submitted (or completed, with `--watch`) |
| `1` | Invalid repo type, generation error, WebSocket failure, or server error |

---

## `docsfy list`

List all projects visible to the authenticated user.

```
docsfy list [OPTIONS]
```

### Options

| Option | Type | Default | Description |
|---|---|---|---|
| `--status` | `string` | None | Filter by status: `ready`, `generating`, `error`, `aborted` |
| `--provider` | `string` | None | Filter by AI provider |
| `--json` | `bool` | `false` | Output as JSON instead of table |

### Output (table)

```
NAME          BRANCH  PROVIDER  MODEL                  STATUS  OWNER  PAGES  GEN ID
----------    ------  --------  ---------------------  ------  -----  -----  ------
my-project    main    cursor    gpt-5.4-xhigh-fast     ready   admin  12     a1b2c3d4-...
other-repo    dev     claude    claude-sonnet-4-20250514        ready   admin  8      e5f6a7b8-...
```

### Examples

```bash
# List all projects
docsfy list

# List only ready projects
docsfy list --status ready

# List projects using Claude
docsfy list --provider claude

# Get machine-readable output
docsfy list --json
```

### JSON Output Structure

```json
[
  {
    "name": "my-project",
    "branch": "main",
    "ai_provider": "cursor",
    "ai_model": "gpt-5.4-xhigh-fast",
    "status": "ready",
    "owner": "admin",
    "page_count": 12,
    "generation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }
]
```

---

## `docsfy status`

Show the status of a project and its variants. Accepts either a project name or a generation ID (UUID).

```
docsfy status <NAME> [OPTIONS]
```

### Arguments

| Argument | Type | Required | Description |
|---|---|---|---|
| `NAME` | `string` | Yes | Project name or generation ID (UUID) |

### Options

| Option | Short | Type | Default | Description |
|---|---|---|---|---|
| `--branch` | `-b` | `string` | None | Filter by branch |
| `--provider` | `-p` | `string` | None | Filter by provider |
| `--model` | `-m` | `string` | None | Filter by model |
| `--owner` | | `string` | None | Project owner (for admin disambiguation) |
| `--json` | | `bool` | `false` | Output as JSON |

### Behavior

- If `--branch`, `--provider`, and `--model` are all specified, returns a single variant's detail.
- Otherwise, returns all matching variants for the project.
- UUID arguments are resolved to project name/branch/provider/model via the server API.

### Output

```
Project: my-project
Variants: 2

  main/cursor/gpt-5.4-xhigh-fast
    ID:      a1b2c3d4-e5f6-7890-abcd-ef1234567890
    Status:  ready
    Owner:   admin
    Pages:   12
    Updated: 2026-06-08T10:30:00
    Commit:  abc12345

  dev/claude/claude-sonnet-4-20250514
    ID:      f9e8d7c6-b5a4-3210-fedc-ba0987654321
    Status:  generating
    Owner:   admin
    Stage:   generating_pages
```

### Examples

```bash
# Show all variants for a project
docsfy status my-project

# Show a specific variant
docsfy status my-project -b main -p cursor -m gpt-5.4-xhigh-fast

# Look up by generation ID
docsfy status a1b2c3d4-e5f6-7890-abcd-ef1234567890

# JSON output for scripting
docsfy status my-project --json
```

---

## `docsfy delete`

Delete a project or a specific variant. Requires confirmation unless `--yes` is passed.

```
docsfy delete <NAME> [OPTIONS]
```

### Arguments

| Argument | Type | Required | Description |
|---|---|---|---|
| `NAME` | `string` | Yes | Project name or generation ID (UUID) |

### Options

| Option | Short | Type | Default | Description |
|---|---|---|---|---|
| `--branch` | `-b` | `string` | None | Branch of variant to delete |
| `--provider` | `-p` | `string` | None | Provider of variant to delete |
| `--model` | `-m` | `string` | None | Model of variant to delete |
| `--owner` | | `string` | None | Project owner (required for admin) |
| `--all` | | `bool` | `false` | Delete all variants of the project |
| `--yes` | `-y` | `bool` | `false` | Skip confirmation prompt |

### Behavior

- To delete a **specific variant**: specify `--branch`, `--provider`, and `--model` together.
- To delete **all variants**: use `--all`.
- `--all` and `--branch`/`--provider`/`--model` are mutually exclusive.
- Without `--all` or the full variant triple, the command exits with an error.

### Examples

```bash
# Delete a specific variant
docsfy delete my-project -b main -p cursor -m gpt-5.4-xhigh-fast

# Delete all variants (skip confirmation)
docsfy delete my-project --all -y

# Delete by generation ID
docsfy delete a1b2c3d4-e5f6-7890-abcd-ef1234567890 -y

# Admin deleting another user's project
docsfy delete my-project --all --owner otheruser -y
```

### Exit Codes

| Code | Meaning |
|---|---|
| `0` | Deletion successful or aborted by user |
| `1` | Missing required options, invalid arguments, or server error |

---

## `docsfy abort`

Abort an active documentation generation.

```
docsfy abort <NAME> [OPTIONS]
```

### Arguments

| Argument | Type | Required | Description |
|---|---|---|---|
| `NAME` | `string` | Yes | Project name or generation ID (UUID) |

### Options

| Option | Short | Type | Default | Description |
|---|---|---|---|---|
| `--branch` | `-b` | `string` | None | Branch of variant to abort |
| `--provider` | `-p` | `string` | None | Provider of variant to abort |
| `--model` | `-m` | `string` | None | Model of variant to abort |
| `--owner` | | `string` | None | Project owner (required for admin) |

### Behavior

- Specify `--branch`, `--provider`, and `--model` together to abort a specific variant.
- Omit all three to abort by project name (server selects the active generation).
- Providing only some of the three variant selectors is an error.

### Examples

```bash
# Abort by project name
docsfy abort my-project

# Abort a specific variant
docsfy abort my-project -b main -p cursor -m gpt-5.4-xhigh-fast

# Abort by generation ID
docsfy abort a1b2c3d4-e5f6-7890-abcd-ef1234567890
```

---

## `docsfy download`

Download generated documentation as a `.tar.gz` archive or extract it directly to a directory.

```
docsfy download <NAME> [OPTIONS]
```

### Arguments

| Argument | Type | Required | Description |
|---|---|---|---|
| `NAME` | `string` | Yes | Project name or generation ID (UUID) |

### Options

| Option | Short | Type | Default | Description |
|---|---|---|---|---|
| `--branch` | `-b` | `string` | None | Branch of variant to download |
| `--provider` | `-p` | `string` | None | Provider of variant to download |
| `--model` | `-m` | `string` | None | Model of variant to download |
| `--owner` | | `string` | None | Project owner (for admin disambiguation) |
| `--output` | `-o` | `string` | None | Output directory to extract to |
| `--flatten` | | `bool` | `false` | Flatten extracted directory structure into output dir |

### Behavior

- Without `--output`: saves a `.tar.gz` file to the current directory.
- With `--output`: extracts the archive to the specified directory.
- `--flatten` moves all files from the archive's top-level subdirectory into `--output` directly. Requires `--output`.
- Specify `--branch`, `--provider`, and `--model` together for a specific variant, or omit all three for the default variant.

### Archive Naming

| Variant specified | Archive filename |
|---|---|
| No | `{name}-docs.tar.gz` |
| Yes | `{name}-{branch}-{provider}-{model}-docs.tar.gz` |

### Examples

```bash
# Download archive to current directory
docsfy download my-project

# Download a specific variant
docsfy download my-project -b main -p cursor -m gpt-5.4-xhigh-fast

# Extract to a directory
docsfy download my-project -o ./docs-output

# Extract and flatten (no nested subdirectory)
docsfy download my-project -o ./docs-output --flatten

# Download by generation ID
docsfy download a1b2c3d4-e5f6-7890-abcd-ef1234567890 -o ./docs
```

### Exit Codes

| Code | Meaning |
|---|---|
| `0` | Download or extraction successful |
| `1` | Missing required options, `--flatten` without `--output`, or server error |

---

## `docsfy models`

List available AI providers and their models from the server.

```
docsfy models [OPTIONS]
```

### Options

| Option | Short | Type | Default | Description |
|---|---|---|---|---|
| `--provider` | `-P` | `string` | None | Filter by a specific provider |
| `--json` | `-j` | `bool` | `false` | Output as JSON |

### Output

```
Provider: cursor (default)
  gpt-5.4-xhigh-fast  (default)
  gpt-4.1-mini

Provider: claude
  claude-sonnet-4-20250514

Provider: gemini
  gemini-2.5-pro
```

### Examples

```bash
# List all providers and models
docsfy models

# Show only Claude models
docsfy models -P claude

# JSON output
docsfy models --json
```

### JSON Output Structure

```json
{
  "providers": ["claude", "gemini", "cursor"],
  "default_provider": "cursor",
  "default_model": "gpt-5.4-xhigh-fast",
  "available_models": {
    "cursor": [{"id": "gpt-5.4-xhigh-fast"}, {"id": "gpt-4.1-mini"}],
    "claude": [{"id": "claude-sonnet-4-20250514"}],
    "gemini": [{"id": "gemini-2.5-pro"}]
  }
}
```

---

## `docsfy health`

Check if the docsfy server is reachable.

```
docsfy health
```

### Output

```
Server: https://myserver.example.com:8000
Status: ok
```

### Exit Codes

| Code | Meaning |
|---|---|
| `0` | Server is healthy |
| `1` | Server unreachable or returned non-JSON response |

### Example

```bash
docsfy --server prod health
```

---

## `docsfy config`

Manage CLI configuration profiles stored in `~/.config/docsfy/config.toml`. See [Configuration Reference](configuration-reference.html) for the full file format.

### `docsfy config init`

Interactive setup that creates a server profile.

```
docsfy config init
```

Prompts for:

| Prompt | Default | Description |
|---|---|---|
| Profile name | `dev` | Name for this server profile |
| Server URL | — | Full URL (e.g., `https://myserver.example.com:8000`) |
| Username | — | Your username |
| Password | — | Your API key (input hidden) |

If no default profile exists, the new profile is automatically set as the default.

```bash
$ docsfy config init
Profile name [dev]: prod
Server URL: https://docs.example.com:8000
Username: admin
Password: ****
Profile 'prod' saved to /home/user/.config/docsfy/config.toml
```

### `docsfy config show`

Display all server profiles with masked passwords.

```
docsfy config show
```

```
Config file: /home/user/.config/docsfy/config.toml
Default server: dev

[dev] (default)
  URL:      http://localhost:8000
  Username: admin
  Password: ad***

[prod]
  URL:      https://docs.example.com:8000
  Username: deployer
  Password: dk***
```

### `docsfy config set`

Set a single configuration value by dotted key path.

```
docsfy config set <KEY> <VALUE>
```

| Argument | Type | Required | Description |
|---|---|---|---|
| `KEY` | `string` | Yes | Dotted config key |
| `VALUE` | `string` | Yes | Value to set |

Valid key prefixes: `default.` and `servers.`

```bash
# Change the default profile
docsfy config set default.server prod

# Update a profile URL
docsfy config set servers.dev.url http://localhost:9000

# Update credentials
docsfy config set servers.prod.username deployer
docsfy config set servers.prod.password <API_KEY>
```

> **Warning:** The config file stores credentials in plain text with `0600` permissions. The directory is set to `0700`. Do not commit this file to version control.

---

## `docsfy admin`

Administrative commands for user and access management. Requires admin-level authentication.

### `docsfy admin users list`

List all users on the server.

```
docsfy admin users list [OPTIONS]
```

| Option | Type | Default | Description |
|---|---|---|---|
| `--json` | `bool` | `false` | Output as JSON |

**Table output:**

```
USERNAME  ROLE   CREATED
--------  -----  -------------------
admin     admin  2026-06-01T00:00:00
alice     user   2026-06-05T14:30:22
bob       viewer 2026-06-07T09:15:00
```

### `docsfy admin users create`

Create a new user account.

```
docsfy admin users create <USERNAME> [OPTIONS]
```

| Argument / Option | Short | Type | Default | Description |
|---|---|---|---|---|
| `USERNAME` (arg) | | `string` | — | Username to create |
| `--role` | `-r` | `string` | `user` | User role: `admin`, `user`, `viewer` |
| `--json` | | `bool` | `false` | Output as JSON |

```bash
$ docsfy admin users create alice --role user
User created: alice
Role: user
API Key: dk_a1b2c3d4e5f6...

Save this API key -- it will not be shown again.
```

> **Warning:** The API key is displayed only once. Store it securely.

### `docsfy admin users delete`

Delete a user account.

```
docsfy admin users delete <USERNAME> [OPTIONS]
```

| Argument / Option | Short | Type | Default | Description |
|---|---|---|---|---|
| `USERNAME` (arg) | | `string` | — | Username to delete |
| `--yes` | `-y` | `bool` | `false` | Skip confirmation prompt |

```bash
docsfy admin users delete alice -y
```

### `docsfy admin users rotate-key`

Rotate a user's API key. Generates a new key (or sets a custom one) and invalidates the old key.

```
docsfy admin users rotate-key <USERNAME> [OPTIONS]
```

| Argument / Option | Type | Default | Description |
|---|---|---|---|
| `USERNAME` (arg) | `string` | — | Username whose key to rotate |
| `--new-key` | `string` | Auto-generated | Custom API key (generated if omitted) |
| `--json` | `bool` | `false` | Output as JSON |

```bash
$ docsfy admin users rotate-key alice
User: alice
New API Key: dk_f6e5d4c3b2a1...

Save this API key -- it will not be shown again.
```

---

### `docsfy admin access list`

List users who have been granted access to a project.

```
docsfy admin access list <PROJECT> --owner <OWNER> [OPTIONS]
```

| Argument / Option | Type | Required | Default | Description |
|---|---|---|---|---|
| `PROJECT` (arg) | `string` | Yes | — | Project name |
| `--owner` | `string` | Yes | — | Project owner |
| `--json` | `bool` | No | `false` | Output as JSON |

```bash
$ docsfy admin access list my-project --owner admin
Project: my-project
Owner: admin
Users with access: alice, bob
```

### `docsfy admin access grant`

Grant a user access to all variants of a project.

```
docsfy admin access grant <PROJECT> --username <USER> --owner <OWNER>
```

| Argument / Option | Type | Required | Description |
|---|---|---|---|
| `PROJECT` (arg) | `string` | Yes | Project name |
| `--username` | `string` | Yes | Username to grant access |
| `--owner` | `string` | Yes | Project owner |

```bash
docsfy admin access grant my-project --username alice --owner admin
```

### `docsfy admin access revoke`

Revoke a user's access to a project.

```
docsfy admin access revoke <PROJECT> --username <USER> --owner <OWNER>
```

| Argument / Option | Type | Required | Description |
|---|---|---|---|
| `PROJECT` (arg) | `string` | Yes | Project name |
| `--username` | `string` | Yes | Username to revoke access |
| `--owner` | `string` | Yes | Project owner |

```bash
docsfy admin access revoke my-project --username alice --owner admin
```

---

## Generation ID Resolution

Many commands accept a **generation ID** (UUID) in place of a project name. When a UUID is detected, the CLI resolves it to the full variant coordinates (name, branch, provider, model, owner) via the `/api/projects/by-id/{id}` endpoint.

UUID format: canonical hyphenated form `8-4-4-4-12` (e.g., `a1b2c3d4-e5f6-7890-abcd-ef1234567890`).

Commands that support generation ID lookup: `status`, `delete`, `abort`, `download`.

```bash
# These are equivalent (if the UUID maps to my-project/main/cursor/gpt-5.4-xhigh-fast)
docsfy status a1b2c3d4-e5f6-7890-abcd-ef1234567890
docsfy status my-project -b main -p cursor -m gpt-5.4-xhigh-fast
```

> **Tip:** Copy the generation ID from `docsfy generate` or `docsfy list` output to use with other commands.

---

## Output Formats

All listing and status commands support two output formats:

| Format | Flag | Description |
|---|---|---|
| Table | *(default)* | Human-readable, column-aligned table |
| JSON | `--json` | Machine-readable JSON for scripting and piping |

```bash
# Pipe JSON output to jq
docsfy list --json | jq '.[].name'

# Use in shell scripts
STATUS=$(docsfy status my-project --json | jq -r '.variants[0].status')
```

---

## Common Exit Codes

| Code | Meaning |
|---|---|
| `0` | Success |
| `1` | Error (invalid input, server error, authentication failure, connection refused) |

All error messages are printed to **stderr**. Successful data output goes to **stdout**, making it safe to pipe output while still seeing errors.

---

## Related Pages

- [Using the CLI](using-the-cli.html) — Setup walkthrough, authentication, and workflow examples
- [Configuration Reference](configuration-reference.html) — Config file format, environment variables, and server settings
- [REST API Reference](api-reference.html) — The HTTP endpoints that the CLI communicates with
- [Managing Projects and Variants](managing-projects.html) — Conceptual overview of projects, branches, and variants
- [Managing Users and Access Control](managing-users.html) — User roles and access control concepts
- [Configuring AI Providers](configuring-ai-providers.html) — Available providers, models, and selection details

## Related Pages

- [Using the CLI](using-the-cli.html)
- [REST API Reference](api-reference.html)
- [Configuration Reference](configuration-reference.html)
- [Managing Users and Access Control](managing-users.html)
- [Managing Projects and Variants](managing-projects.html)

---

Source: configuration-reference.md

# Configuration Reference

This page documents every configuration knob for the docsfy server, CLI, and Docker deployment. Each setting lists its name, type, default, valid values, and a concrete example.

## Server Environment Variables

The docsfy server reads its configuration from environment variables (or an `.env` file in the working directory). Settings are loaded by `pydantic_settings` — variable names are **case-insensitive**.

| Variable | Type | Default | Description |
|---|---|---|---|
| `ADMIN_KEY` | `string` | *(none — required)* | Master admin password. Must be at least 16 characters. Used for admin login, API key HMAC hashing, and initial authentication. |
| `AI_PROVIDER` | `string` | `cursor` | Default AI provider for documentation generation. |
| `AI_MODEL` | `string` | `gpt-5.4-xhigh-fast` | Default AI model for documentation generation. |
| `AI_CLI_TIMEOUT` | `integer` | `60` | Timeout in seconds for each AI CLI call. Must be greater than 0. |
| `LOG_LEVEL` | `string` | `INFO` | Python logging level. |
| `DATA_DIR` | `string` | `/data` | Root directory for the SQLite database and generated documentation files. |
| `SECURE_COOKIES` | `boolean` | `true` | Set session cookies with the `Secure` flag. Set to `false` for local HTTP development. |
| `MAX_CONCURRENT_PAGES` | `integer` | `10` | Maximum number of AI calls to run in parallel during page generation and validation. Must be greater than 0. |
| `PORT` | `integer` | `8000` | TCP port the uvicorn server listens on. |
| `HOST` | `string` | `127.0.0.1` | Bind address for the uvicorn server. |
| `DEBUG` | `string` | *(unset)* | When `true`, starts uvicorn with `--reload` for hot reloading. |
| `SIDECAR_PORT` | `integer` | `9100` | TCP port the Pi SDK HTTP sidecar listens on. Set by the container entrypoint. |
| `DEV_MODE` | `string` | *(unset)* | When `true`, the entrypoint installs frontend dependencies, starts a Vite dev server on port `5173`, recompiles the sidecar TypeScript, and runs uvicorn with `--reload`. |

### Example `.env` file

```env
ADMIN_KEY=my-very-long-secret-key-here
AI_PROVIDER=cursor
AI_MODEL=gpt-5.4-xhigh-fast
AI_CLI_TIMEOUT=60
LOG_LEVEL=INFO
DATA_DIR=/data
SECURE_COOKIES=true
MAX_CONCURRENT_PAGES=10
```

> **Warning:** `ADMIN_KEY` is required. The server will exit immediately at startup if it is empty or shorter than 16 characters.


> **Note:** Rotating `ADMIN_KEY` invalidates all existing user API key hashes. Every user must regenerate their API key after an `ADMIN_KEY` change.

### Valid AI Providers

| Provider | Value |
|---|---|
| Claude | `claude` |
| Gemini | `gemini` |
| Cursor | `cursor` |

See [Configuring AI Providers](configuring-ai-providers.html) for provider setup details.

### Valid Log Levels

`DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`

## Data Directory Layout

All persistent state lives under `DATA_DIR` (default `/data`).

```
<DATA_DIR>/
├── docsfy.db                  # SQLite database
└── projects/
    └── <owner>/
        └── <project>/
            └── <branch>/
                └── <provider>/
                    └── <model>/
                        ├── cache/pages/   # Cached page markdown
                        └── site/          # Rendered HTML site
```

- `<owner>` is the username of the project creator (`_default` when empty).
- `<branch>` defaults to `main`.
- `<provider>` and `<model>` identify the AI variant used for generation.

## CLI Configuration File

The CLI stores server profiles in a TOML file at:

```
~/.config/docsfy/config.toml
```

The file is created with permissions `600` (owner read/write only).

### File Structure

```toml
# Default server profile to use when --server is not specified
[default]
server = "dev"

# Server profiles — add as many as needed
[servers.<profile-name>]
url = "<server-url>"
username = "<username>"
password = "<api-key>"
```

### Keys Reference

| Key | Type | Required | Description |
|---|---|---|---|
| `default.server` | `string` | No | Name of the profile to use by default when `--server` is not passed. |
| `servers.<name>.url` | `string` | Yes | Full URL of the docsfy server (e.g. `https://docsfy.example.com`). |
| `servers.<name>.username` | `string` | Yes | Username for authentication. |
| `servers.<name>.password` | `string` | Yes | API key or admin password. |

> **Tip:** Valid key prefixes for `docsfy config set` are `default.` and `servers.` only.

### Example

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

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

[servers.prod]
url = "https://docsfy.example.com"
username = "admin"
password = "<your-prod-key>"
```

### CLI Config Commands

#### `docsfy config init`

Interactive setup that prompts for profile name, server URL, username, and password. Creates the config file if it doesn't exist, or adds a new profile.

```bash
docsfy config init
```

```
Profile name [dev]: prod
Server URL: https://docsfy.example.com
Username: admin
Password: ********
Profile 'prod' saved to /home/user/.config/docsfy/config.toml
```

#### `docsfy config show`

Displays all profiles with masked passwords.

```bash
docsfy config show
```

```
Config file: /home/user/.config/docsfy/config.toml
Default server: dev

[dev] (default)
  URL:      http://localhost:8000
  Username: admin
  Password: my***
```

#### `docsfy config set`

Set a single configuration key.

```bash
docsfy config set default.server prod
docsfy config set servers.dev.url https://new-server.com
docsfy config set servers.staging.password new-api-key
```

See [Using the CLI](using-the-cli.html) for the full CLI setup workflow and [CLI Command Reference](cli-reference.html) for all commands.

### CLI Connection Resolution

When the CLI connects to a server, parameters are resolved in this priority order (highest first):

1. Explicit CLI flags (`--host`, `--port`, `--username`, `--password`)
2. Named server profile from `--server` flag
3. Default server profile from `[default].server` in config
4. Error if nothing is configured

| CLI Flag | Short | Type | Default | Description |
|---|---|---|---|---|
| `--server` | `-s` | `string` | *(from config)* | Server profile name to use. |
| `--host` | | `string` | *(from profile)* | Server hostname. Overrides the profile URL. |
| `--port` | | `integer` | `8000` | Server port. Used with `--host`. |
| `--username` | `-u` | `string` | *(from profile)* | Username for authentication. |
| `--password` | `-p` | `string` | *(from profile)* | API key or password. |

```bash
# Use config default
docsfy list

# Use a named profile
docsfy --server prod list

# Override host and port
docsfy --host myserver --port 9000 -u admin -p my-key list
```

> **Note:** When `--host` is used with a profile, the scheme is preserved from the profile URL. Without a profile, the scheme defaults to `https`.

## Docker Compose Configuration

### Compose File Reference

```yaml
services:
  docsfy:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8000:8000"
      # - "5173:5173"  # Uncomment for DEV_MODE
    volumes:
      - ./data:/data
      # - ./frontend:/app/frontend  # Uncomment for frontend hot reload
    env_file:
      - .env
    environment:
      - ADMIN_KEY=${ADMIN_KEY}
      # - DEV_MODE=true
    restart: unless-stopped
```

### Compose Options

| Key | Type | Default | Description |
|---|---|---|---|
| `services.docsfy.ports` | `list` | `["8000:8000"]` | Port mappings. Add `5173:5173` when `DEV_MODE=true`. |
| `services.docsfy.volumes` | `list` | `["./data:/data"]` | Persistent storage. Add `./frontend:/app/frontend` for development. |
| `services.docsfy.env_file` | `list` | `[".env"]` | Path to the environment file. |
| `services.docsfy.environment.ADMIN_KEY` | `string` | `${ADMIN_KEY}` | Admin key passed from shell or `.env`. |
| `services.docsfy.environment.DEV_MODE` | `string` | *(commented)* | Set to `true` for development mode. |
| `services.docsfy.restart` | `string` | `unless-stopped` | Container restart policy. |

### Container DEV_MODE

When `DEV_MODE=true`, the container entrypoint:

1. Runs `npm ci` and starts the Vite dev server on port `5173`
2. Recompiles sidecar TypeScript from source
3. Starts uvicorn with `--reload --reload-dir /app/src`

```yaml
services:
  docsfy:
    ports:
      - "8000:8000"
      - "5173:5173"
    volumes:
      - ./data:/data
      - ./frontend:/app/frontend
    environment:
      - ADMIN_KEY=${ADMIN_KEY}
      - DEV_MODE=true
```

> **Note:** When `DEV_MODE` is not `true`, the container serves the prebuilt frontend from `frontend/dist`.

See [Deploying with Docker](deployment.html) for production deployment patterns.

### Container Ports

| Port | Protocol | Default | Description |
|---|---|---|---|
| `8000` | TCP | `8000` | FastAPI server (uvicorn). Controlled by `PORT` env var. |
| `5173` | TCP | `5173` | Vite development server. Only active when `DEV_MODE=true`. |
| `9100` | TCP | `9100` | Pi SDK sidecar (internal). Controlled by `SIDECAR_PORT` env var. |

### Health Check

The container health check probes both the server and the sidecar:

```
curl -f http://localhost:${PORT:-8000}/health
curl -f http://localhost:${SIDECAR_PORT:-9100}/health
```

- **Interval:** 30 seconds
- **Timeout:** 10 seconds
- **Start period:** 30 seconds
- **Retries:** 3

## Session and Authentication Constants

These values are compiled into the server and cannot be changed via environment variables.

| Constant | Value | Description |
|---|---|---|
| Session TTL | `28800` seconds (8 hours) | Duration before a session cookie expires. |
| Minimum API key length | `16` characters | Minimum length for `ADMIN_KEY` and user API keys. |
| Session cookie name | `docsfy_session` | Name of the HTTP-only session cookie. |
| Cookie `SameSite` | `strict` | Cookie SameSite policy. |
| Cookie `HttpOnly` | `true` | Cookie cannot be accessed by JavaScript. |
| API key prefix | `docsfy_` | Generated API keys start with this prefix. |
| Valid user roles | `admin`, `user`, `viewer` | Assignable roles for user accounts. |

See [Managing Users and Access Control](managing-users.html) for role descriptions.

## Git Operation Timeouts

Internal timeouts for git subprocess calls. These are compiled constants in `repository.py`.

| Operation | Timeout | Description |
|---|---|---|
| Clone | 300 seconds | `git clone --depth 1` of a remote repository. |
| Fetch | 120 seconds | `git fetch --depth=1` to deepen a shallow clone for diffing. |
| Diff | 60 seconds | `git diff --stat --patch` between two commits. |
| Name listing | 30 seconds | `git diff --name-only` to list changed files. |
| Cat-file / rev-parse | 10 seconds | `git cat-file`, `git rev-parse` for commit SHA and branch detection. |

## GenerateRequest Defaults

When submitting a generation request (via API or CLI), these defaults apply to omitted fields.

| Field | Type | Default | Valid Values | Description |
|---|---|---|---|---|
| `ai_provider` | `string` | Server default (`cursor`) | `claude`, `gemini`, `cursor` | AI provider. |
| `ai_model` | `string` | Server default (`gpt-5.4-xhigh-fast`) | *(provider-specific)* | AI model. |
| `ai_cli_timeout` | `integer` | `60` | > 0 | Per-call AI timeout in seconds. |
| `branch` | `string` | `main` | `^[a-zA-Z0-9][a-zA-Z0-9._-]*$` | Git branch to generate docs from. Slashes are **not** allowed. |
| `force` | `boolean` | `false` | `true`, `false` | Force full regeneration, ignoring cache. |
| `repo_type` | `string` | *(auto-detected)* | `app`, `tests`, `library`, `framework` | Repository type hint for prompt selection. |

> **Warning:** Branch names cannot contain `/` characters. Use hyphens instead (e.g. `release-1.x` rather than `release/1.x`).

See [Generating Documentation](generating-docs.html) for generation workflows and [REST API Reference](api-reference.html) for the `POST /api/generate` endpoint.

## Generation Stages

A generation run progresses through these stages in order. The `current_stage` field reflects the active stage in status responses and WebSocket messages.

| Stage | Description |
|---|---|
| `cloning` | Cloning the git repository (or reading the local path). |
| `analyzing` | Building the code knowledge graph with Graphify. |
| `planning` | AI plans the documentation structure (full generation). |
| `incremental_planning` | AI plans which pages need updates (incremental generation). |
| `generating_pages` | AI generates markdown content for each page. |
| `validating` | AI validates pages for stale references and completeness. |
| `completeness_check` | Verifying all planned pages were generated. |
| `cross_linking` | AI adds cross-reference links between related pages. |
| `rendering` | Converting markdown to HTML and building the static site. |

## Prompt Constraints

Internal limits applied during AI prompt construction.

| Constant | Value | Description |
|---|---|---|
| Max diff length | 30,000 characters | Diff content is truncated beyond this limit before being passed to the incremental planner. |
| Max file char cap (code graph) | 20,000 characters | Per-file character limit during code graph semantic extraction. |

## WebSocket Constants

Internal constants for the WebSocket connection (`/api/ws`).

| Constant | Value | Description |
|---|---|---|
| Heartbeat interval | 30 seconds | Server sends a ping frame at this interval. |
| Pong timeout | 10 seconds | Maximum wait for a pong response before marking a missed pong. |
| Max missed pongs | 2 | Connection is closed after this many consecutive missed pongs. |

## Frontend Constants

Client-side timing constants defined in `frontend/src/lib/constants.ts`.

| Constant | Value | Description |
|---|---|---|
| `TOAST_DEFAULT_MS` | 4000 ms | Default toast notification duration. |
| `TOAST_ERROR_MS` | 6000 ms | Error toast notification duration. |
| `WS_HEARTBEAT_INTERVAL_MS` | 30000 ms | Client-side WebSocket heartbeat interval. |
| `WS_RECONNECT_MAX_DELAY_MS` | 30000 ms | Maximum delay between WebSocket reconnection attempts. |
| `WS_POLLING_FALLBACK_MS` | 10000 ms | Polling interval when WebSocket is unavailable. |
| `SIDEBAR_MIN_WIDTH` | 180 px | Minimum sidebar width in the dashboard. |
| `SIDEBAR_MAX_WIDTH` | 500 px | Maximum sidebar width in the dashboard. |
| `SIDEBAR_DEFAULT_WIDTH` | 256 px | Default sidebar width in the dashboard. |

## Project Database Schema

The composite primary key for project variants:

```
PRIMARY KEY (name, branch, ai_provider, ai_model, owner)
```

| Column | Type | Default | Description |
|---|---|---|---|
| `name` | `TEXT` | *(required)* | Repository name. |
| `branch` | `TEXT` | `main` | Git branch. |
| `ai_provider` | `TEXT` | `''` | AI provider used. |
| `ai_model` | `TEXT` | `''` | AI model used. |
| `owner` | `TEXT` | `''` | Username of the project creator. |
| `generation_id` | `TEXT` | *(auto-generated UUID)* | Unique identifier for the generation run. |
| `repo_url` | `TEXT` | *(required)* | Git repository URL. |
| `status` | `TEXT` | `generating` | Current status: `generating`, `ready`, `error`, `aborted`. |
| `current_stage` | `TEXT` | `NULL` | Active generation stage (see Generation Stages above). |
| `last_commit_sha` | `TEXT` | `NULL` | SHA of the commit used for the last generation. |
| `last_generated` | `TIMESTAMP` | `NULL` | Timestamp of last successful generation. |
| `page_count` | `INTEGER` | `0` | Number of generated documentation pages. |
| `error_message` | `TEXT` | `NULL` | Error description when status is `error`. |
| `plan_json` | `TEXT` | `NULL` | JSON-serialized documentation plan. |
| `repo_type` | `TEXT` | `NULL` | Detected or specified repository type. |
| `total_cost_usd` | `REAL` | `NULL` | Total AI generation cost in USD. |

## Related Pages

- [Deploying with Docker](deployment.html)
- [Configuring AI Providers](configuring-ai-providers.html)
- [CLI Command Reference](cli-reference.html)
- [Using the CLI](using-the-cli.html)
- [Generating Documentation](generating-docs.html)

---
