Setup guide
From zero to a running board
This guide walks a competent developer from an empty Ubuntu 24.04 server to a logged-in OperationKit board. The steps match the current source tree (V1, reseeded 2026-08-28).
The source is public at github.com/m1keluka/OperationKit.
These docs describe exactly what install.sh, docker-compose.yml, and .env.example
contain today. Nothing here is invented or forward-looking.
Two setup paths. The DigitalOcean installer
(install.sh) provisions Docker, Caddy, and all secrets for you on a fresh
droplet. Manual is any Linux host where you run the steps yourself.
The manual path is described below.
Prerequisites
docker compose version must work. Install from Docker's official repo.
Model keys are optional. ANTHROPIC_API_KEY powers a small
server-side summarizer, not agent sessions. Agent sessions run under your Claude Code
subscription (authenticated separately). OpenAI and Gemini keys are also optional and off by
default.
Get the source
Clone the repo to your server:
# On your server (not your laptop)
git clone https://github.com/m1keluka/OperationKit.git
cd OperationKit
The source is public. Clone or star the repo at github.com/m1keluka/OperationKit.
Configure .env
cp .env.example .env
The .env.example documents every variable. The seven you must fill before
the stack will start:
| Variable | What it is | Required? |
|---|---|---|
| CC_JWT_SECRET | Signs session cookies. Rotating this logs everyone out. | required |
| LITELLM_MASTER_KEY | Auth key for the LiteLLM proxy. Use sk- prefix by convention. |
required |
| LITELLM_SALT_KEY | Encryption salt for LiteLLM credential storage. | required |
| POSTGRES_PASSWORD | Password for the LiteLLM Postgres database (internal only, loopback). | required |
| ANTHROPIC_API_KEY | Powers the server-side session summarizer only. Agent sessions do not use this key. | optional |
| OPENAI_API_KEY | OpenAI models via LiteLLM. Leave blank to skip. | optional |
| GEMINI_API_KEY | Google Gemini models via LiteLLM. Leave blank to skip. | optional |
Generate the secrets you invent yourself with:
openssl rand -hex 32 # for CC_JWT_SECRET, LITELLM_SALT_KEY, POSTGRES_PASSWORD
openssl rand -hex 24 # then prepend sk- for LITELLM_MASTER_KEY
Never commit .env. It is git-ignored and contains live keys.
Start the stack
docker compose up -d
Compose starts three services: litellm-db (Postgres) first, then
litellm (model proxy on port 4000), then command-center
(the app on port 3002). The first run pulls images and builds the app container,
so give it a few minutes.
# Watch the logs as it comes up:
docker compose ps
docker compose logs -f command-center
Authenticate your Claude subscription
This step is required before any objective can run. Agent sessions are powered by a Claude Pro or Max subscription, not by an API key. The subscription authenticates via OAuth into an account slot on the host.
Once the stack is up, authenticate at least one Claude Code account using the helper:
# Interactive (prompts you to authorize in a browser):
./scripts/claude-auth.sh a
# Headless / server-only path:
./scripts/claude-auth.sh a --setup-token
The installer supports up to five account slots (a through e) plus a
Codex slot. Add more subscriptions later to increase concurrent session throughput.
Full details are in docs/CLAUDE-CODE-AUTH.md in the source tree.
Health check
Both services expose health endpoints. Check them before moving on:
# OperationKit app (should return HTTP 200 with {"status":"ok"}):
curl -f http://127.0.0.1:3002/api/health
# LiteLLM model proxy (loopback only — never exposed to the internet):
curl -f http://127.0.0.1:4000/health/liveliness
If command-center is not healthy, check
docker compose logs command-center. The most common first-run causes are a
missing or blank required variable in .env or a bind-mount path that does not
exist on your host.
Seed the first user
The app ships with no users. Run the seed script once:
docker compose exec command-center npm run seed
This creates an admin user with the initial password changeme
and a sample member user ava. The script prints
Change passwords after first login! Do that immediately.
Log in
Open your browser and navigate to:
http://127.0.0.1:3002
Log in with username admin and password changeme. Change the
password from the account settings UI before doing anything else.
Login issues a JWT signed with CC_JWT_SECRET. If you rotate that secret
later, all sessions are invalidated and everyone must log in again.
Create your first objective
Create a workspace
A workspace scopes objectives, users, and credentials to a project or team. Create a new one from the board UI for your project.
Write an objective
An objective is a goal statement plus "done when..." acceptance criteria. Give it a clear title and a concrete description of what done looks like.
Let it run
The board moves the card through planning, working, and review. Each stage is a live agent session with a terminal you can watch in real time.
TLS with Caddy (public deployments)
For an internet-facing deployment, the app ports stay bound to 127.0.0.1.
Caddy on the host terminates TLS on ports 80 and 443 and reverse-proxies to :3002.
Only SSH (22), HTTP (80), and HTTPS (443) are ever exposed.
The DigitalOcean installer (install.sh) wires the Caddyfile
for you. For a manual setup, run scripts/bootstrap.sh to install Caddy,
a firewall (ufw: 22/80/443), and fail2ban. Read SECURITY.md before
exposing the board to the internet.
A recommended starting point is a 4 GB / 2 vCPU droplet (~$24/month) on DigitalOcean plus your Claude Pro/Max subscription. TLS certificates from Let's Encrypt are free.
Model keys
OperationKit routes model calls through LiteLLM, which supports every major provider.
Out of the box, the stack is configured for Claude (Anthropic), OpenAI, and Google Gemini via
the keys in .env.
To add Grok (xAI) or any other provider LiteLLM supports, add the provider's API key to
.env and update config/litellm/config.yaml with a model entry
pointing at that key. To apply new config, run docker compose restart litellm.
| Provider | Key variable | Notes |
|---|---|---|
| Anthropic (Claude) | ANTHROPIC_API_KEY | Optional; agent sessions run on Claude subscription, not this key. |
| OpenAI | OPENAI_API_KEY | Optional; add to .env and config.yaml. |
| Google Gemini | GEMINI_API_KEY | Optional; add to .env and config.yaml. |
| xAI / Grok | XAI_API_KEY (or similar) | Add as a custom provider in config.yaml. LiteLLM supports xAI natively. |
| Ollama (self-hosted) | GPU_OLLAMA_URL | Set to your Ollama host URL. Leave blank if not using a local model. |
| Any LiteLLM provider | varies | Add the key and a model entry in config/litellm/config.yaml. |
Teams and workspaces
OperationKit is multi-tenant from day one. Each workspace is a hard boundary: objectives, files, credentials, and users stay scoped to the workspace they belong to. Nothing from workspace A is visible in workspace B.
- Create workspaces per team, project, or client from the board UI.
- Invite users into a workspace. Each user gets their own board view scoped to that workspace.
- Admin users can see and manage all workspaces. Members see only theirs.
- Credentials (API keys, integration tokens) stored in a workspace are only available to agents in that workspace.
Troubleshooting first-run
| Symptom | Likely fix |
|---|---|
| command-center container restarts / unhealthy | Missing or blank required value in .env. Run docker compose logs command-center. |
| Health check on :3002 fails but container is running | Give it ~15 seconds to boot (start_period), then retry. Check the log for a bind-mount path that doesn't exist on your host. |
| LLM calls fail with auth errors | Bad or missing provider key in .env. Fix the key and run docker compose restart litellm. |
| Objectives queue but no sessions start | No Claude Code account is authenticated. Run ./scripts/claude-auth.sh a. |
| Can't log in after rotating CC_JWT_SECRET | Expected. Rotating the JWT secret invalidates all existing tokens. Log in again with the new secret active. |
Still stuck? Open a bug report in the GitHub repo. Onboarding friction is a first-class V1 bug.