Skip to content

Latest commit

 

History

History
157 lines (131 loc) · 12.7 KB

File metadata and controls

157 lines (131 loc) · 12.7 KB

The support contract: what we edit, what we refuse, and why

design — the single statement of the boundary. Index: ../../INDEX.md

Status: the front door for the target boundary. Captured 2026-07-11. This page combines shipped behaviour with the design that must precede the rest of the boundary. Rows marked planned state the intended contract, not implemented behaviour; other rows describe the current runtime. If a claim here and a claim there disagree, this page is wrong and should be fixed — the boundary has one home now.

The contract, in one sentence

GitOps Reverser should edit the intent layer. It must never edit the expansion layer.

Every GitOps repository has an intent layer — the documents a human authored and committed — and an expansion layer — the objects a controller derived from them. We mirror and edit the intent layer, because that is the layer with a home in Git and the layer a careful human would edit anyway. The target boundary refuses the expansion layer, because expansion is a one-way function and its output has nowhere to go.

That refusal is the operator's defensible support contract, not a gap — but the live-object provenance gate that enforces it is not implemented yet. In particular, the runtime currently does not distinguish controller-expanded objects before sanitization. See expansion-boundary-and-corpus-organisation.md.

The three questions we ask

An edit must survive all three. They are independent, and each has its own design doc.

# Question Failure if we get it wrong Where it is argued
1 Is it KRM, and may I read/write it? we corrupt a document we did not understand resource-capability-model.md
2 Can this edit be written back to exactly one document? we cannot express the change, or we change more than one thing kustomize-support-boundary.md, gittarget-granularity-and-cross-environment-edits.md §1
3 Does that document already have another writer — and does this object even have a home in Git? we destroy someone else's invariant, or invent a second source of truth orchestrator-knowledge-boundary.md, expansion-boundary-and-corpus-organisation.md

The governing rule of question 2 is fan-in = 1: we never change more than one thing. An edit may be written to a document only if exactly one live object derives from it. That single rule refuses shared kustomize bases, ApplicationSet templates, and multi-input ResourceSet templates alike. It is specified once, in gittarget-granularity-and-cross-environment-edits.md §1 (the two-layer write boundary, L1/L2); everywhere else it is quoted, not redefined.

The target verdicts

Verdict Meaning
Editable We mirror it and write edits back to its file.
Read-only context We read it to understand the folder; we never write it.
Not mirrored It exists live, but a controller synthesised it and Git has no home for it. This needs the planned provenance gate.
Refused The folder cannot be written safely. We say why, and fail the target.
Write-only We can describe and replace it, but never read it (SOPS).

Delivery and orchestration

Construct Layer Verdict Why
Plain KRM folder intent Editable the files are the objects — the first-class target
Argo CD Application (hand-written) intent Editable ordinary KRM
Argo CD Application generated by an ApplicationSet expansion Planned: not mirrored carries an ownerReference; no home file
Argo CD ApplicationSet (the CR) intent Editable as a document but its template has fan-in N — see below
App-of-apps root Application intent Editable, flagged cluster entry point not an onboarding answer
Flux Kustomization, GitRepository, OCIRepository, HelmRepository intent Editable ordinary KRM
Flux HelmRelease intent Editable shipped; an ordinary KRM document like any other
Objects the helm-controller renders from it expansion Planned: not mirrored no home file
flux-operator ResourceSet (the CR) intent Editable as a document spec.inputs is the supported edit surface
Objects a ResourceSet expands expansion Planned: not mirrored spec.resources × inputs; no home file
flux-operator ResourceSetInputProvider intent Editable but status.exportedInputs is live-only and unwritable
flux-operator FluxInstance intent Editable, flagged cluster entry point it manages Flux itself
KRO ResourceGraphDefinition, Crossplane Composition + claims intent Editable ordinary KRM
Objects they expand expansion Planned: not mirrored no home file

Rendering

Construct Verdict Why
kustomize resources, namespace, images, replicas Editable invertible; images/replicas edit-through is shipped
kustomize base + un-fancy overlays Editable, narrow render-root scoping reads ../../base as read-only context and writes only inside the overlay; the base is never written. It edits existing overlay-local documents and declared image/replica entries, and authors a new images:/replicas: entry when a base-supplied image/replica is changed in one environment. scan-repo still reports these as overlay-fan-out-unsupported — the discovery-side flip is a follow-up
New object in an external-base overlay Editable placed as an overlay-local file and registered in the overlay's own resources: entry (never the base's), verified by re-render before the commit; proven by TestPlacement_ExternalBaseOverlay_NewObject. A new object a folder images:/replicas: entry would override is refused loudly by the oracle, not committed
Change a base-supplied image/replica in one overlay Editable the writer authors a new images:/replicas: entry in the overlay's own kustomization (the base stays read-only), verified by re-render; proven by TestOverlayAuthors_ImageEntry_ForBaseSuppliedImage / ..._ReplicaEntry_ForBaseSuppliedCount
Delete a base-inherited object in one overlay Editable the writer authors a $patch: delete (patch file plus patches: entry) in the overlay, verified by re-render that the object leaves the render; the base is untouched. Proven by TestOverlayAuthors_DeletePatch_ForInheritedObject. A patch that fails to match is refused by the oracle, not committed
kustomize base shared by >1 overlay, edited in place Refused fan-in > 1
kustomize patches: — a strategic-merge document named by path: Tolerated, not authored the folder is accepted and the render is mirrored; the patch is read-only build context. An edit to a field the patch OWNS is refused per object, not per folder
kustomize patches: — inline, JSON6902, or a path outside the tree Refused not a sparse KRM document we can read; refused by name
kustomize patchesStrategicMerge, patchesJson6902 (deprecated spellings) Refused kustomize does not fold them into patches: (measured), so they refuse under their own names
kustomize generators, components, namePrefix/nameSuffix, remote bases Refused non-invertible
kustomize helmCharts: (inflation) Refused we never render a chart
A Helm chart (Chart.yaml + templates/ + values.yaml + crds/) Planned: skipped as a unit this needs chart-folder detection; today scan-repo can report an incidental crds/ directory as accepted
Helm knobs on a HelmRelease / Application (chart version, inline values, parameters) Editable the Helm surface people actually use — see below
Free-standing Helm values file (values/production.yaml) Refused today not KRM; a ValuesFile projection is proposed
CI-rendered rendered/ output Refused the next render destroys the edit

Machine-written Git, and secrets

Construct Verdict Why
A path written by Flux ImageUpdateAutomation Refused a bot commits here; $imagepolicy comments are load-bearing
.argocd-source*.yaml (Argo CD Image Updater) Refused / read-only written by a bot, and it outranks the config a reader would inspect
SOPS Secret Write-only not schema-conformant; the mac binds the whole document
SealedSecret Editable ordinary KRM; each value sealed independently
ExternalSecret Editable ordinary KRM; the value simply lives elsewhere
Secret derived by ESO / sealed-secrets expansion → Planned: not mirrored carries an ownerReference; the mirror-path gate is unbuilt

What "we do not do Helm" gets wrong

We already do more Helm than that sentence admits, and the honest headline is:

We support Helm the way GitOps repositories actually use it — as a declaration you edit, not a chart you render.

The most common Helm operations in a GitOps repo are bump the chart version and change a value on a HelmRelease, and both have worked since higher-level KRM documents landed. What stays refused, permanently and correctly, is chart inflation: we never render templates/, and we never learn what a value means.

The one genuine gap is the free-standing values file, which is not KRM and so has no object for a user to edit. The proposed fix reuses the EncryptedSecret trick — project it into a kind we can store — and is argued in expansion-boundary-and-corpus-organisation.md.

What we will not do

  • No orchestrator emulation. We do not resolve a Helm chart, evaluate an ApplicationSet generator or a ResourceSet template, decrypt SOPS, or contact a registry. We do run kustomize — the real sigs.k8s.io/kustomize/api, the library Flux itself renders with — locally, with plugins disabled, and never on a remote base: a remote resource is refused before the build is invoked. Using the renderer is the opposite of emulating it; see kustomize-support-boundary.md §4.
  • No Argo CD or Flux Go dependency. Their kinds are matched by group and kind over unstructured; the upstream checkouts under external-sources/ are reference material, never vendored code. (sigs.k8s.io/kustomize is neither — it is a Kubernetes SIG library, already in this module's requirement graph.)
  • We never widen the boundary by guessing. Ownership and provenance only ever refuse more. Everything refused for renderability stays refused.

Where the arguments live

Doc What it settles
../../facts/expansion-provenance-markers.md the measured evidence each producer leaves behind — and why the ownerReference gate catches one producer in five
README.md the index of this folder, and the summary of what works today
kustomize-support-boundary.md the kustomization field taxonomy and the supported-layout allowlist
gittarget-granularity-and-cross-environment-edits.md the write boundary (L1/L2) — the one home of fan-in = 1
orchestrator-knowledge-boundary.md where Flux/Argo knowledge lives: claims about paths, not a dependency
expansion-boundary-and-corpus-organisation.md the provenance axis; ApplicationSet vs ResourceSet; the Helm surface
resource-capability-model.md what may I do to this document (schema conformance, visibility, write unit)
write-only-encrypted-secrets.md SOPS: describe the ciphertext, never read it
sealed-secrets-and-external-secrets.md why neither needs a new kind, and the derived-object gate
unreflectable-edits-and-write-gating.md what happens to an edit with no legal destination
repo-discovery-and-onboarding-scan.md reporting all of this over a whole repository
test/fixtures/gitops-layouts/ the corpus of real-world shapes every verdict is checked against