> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crucihil.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Self-hosting

> Run the full CruciHiL stack on your own infrastructure with Docker Compose

Self-hosted CruciHiL is free and has **no rig limits** — the open source stack never enforces quotas on your own server. Register as many rigs as your benches need.

## What the stack is

Docker Compose runs three services plus an optional virtual agent:

| Service  | What it is                                                            | Port                           |
| -------- | --------------------------------------------------------------------- | ------------------------------ |
| `db`     | PostgreSQL 16 — run history, rigs, results                            | internal (5433 exposed in dev) |
| `server` | FastAPI control plane — REST API, agent WebSocket, Alembic migrations | 8000                           |
| `mcp`    | FastMCP server (SSE transport) — the AI interface                     | 8001                           |
| `agent`  | Optional virtual rig agent (compose profile `agent`)                  | —                              |

The React dashboard (port 5173) runs natively via `./dev.sh` (Vite HMR) or as a container through the development override compose file. Real rig agents run natively on your bench machines, not in Docker — they connect outbound to the server over WebSocket.

## Prerequisites

* Docker and Docker Compose (v2 plugin or v1 standalone — both are detected)
* `curl` or `wget`, and `python3` (used by the bootstrap script)

## Bootstrap with `setup.sh`

From the project root:

```bash theme={null}
./setup.sh
```

One command does the full bootstrap, and it is idempotent — safe to re-run:

1. Validates prerequisites (docker, docker compose)
2. Creates `.env` from `.env.example` and generates `SECRET_KEY` and `REGISTRATION_TOKEN` automatically
3. Starts `db` + `server` and waits for health (Alembic migrations run on startup)
4. Registers a `default-rig` and saves its API key to `.env` as `CRUCIHIL_API_KEY`
5. Starts all services (control plane + MCP server)

When it finishes:

* Control plane: `http://localhost:8000` (API docs at `/docs`)
* MCP server: `http://localhost:8001/sse`

Other modes:

```bash theme={null}
./setup.sh --status        # service health + rig connectivity
./setup.sh --restart       # restart all services without re-registration
./setup.sh --clean         # stop and remove containers (volumes kept)
```

<Warning>
  `setup.sh` warns if `POSTGRES_PASSWORD` is still `changeme`. Change it in `.env` before exposing the server beyond localhost.
</Warning>

## Environment variables

All configuration lives in `.env` (created from `.env.example`):

<ParamField path="POSTGRES_USER" type="string">
  PostgreSQL username, used by the `db` service. Default: `crucihil`.
</ParamField>

<ParamField path="POSTGRES_PASSWORD" type="string">
  PostgreSQL password. Must be changed from the default `changeme` for production.
</ParamField>

<ParamField path="POSTGRES_DB" type="string">
  Database name. Default: `crucihil`.
</ParamField>

<ParamField path="DATABASE_URL" type="string">
  Full connection string used by the server and Alembic, e.g. `postgresql://crucihil:changeme@db:5432/crucihil`. The hostname must match the compose service name `db`.
</ParamField>

<ParamField path="SECRET_KEY" type="string">
  JWT signing secret. Generated automatically by `setup.sh` (or create one with `python -c "import secrets; print(secrets.token_hex(32))"`). Must be changed before production use.
</ParamField>

<ParamField path="REGISTRATION_TOKEN" type="string">
  Static token required to register new rigs (`POST /api/v1/rigs`). Generated automatically by `setup.sh`. Keep it private.
</ParamField>

<ParamField path="CRUCIHIL_API_KEY" type="string">
  API key the MCP server uses to authenticate to the control plane. Written by `setup.sh` after it registers the default rig.
</ParamField>

## First org and admin — the one-shot `/setup` bootstrap

The dashboard and API use org-scoped users. Create the first organisation and admin account with a single call:

```bash theme={null}
curl -X POST http://localhost:8000/api/v1/setup \
  -H 'Content-Type: application/json' \
  -d '{"org_name":"Acme","admin_email":"you@company.com","admin_password":"strong-password"}'
```

It returns a JWT and the org slug. Log in to the dashboard with the same email and password. The endpoint only succeeds while no organisation exists — every subsequent call returns `409` to prevent accidental re-initialisation. Admins can then invite team members from **Settings → Team → Invite member**.

## Local development — `dev.sh`

For working on CruciHiL itself: backend in Docker, dashboard natively with Vite HMR.

```bash theme={null}
./dev.sh                             # backend (db + server + mcp) + dashboard at localhost:5173
./dev.sh --agent                     # also start the Docker virtual rig agent
./dev.sh --rig rigs/my_bench.toml    # register + start a native agent for a TOML (repeatable)
./dev.sh --backend                   # backend only
./dev.sh --dashboard                 # dashboard only (backend must already be running)
./dev.sh --status                    # container health + rig connectivity
./dev.sh --stop                      # stop containers + kill native agents
```

The Docker virtual agent (rigs/virtual.toml) can also be started directly:

```bash theme={null}
docker compose --profile agent up -d
```

## Installing agents on bench machines

Each HiL bench machine runs a native agent that connects outbound to your control plane. Register the rig first (via `POST /api/v1/rigs` with the `REGISTRATION_TOKEN`, the dashboard, or the `register_rig` MCP tool) to get its API key, then install:

```bash theme={null}
sudo ./scripts/install-agent.sh \
  --rig rigs/my_bench.toml \
  --server https://your-server.example.com \
  --key <api-key>
```

The installer (requires Python 3.11+, systemd, and sudo):

1. Creates a `crucihil` system user and installs the package into a venv at `/opt/crucihil`
2. Copies the rig TOML to `/opt/crucihil/rigs/<name>.toml`
3. Writes `CRUCIHIL_BASE_URL` and `CRUCIHIL_API_KEY` to `/etc/crucihil/<name>.env` (chmod 600)
4. Installs the systemd template unit `crucihil-agent@.service`
5. Enables and starts `crucihil-agent@<name>`

Optional flags: `--dir` (install directory, default `/opt/crucihil`) and `--user` (service user, default `crucihil`).

Manage the service afterwards:

```bash theme={null}
systemctl status crucihil-agent@my_bench          # check status
journalctl -u crucihil-agent@my_bench -f          # follow logs
systemctl stop crucihil-agent@my_bench            # stop
systemctl disable --now crucihil-agent@my_bench   # uninstall
```

The unit restarts the agent automatically on failure, backing off up to 5 minutes. The rig appears as **connected** in the dashboard within seconds of the agent starting.

<Info>
  Agents can also self-register: set `registration_token` in the rig TOML's `[rig.cloud]` section, and on first boot the agent exchanges it for a permanent API key stored in `~/.crucihil/credentials.toml`. The `CRUCIHIL_BASE_URL` / `CRUCIHIL_API_KEY` environment variables take precedence over `[rig.cloud]`. See [Rig Configuration](/rig-config/overview).
</Info>

## Connecting an AI client

Point any MCP client at your self-hosted MCP server. For Claude Desktop, add to `claude_desktop_config.json`:

```json theme={null}
{ "mcpServers": { "crucihil": { "url": "http://localhost:8001/sse" } } }
```

See [MCP Overview](/mcp/overview) for the full tool list and configuration options.

## Deploying to Fly.io + Vercel

The repo ships Fly configs for the two backend services:

```bash theme={null}
fly deploy --config fly.server.toml   # control plane
fly deploy --config fly.mcp.toml      # MCP server
# Dashboard auto-deploys to Vercel on push to main
```

The MCP app's `SECRET_KEY` must match the control plane — set it with `fly secrets set SECRET_KEY=... --app crucihil-mcp`.

## See also

* [Installation](/installation) — installing the `crucihil` package
* [Quickstart](/quickstart) — first test against the virtual rig
* [Rig Configuration](/rig-config/overview) — the `[rig.cloud]` section and agent registration
* [`crucihil agent`](/cli/agent) — running the agent by hand
