# Cairn — Deploy

## Prerequisites

- macOS or Linux
- Python ≥ 3.12
- Docker

## Quick Start (Docker Compose)

```bash
# Pull worker container image
docker pull --platform=linux/amd64 ghcr.io/oritera/cairn-worker-container:latest

# Pull base image for building Cairn
docker pull ghcr.io/astral-sh/uv:python3.13-trixie

# Configure dispatcher
cp dispatch.example.yaml dispatch.yaml
# Edit dispatch.yaml — fill in LLM endpoints and API keys

# Start
docker compose up --build
```

This starts `cairn-server` on port 8000 and `cairn-dispatcher` (waits for server health check). Data persisted to `./datas/cairn/`.

## Manual Setup

```bash
# Start server
uv run --project cairn cairn serve

# Run dispatcher
uv run --project cairn cairn dispatch --config dispatch.yaml

# Healthcheck only
uv run --project cairn cairn dispatch --config dispatch.yaml --startup-healthcheck-only
```

## Docker Compose Services

```yaml
services:
  cairn-server:
    build: .
    command: ["serve"]
    ports: ["8000:8000"]
    volumes: ["./datas/cairn:/root/.local/share/cairn"]

  cairn-dispatcher:
    build: .
    command: ["dispatch", "--config", "/config/dispatch.yaml"]
    depends_on:
      cairn-server:
        condition: service_healthy
    volumes:
      - ./dispatch.yaml:/config/dispatch.yaml:ro
      - /var/run/docker.sock:/var/run/docker.sock
```

## Configuration

`dispatch.yaml` structure:

| Section | Key fields |
|---------|-----------|
| `server` | Server URL (e.g., `http://cairn-server:8000`) |
| `runtime` | `max_workers`, `max_running_projects`, `max_project_workers`, `interval`, `healthcheck_timeout`, `worker_healthcheck`, `prompt_group` |
| `tasks` | `bootstrap.timeout/conclude_timeout`, `reason.timeout/max_intents`, `explore.timeout/conclude_timeout` |
| `container` | `image`, `network_mode`, `completed_action`, `cap_add` |
| `common_env` | Shared env vars merged into all workers |
| `workers[]` | Per-worker: `name`, `type`, `task_types`, `max_running`, `priority`, `env` |

## Tests

```bash
# Run fast regression suite (no Docker, no live LLM)
uv run --project cairn --group dev pytest
```

10 test files covering: config validation, container archives, contracts, DB migrations, mock end-to-end, protocol/startup, runtime logic, scheduler logic, server API, worker tasks.

## CI

GitHub Actions workflow builds and publishes the container image to GHCR on push.

## Environment Variables

Worker env vars are configured in `dispatch.yaml` under each worker's `env` section:

| Worker Type | Required Env Keys |
|------------|-------------------|
| claudecode | `ANTHROPIC_MODEL`, `ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN` |
| codex | `CODEX_MODEL`, `CODEX_BASE_URL`, `OPENAI_API_KEY` |
| pi | `PI_MODEL`, `PI_BASE_URL`, `PI_API_KEY`, `PI_PROVIDER_API` |

## Data Persistence

SQLite database stored at `~/.local/share/cairn/cairn.db` by default. In Docker, mounted via volume to `./datas/cairn/`.
