GitOps Is the Control Plane
There's a question every platform team asks the moment a new deploy tool walks in: does it replace my GitOps, or feed it? Most tools answer wrong. They want to be the control plane — their console, their API, their database of desired state, with your GitOps at best a mirror of it afterward.
Kilter answers the other way. GitOps is the control plane. Kilter is an authoring tier that feeds it. And later this year, kilter apps will promote seamlessly from a kilter-provisioned middle environment into pure-Kubernetes production reconciled by your ArgoCD — through your production engineering process: PRs, AppProjects, RBAC, sync windows, audit, all yours.
This post is the plan.
Two tiers, two needs, one artifact pipeline
Dev/QA and Prod want opposite things from a control plane, and a single tool serving both ends up compromising both.
Dev/QA wants velocity. You spin up an env per PR, tear it down, mutate it,
break it. Hand-writing a Namespace, two ServiceAccounts, and ten secrets per
ephemeral environment would be madness — you want a controller to do that
provisioning for you, instantly, and throw it away. This is kilter's home: an
imperative operator that, on kilter deploy, builds and pushes the image,
renders the chart, provisions a database and a user, registers a workflow
namespace, cuts an object-store bucket, writes per-app secrets — then commits the
result and lets Flux take it live. Fast, magical, disposable.
Prod wants trust. You want the deployed state to be exactly what a human reviewed in a pull request, reconciled continuously, with no hidden imperative layer and no machine co-authoring the source of truth. You want pure declarative Kubernetes — manifests that are self-sufficient, diffable, and reconciled by ArgoCD with self-heal and prune behind your RBAC.
One artifact pipeline serves both: the app is an OCI image, the deployment is an
OCI chart (kilter-app), the configuration is a known shape of environment and
secrets. What changes between the tiers is not the artifact — it's the control
style.
The promotion: transmute provisioned into declared
Today, kilter's Dev/QA envs are operator-rendered: the controller commits a HelmRelease but synthesizes the Namespace, ServiceAccounts, and backing secrets at runtime — they're not in git. That's the right tradeoff for an ephemeral middle tier, and the wrong one for prod. (It's the "almost" from the last post — the seam between a delightful delivery layer and a hidden imperative one.)
Promotion is the step that closes the seam. A kilter package takes a frozen
middle-tier environment and emits a self-sufficient bundle for prod: every
runtime object materialized as a declared one, ready for ArgoCD to reconcile with
no kilter-server anywhere in the path.
Here is what is identical across the tiers, and what transmutes on promotion:
| Dev/QA (kilter middle) | Prod (ArgoCD) | |
|---|---|---|
| App image | OCI, digest-pinned | same OCI digest |
| Chart | kilter-app, OCI | same chart, same version |
| Config shape | env + secret keys | same keys |
| Namespace | synthesized (targetNamespace) | committed Namespace manifest |
| ServiceAccounts | synthesized | committed SA + RoleBinding |
| Reconciler | Flux Kustomization (operator-committed) | ArgoCD Application (PR-reviewed) |
| Connection secrets | injected at runtime | committed SOPS (or ExternalSecret) |
| Provisioned resources | operator creates DB/user, workflow ns, bucket | declarative: CNPG Database, Crossplane claims, a workflow-namespace declaration |
The artifacts that matter — image, chart, configuration shape — flow through untouched. Only the control changes: injected becomes declared, operator-push becomes git-pull.
The one real piece of engineering is the bottom row. In Dev/QA, kilter's
provisioners create a database and a user, register a workflow namespace, cut a
bucket and a key. In prod those same resources must be created declaratively,
as objects ArgoCD reconciles: a CloudNativePG Database for the database and
user, a Crossplane claim for the object-store bucket, an idempotent
workflow-namespace declaration. Kilter knows how to provision these imperatively
today; the work is to emit the declarative equivalent — to write the
provisioner's intent as a Crossplane or CNPG manifest rather than executing it.
Stateless connection secrets (the LLM gateway URL, the tracing endpoint, SMTP)
simply get SOPS-encrypted and committed, the same way kilter already encrypts
them — it just commits them instead of injecting them.
Why the split is the point
This isn't kilter begrudgingly supporting ArgoCD. The two-tier split is the correct architecture, and it's the thing hosted PaaS platforms structurally cannot offer.
Kilter, by feeding GitOps instead of replacing it, gets both. The middle tier keeps the operator and its velocity; prod keeps ArgoCD and its trust. The seam that was a leak when one tier tried to do both jobs becomes a boundary — the single place where imperative provisioning is transmuted into declarative declaration, exactly at the Dev/QA → Prod edge where you want a gate anyway.
And because kilter produces standard artifacts — OCI image, OCI chart, committed manifests — your ArgoCD process doesn't need to know kilter exists. AppProjects, RBAC, sync windows, the PR template, the audit trail: all yours, unchanged. Kilter is an upstream source of reviewed bundles, not a competing authority.
What "seamless" actually means
It means the app code and the app image never know which tier reconciled them. The same container that ran in the kilter middle environment, against the same chart, with the same environment shape, runs in prod — the only difference is that prod's Namespace, identity, and credentials were committed by a human-reviewed PR and reconciled by ArgoCD, while the middle tier's were provisioned by an operator and reconciled by Flux.
A promotion is a PR. You see, in the diff, exactly what gets materialized: the Namespace, the ServiceAccounts, the SOPS secrets, the Crossplane claims. You review it, you merge it, ArgoCD syncs it. Nothing is hidden, because there's no operator in the prod path to hide anything.
Roadmap — late Q3
kilter package— freeze a middle-tier env and emit the self-sufficient prod bundle (Namespace, SAs, HelmRelease or rendered manifests, ArgoCDApplication/AppProject).- Declarative provisioners — Crossplane claim templates and a CNPG
Databasemapping, so the database/user, bucket, and (where a CRD exists) workflow namespace become ArgoCD-reconciled objects instead of operator calls. - SOPS-commit path — stateless connection secrets committed, not injected, on promotion.
- Promotion diff —
kilter package --dry-runshowing exactly what will be materialized, so the prod PR is reviewable before it exists. - ArgoCD-native output — first-class
Application/AppProjectgeneration, so kilter writes into an ArgoCD repo as naturally as it writes into a Flux one today.
The one-line version
GitOps is the control plane. Kilter is the fastest way to author for it. In late Q3, a kilter app in your Dev/QA middle tier promotes to pure-Kubernetes prod through your ArgoCD process — same image, same chart, same config, declared not injected, reviewed not assumed. Your production engineering process stays yours; kilter just feeds it the best bundles in the business.
Where to go next
- Read modern Kubernetes is delightfully elegant and almost even small — the seam this promotion closes.
- See what makes a platform agent-native — why declarative + reconciled is the right control style.
- Read why IDPs are the wrong abstraction — another flavor of "don't try to be the control plane."
- Start your free trial — the full platform, 7 days, no card.