Callahan CI handbook.
A practical guide to running Callahan. Read top-to-bottom on first install; come back later for the reference sections.
Introduction
Callahan is a CI/CD tool that runs on your machine. A Go binary handles the API, pipeline execution, and webhook intake. A Next.js dashboard is the UI. State is one SQLite file. Four AI features — Architect, Debugger, Reviewer, Analyst — run on each build.
Go 1.22 backend · Next.js 15 dashboard · SQLite (WAL) · GitHub-Actions-compatible YAML with an ai: extension block.
Installation
Docker (quickest)
Prerequisites: Docker 24+. Everything runs on a single port — no separate frontend dev server needed.
git clone https://github.com/Callahan-ci/Callahan
cd Callahan
cp .env.example .env
# Edit .env — set ANTHROPIC_API_KEY or another LLM key
docker compose up --build
Open http://localhost:8080. The Go binary serves both the API and the bundled Next.js static files.
From source
Requires Go 1.22+ and Node 18+. Gives you hot-reload on both processes.
git clone https://github.com/Callahan-ci/Callahan
cd Callahan
# start backend (:8080) and dashboard (:3000)
./start.sh dev
Stop with Ctrl+C — the script's trap kills both processes. For two-terminal manual mode, run go run ./cmd/callahan in one and npm run dev in frontend/ in the other.
Your first build
Open http://localhost:3000. Click the + in the sidebar, paste your repo URL, and add a personal access token. Callahan auto-detects the language from go.mod, package.json, Cargo.toml, etc., and offers a starter Callahanfile.
Click Run Build. Logs stream live over WebSocket. If a step fails, the AI Explain Failure button on the failed job returns a plain-English root cause.
Callahanfile.yaml
Place a Callahanfile.yaml at the repo root. The schema is GitHub-Actions-compatible — most existing workflows run as-is. The top-level ai: and deploy: blocks are Callahan extensions.
name: my-app on: [push, pull_request] jobs: test: runs-on: local steps: - name: Test run: go test ./... build: runs-on: local needs: [test] steps: - name: Build run: go build ./cmd/myapp ai: review: true # AI code review on every build security-scan: true # Trivy/Semgrep + AI fallback explain-failures: true # AI Explain button on failed jobs deploy: - name: dev auto: true steps: - run: ./scripts/deploy.sh
Webhooks
Trigger builds from GitHub or GitLab by pointing a webhook at:
# GitHub POST https://your-host:8080/api/v1/webhook/github # GitLab POST https://your-host:8080/api/v1/webhook/gitlab
For GitHub, set the secret in the webhook config — Callahan verifies X-Hub-Signature-256 with HMAC-SHA256. For GitLab, set the same value as the project's webhook token (it arrives in X-Gitlab-Token). Set CALLAHAN_WEBHOOK_SECRET in .env to enable verification; leave empty for dev.
push events build the configured branch only. pull_request / merge_request events build the source branch and post a single markdown comment back to the PR with the AI review and the security scan summary.
Secrets
Add secrets in the dashboard under Project → Secrets. They're XOR-obfuscated at rest in the SQLite file and decrypted in memory only when injected into a step or used for git clone. The GIT_TOKEN secret is special — it's used as the PAT when cloning the repo and when posting PR comments back.
- name: Deploy run: flyctl deploy --remote-only env: FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
AI configuration
Configure the LLM in the Configure AI / LLM page or via environment variables in .env. Settings are stored in SQLite (API keys obfuscated), so the UI selection persists across restarts.
# pick one provider ANTHROPIC_API_KEY=sk-ant-… OPENAI_API_KEY=sk-… GROQ_API_KEY=gsk_… # or run Ollama locally — no key needed OLLAMA_URL=http://localhost:11434 LLM_PROVIDER=anthropic LLM_MODEL=claude-sonnet-4-5
Providers
Four providers are wired into the LLM client:
- Anthropic — Claude Opus 4.5, Sonnet 4.5, Haiku 4.5
- OpenAI — GPT-4o, GPT-4o-mini, o1-mini
- Groq — Llama 3.3 70B, Llama 3 8B, Gemma 2 9B
- Ollama — anything you've pulled locally (Llama 3.2, Mistral, CodeLlama, Phi3…)
If no API key is set, Callahan falls back to Ollama on localhost:11434.
Offline mode
Install Ollama, pull a model (ollama pull llama3.2), and set OLLAMA_URL=http://localhost:11434. Pick Ollama in the settings page. AI features then work with no API key and no outbound network traffic.
Scanners
Two scanners are run when their binary is on the runner's $PATH. Both are invoked with --format json and the output is parsed into a structured SecurityScanResult:
- trivy — filesystem and dependency vulnerabilities, plus secret detection
- semgrep — SAST with auto-config rule packs
If neither is installed, the Security Analyst falls back to an LLM-only source review. Findings are reported but do not, by themselves, fail the build — add trivy fs --exit-code 1 --severity CRITICAL as a regular step if you want a critical finding to fail the pipeline.
Environment variables
| Variable | Default | Notes |
|---|---|---|
| PORT | 8080 | API server port |
| DB_PATH | ./callahan.db | SQLite file |
| DATA_DIR | ./data | Working dir for clones and artifacts |
| LLM_PROVIDER | anthropic | anthropic · openai · groq · ollama |
| LLM_MODEL | claude-sonnet-4-5 | Default model for the chosen provider |
| ANTHROPIC_API_KEY | — | Claude API key |
| OPENAI_API_KEY | — | OpenAI API key |
| GROQ_API_KEY | — | Groq API key |
| OLLAMA_URL | http://localhost:11434 | Local Ollama endpoint |
| CALLAHAN_API_TOKEN | — | Bearer token for API auth. Empty disables auth (dev). |
| CALLAHAN_WEBHOOK_SECRET | — | HMAC secret for inbound webhooks. Empty skips verification. |
HTTP API
JSON over HTTP at http://localhost:8080/api/v1. If CALLAHAN_API_TOKEN is set, send it as Authorization: Bearer <token>.
# projects GET /api/v1/projects POST /api/v1/projects GET /api/v1/projects/:id # builds POST /api/v1/projects/:id/builds # trigger GET /api/v1/builds/:id WS /api/v1/builds/:id/logs # live stream POST /api/v1/builds/:id/explain # AI failure analysis # pipelines GET /api/v1/projects/:id/pipeline PUT /api/v1/projects/:id/pipeline # commits Callahanfile # inbound webhooks POST /api/v1/webhook/github POST /api/v1/webhook/gitlab # LLM GET /api/v1/settings/llm PUT /api/v1/settings/llm POST /api/v1/settings/llm/test
Missing something? Open a discussion — this handbook follows the code.