The Control Plane — planning, lease queue, and sealed credentials
The Control Plane (cmd/kiwid, pkg/orchestrator) is the SaaS brain: it exposes the API, authenticates callers, runs the Planner, holds the lease queue, and stores sealed credentials. It never runs your code — that is the Data Plane's job.
Roles
The Control Plane binary runs as one of four roles so the request-serving surface and the background engine can scale and deploy independently:
| Role | What it does |
|---|---|
api | Serves the HTTP API and auth only. |
orchestrator | Runs the background engine and singleton sweepers only. |
migrate | Applies database migrations and exits. Run before rolling serving instances. |
all | Everything in one process — convenient for local and single-box dev. |
Health checks are exposed at /healthz (liveness) and /readyz (DB-checked readiness). In a multi-replica deployment, run kiwid -role migrate once before serving and set KIWI_SKIP_BOOT_MIGRATE=true on the serving roles so they don't each attempt migrations on boot.
The API surface
The API is the single entry point for every surface — the kiwi CLI, the Node/Python SDKs, the Next.js dashboard, and the Linear webhook. Notable endpoints:
POST /api/v1/planner/plan— submit a task for planning. Supports idempotent submission via theIdempotency-Keyheader.POST /api/v1/daemon/join-token— mint a single-use join token for a new daemon.POST /api/v1/webhooks/linear— receive Linear issues labeledkiwi(or moved to In Progress) and convert them into planner jobs.
Daemons never receive an inbound connection; they poll the API over HTTPS (heartbeat) and receive leased work in the response. See The Data Plane daemon.
The lease queue
The lease queue (pkg/store/queue.go) is the heart of the Control Plane's hand-off to daemons. Its defining property: tasks are leased, not popped.
A task is never removed when handed out. It is leased to one daemon for a bounded window (a TTL). If that daemon dies without renewing the lease, the lease expires and the task returns to QUEUED so another daemon can pick it up. This gives crash recovery for free — a dead daemon's work is never lost.
State machine
Fencing and poison pills
LeaseIDis a fencing token. Every renew and every complete must present the current lease id. A stale daemon whose lease has since been reassigned cannot mutate the task — its writes are rejected.MaxLeaseAttempts(5) dead-letters poison pills. A worker spec that reliably crashes whichever daemon leases it would otherwise be requeued forever. After the attempt bound is reached, an expired lease is markedFAILED(dead-lettered) instead of requeued.StartedAtis set once at lease and never touched by renewals, so it is the correct basis for agent-minute metering (unlikeUpdatedAt, which resets on every renew).
Fleet scoping and caps
Every task carries a fleet_id. A daemon leases only work for its own fleet plus unassigned work (fleet_id = ""). At lease time the queue also enforces per-org limits — a concurrency cap, per-job and per-month budget caps, and a monthly agent-minute ceiling — refusing a new lease when a cap is hit. On Postgres the candidate scan uses SELECT … FOR UPDATE SKIP LOCKED, so concurrent daemons never contend for the same row.
DAG gating
The queue is also where the DAG is enforced: LeaseNextTask only hands out a task once all of its depends_on siblings have SUCCEEDED, and fails a task fast if any dependency has FAILED. That logic is covered in detail on the DAGs page.