Control plane¶
Community Edition · Phase 1 (early)
The control-plane proposal ledger, the vornikctl control-plane
surface, and vornikctl doctor --offline are in the free Community
Edition. This is an early slice — it proposes changes for you to review;
it never applies them itself.
The control plane helps you operate vornik — troubleshoot, tune, and change configuration — instead of hand-editing YAML and reading raw logs. Its Phase-1 core is a human-gated proposal ledger: a suggested change (a config diff, a model swap) is recorded as a proposal that you review and approve or reject. Nothing is ever applied automatically — proposing is the only action the control plane takes, so it is safe by construction.
The proposal ledger¶
A proposal has a kind (config / model / scaffold), a blast radius
(model ⊂ project ⊂ swarm ⊂ daemon), a diff, a rationale, and a status:
DRAFT ──approve──▶ APPROVED (you then action it by hand — Phase 1)
└─reject───▶ REJECTED (kept for audit; supersede with a new one)
Proposals come from you (via the CLI) or from a Tune detector — a leader-gated watcher that raises a DRAFT when a project's health signal degrades, so a regression surfaces as a reviewable suggestion rather than something you have to notice yourself. It watches two signals over a rolling window and only fires after a sustained breach (three consecutive scans):
- failed-task rate — too many executions failing;
- p95 latency — executions taking too long.
So a project that starts failing or slowing down produces a DRAFT proposal
("investigate the failing step / consider a faster model or a timeout change")
you can review with vornikctl cp proposals.
vornikctl control-plane (alias cp)¶
# See what's waiting for you
vornikctl cp proposals # add --status DRAFT / --project family
# Inspect one (full diff + rationale)
vornikctl cp show <proposal-id>
# Decide
vornikctl cp approve <proposal-id> # you must NOT be the proposer
vornikctl cp reject <proposal-id>
# Raise one yourself
vornikctl cp propose --title "bump scraper timeout" \
--project family --kind config --blast-radius project \
--diff '-timeout_seconds: 30
+timeout_seconds: 90' --rationale "web_fetch timing out"
# Apply an APPROVED, applyable proposal (hot-reload) — and roll it back
vornikctl cp apply <proposal-id> # --ack-daemon for daemon-scope
vornikctl cp rollback <proposal-id>
Applying a change (gated, reversible)¶
An APPROVED proposal that carries a concrete file rewrite can be applied by the daemon via config hot-reload — no restart:
- Whole-file replace only — the proposal carries the full new file contents (no fragile diff-patching); the human-readable diff is for review.
- Fail-closed preconditions: only APPROVED applies (exactly once); a
daemon-scope change needs an explicit
--ack-daemon; apply is refused while any task in scope is running (retry in an idle window); the new content is parsed/validated before anything is written. - Workflow edits are daemon-scope: workflow definitions are shared registry
objects, even when one project's metrics motivated the proposal. Timeout
proposals therefore disclose their cross-project reach, require
--ack-daemon, and check every project for active tasks. - Atomic + snapshotted: the current file is snapshotted, the new file is written atomically, then reloaded. If the reload rejects the config (or times out), the daemon auto-rolls-back to the snapshot — it never runs a bad config.
- Rollback:
cp rollbackrestores the pre-apply snapshot and reloads.
Proposals from the Tune detector are review-only (they describe a problem,
not a specific rewrite) — you approve them and make the change yourself, or
attach a concrete rewrite. Auto-apply does not exist: a human always runs
cp apply.
The surface is operator-scoped (it works in Community and is never behind
the Enterprise admin gate). Two safety rules are enforced daemon-side: a
proposal can only be decided while it is DRAFT (a decided proposal is
terminal — supersede it with a new one), and the proposer can never approve
their own proposal — approval is always a separate human decision. So when
the Tune detector proposes (as tune-detector), a human approval always
clears the gate.
Web console (Enterprise)¶
The Admin → Control plane page (/ui/admin/control-plane) is a browser
surface over the same ledger: browse proposals by status, read a proposal's
rationale + diff, and approve / reject / apply / rollback with a click. It
delegates to the same engine as the CLI, so every gate holds — a daemon-scope
apply needs the "affects all projects" checkbox, and the proposer can't approve
their own proposal. (The CLI above is the Community path; the console is an
Enterprise convenience.)
vornikctl doctor --offline¶
The escape hatch for when the daemon won't start. It runs static checks without any daemon RPC:
vornikctl doctor --offline
- config — the config file is present and parses;
- database — reachable (a direct connection, not the daemon's pool);
- migrations — the applied schema head vs the binary's expected head (so "applied 116 < head 117" tells you a migration is pending);
- journal — recent
fatal/panic/errorlines from the daemon's own log, so "why won't it boot" is answered in one command.
It never changes anything and exits non-zero if any check fails.
What's not here yet¶
Config + model applies land now (Phase 2). Still ahead: scaffold-apply for new projects/workflows, the richer diagnostic assembler over logs/metrics/traces, config-as-conversation scaffolding, self-healing incident detection, and an operator agent you can just talk to. See the design notes in the repo for the full arc.