ADR-0803: The git snapshot sink converges to one export engine, go-git¶
One export engine for the git snapshot sink:
engine: cliis removed from the API surface; the git CLI machinery survives only where it is genuinely shared (file:// remotes and the ls-remote connection probe).
Theme: 04 · Export & sinks (numbered in the 08 range: 0802 was held for the pipeline-CLI contract, see Notes) · Status: Accepted (2026-10-06)
Context¶
spec.git.engine offered go-git (default) and cli. The CLI engine existed for an assumed
class of SSH/KEX edge cases, but a verification pass of the product decisions behind it found no
auth mode that genuinely needs it:
- The pipeline image ships no git binary, so the CLI engine cannot serve pipeline mode — one engine means go-git.
- The go-git SSH key-exchange offer is an in-repo pin, and the pinned x/crypto already implements
mlkem768x25519-sha256anddiffie-hellman-group16-sha512; the CRD doc's "cli required for SSH/KEX edge cases" reduces to a stale pin plus a transitional-algorithm residue (sntrup761-only servers) with no in-repo evidence of users. file://remotes andgit ls-remoteconnection probes call the CLI machinery for both engines today, soexec_git.go/cli_env.go/export_file.gostay regardless — removing them was never on the table.- ADR-0104 documents the CLI SSH path as the weaker host-key story (no fail-closed guard), so making the CLI the only engine would regress the documented security model.
Decision¶
The git snapshot sink exports and deletes through go-git only:
- the CRD field marker becomes
+kubebuilder:validation:Enum=go-git, and the exportedGitEngineCLIAPI constant is removed; - admission validation rejects any
spec.git.engineother thango-git(includingcli), naminggo-gitas the engine; - backend construction rejects it independently (defence in depth): a persisted object still
carrying
engine: clifails at its next export build with an error naminggo-git; the operator never deletes or rewrites the object; - the internal
GitEnginetype,Config.Enginefield and the engine branch points (isFileRemote(...) || engine == cliin the export and delete paths) are removed: the delivery split isfile://remote vs everything else; - the go-git SSH key-exchange offer is extended with the two algorithms x/crypto already implements, appended so the existing eight keep their relative order;
- every in-repo site asserting
engine: cliworks is truthed up in the same change (CRD reference, chart README template and regenerated README, operator manual engine table, coding-standards container-build MUST line, security architecture engine sections, operator Dockerfile comments, ADR-0415 commit-ergonomics sentence);docs/operator-manual/upgrading.mdnames the persisted-sink behaviour.
Consequences¶
- The CLI delivery machinery's persistent warm-mirror handling becomes unreachable in production (file:// remotes get a fresh workdir per operation). The per-engine probe/delivery function pairs inside the shared machinery are hoisted/trimmed in a later sweep, not here — the branch points that made them drift are gone.
- The integration-tier regression locks that pin the (CLI machinery × persistent mirror) surface were removed with that surface; the go-git twins keep pinning the surviving persistent-mirror machinery.
- Operators with a persisted
engine: clisink must remove the field before that sink exports again; see the upgrade note. SSH sinks converting to go-git needknown_hostsin the sink's credential secret (the go-git path fails closed where the CLI path left host-key policy to the ambient ssh configuration).
Alternatives considered¶
- Keep
clias an opt-in: rejected — no verified auth mode needs it, it doubles the export/delete/probe surfaces to keep green, and the pipeline image cannot serve it. - Make
clithe only engine: rejected — it would regress the fail-closed SSH host-key model of ADR-0104 and keep the git binary as a hard runtime dependency of the operator image. - Deprecate without removing: rejected — pre-v0.x API, no deprecation window to buy; soft-deprecation would keep both engines' behaviours "green" while only one is served.
Notes¶
- ADR number 0802 is skipped: when this ADR was written, eight pipeline code comments cited 0802 as the future pipeline-CLI contract, so it took 0803 to avoid squatting on it. The docs-truth sweep has since removed those cites; 0803 keeps its number rather than being renumbered.
- GTE-1..GTE-3 in
openspec/changes/2026-10-06-product-decision-convergence/specs/git-engine/spec.mdare the requirement form of this decision.