gitops-reverser

module
v0.49.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 22, 2026 License: Apache-2.0

README

CI OpenSSF Scorecard OpenSSF Best Practices Release codecov License Platforms Container Artifact Hub Open Issues

GitOps Reverser

GitOps Reverser watches selected Kubernetes resources and commits a clean YAML representation to Git. It strips status, managedFields, and runtime metadata. For supported layouts, it updates existing manifests in place, preserving comments and document structure.

Use it to capture live changes, bring an existing cluster into Git, or experiment with API-first workflows alongside Flux or Argo CD.

Quick start: install the operator and capture your first ConfigMap in Git.

It is pre-1.0: the CRDs are v1alpha3 and the configuration surface can still change between releases. Before you adopt it covers what to weigh.

Inspect an example commit. Named Kubernetes actors in Git history require optional audit attribution; the default uses a configured Git identity.

How it works

Changes reach the Kubernetes API however your users make them: kubectl, a GUI, a CI job, or an agent over MCP.

  1. Watch the Kubernetes API for the types each GitTarget claims, the single source of object state. A new target also reconciles all existing resources of those types into Git.
  2. Sanitize the change and compare it against what Git already holds.
  3. Write stable YAML to the target folder and push, grouping a burst of changes into one commit.

Custom resources configure it: GitProvider for repository and credentials, GitTarget for branch and folder, WatchRule or ClusterWatchRule for what to capture, and ClusterProvider for the source cluster, created as default on install. See Configuration.

Principles

  • We never commit secret content unencrypted. Ever. A resource classified as sensitive is encrypted before it can reach a commit. If encryption fails, or no encryptor is configured, the write is rejected. There is no plaintext fallback.
  • We only write one way: Kubernetes to Git. Deploying Git back into a cluster is Flux's and Argo CD's job. Run one of them alongside if you want the round trip. See bi-directional usage.
  • We never put the wrong name on a change. When the facts are weak, late, conflicting or missing, the author is unknown (attribution unresolved) instead of a guess. A person's name in the author field means we are sure. See audit attribution.
  • We refuse a folder we cannot support. An unsupported layout is refused before anything is written, with Stalled=True and a reason naming the problem. We will not write the half we understood and leave you the rest. See status conditions.
  • We assume changes arrive through the API. Publication is driven by API writes, and a branch that moved underneath is an expected exception rather than a failure. CI tests with Flux and Argo CD verify that the round trip settles without a commit loop. See API-first publication.

Features

  • Capture existing resources and future changes. A new target captures the selected resources already in the cluster, then follows changes. With deletion mirroring enabled, removal follows the deletion request, even while finalizers keep the object around. Choose which deletions reach Git with a deletion policy.
  • In-place edits keep the shape of your file. Updates preserve key order, comments, and untouched documents in multi-document files.
  • Duplicate resource identities are rejected. A folder holding the same resource in two documents is refused rather than adopted, so a capture cannot update one copy and leave the other stale.
  • Inspect a repository before you point at it. manifest-analyzer --mode scan-repo classifies every candidate folder under a repository root, read-only and with no cluster, so you can see what a target could adopt before creating one. See cmd/manifest-analyzer/.
  • SOPS and age, set up on the fly. The operator creates the encryption configuration and can generate a missing age key in your chosen Kubernetes Secret. Private keys stay in the cluster, never in Git; back up a generated key before relying on it. See SOPS and age.
  • Signed commits. SSH signing through GitProvider.spec.commit.signing, including what it takes to earn a verified badge on your Git host. See commit signing.
  • Commit messages you control. Separate templates for live windows, reconciles, and save requests. A bad template holds the target at Validated=False instead of surfacing at commit time. See message templates.
  • Status you can wait on. Conditions follow the kstatus convention, so Ready, Reconciling, and Stalled mean what kubectl wait and GitOps tooling expect, asserted in tests against the kstatus library rather than against our own reading of it.
  • Metrics. A Prometheus surface with copy-pasteable PromQL for the questions operators ask, and a named list of what is deliberately not instrumented. See interpreting metrics.

What it can write

Choose the resources to watch and the repository, branch, and folder to write into.

Your source in Git What you can do
Plain Kubernetes manifests Capture resources and update their existing YAML documents
Supported Kustomize layouts Update source manifests or image/replica declarations; add and remove resources
A Flux HelmRelease or Argo CD Application Capture the declaration, including chart versions and inline values
Helm templates or standalone values.yaml No writeback from rendered workloads

Kustomize support includes local bases and overlays, with the base kept read-only when targeting an overlay. Generators, components, remote bases, and several other transforms are unsupported. See the supported subset.

Select the resources that express your intent. For example, watch a HelmRelease to capture chart settings. The operator cannot automatically distinguish authored resources from controller-generated ones. See choosing what to capture.

Quick start

Follow the installation walkthrough to capture ConfigMaps from a demo namespace into a disposable repository. You need a Kubernetes cluster, kubectl, Helm 3, and Git write access. The walkthrough includes cert-manager setup; Redis and audit delivery are optional.

The demo captures a live change:

kubectl create configmap test-config --from-literal=key=value -n gitops-reverser-quickstart-demo

Inspect the commit under live-cluster/ in your repository. Then edit the ConfigMap and inspect the next diff. The walkthrough includes status checks, troubleshooting, and cleanup.

Batch changes into a commit

Changes for the same target and author share a commit window: each change restarts the timer, and the commit is made after that much silence. Omitted, the window is 5s; 0s opts into a commit per event. A CommitRequest can close an open window early and supply the commit message. This allows the person making a change to explain why the change was necessary.

Kubernetes resource changes flow through GitOps Reverser's commit window into Git

The picture shows the commit-window example running. It carries the manifests: a GitTarget with a window, a WatchRule selecting the two types, and the save request that closes the window early. It runs on top of the quickstart above.

Try it with your existing repo

Start with a scratch branch containing your existing manifests, with no reconciler deploying that branch. Select a small resource scope and one destination folder. Inspect the initial commits before making a live edit, then check which source files changed.

The operator pushes directly to the configured branch. If you later write to a branch that Flux or Argo CD deploys, read the bidirectional guide first. Live edits can be reverted by the reconciler, and replaying captured state can overwrite concurrent Git edits to the same object. Decide per folder which side is authoritative.

Before you adopt it

The CRDs are v1alpha3, so the configuration surface can change between releases; docs/UPGRADING.md carries each migration. Keep one GitProvider per repository, so that two of them never write the same paths.

  • Access: the chart defaults to cluster-wide read access, including Secrets. Review RBAC to restrict watched types and understand the remaining credential permissions.
  • History: batching can collapse intermediate edits. Unpublished work is held in memory and can be lost on restart; recovery captures current state. Git history is not a complete event log.
  • Deletes: the default mirrors observed delete events but retains documents absent from a reconnect snapshot. Choose a deletion policy that fits your repository.
  • Versions: tested against Kubernetes 1.37 at the API level (envtest) and 1.36 end-to-end (k3s, which has no stable 1.37 release yet). Other versions may work but are not in the matrix. Running the image needs no Go, but anything importing this repo as a module (pkg/manifestanalyzer, for example) is bound by the go directive in go.mod, which is the source of truth. That floor can move in any release, including a patch release; when it does, the release notes say so.

It runs one replica, with no standby failover: the chart rejects replicaCount > 1 rather than let two instances write the same repository. Ownership coordination and a durable worker queue are on the way to 1.0. The backlog is in docs/TODO.md, and longer-range directions in docs/future/.

Rather have it managed?

ConfigButler can run a small, secure, public-facing Kubernetes API for you: we operate GitOps Reverser and authorize your end users, with forward-deployed engineers to get you started. You keep a clean, self-owned Git repo where your users express their intent.

Documentation and feedback

Trying it with a real repo? Open an issue with the layout you tried, the diff you expected, and what happened. Install attempts, first-commit experience, audit delivery, Git output shape, and CRD ergonomics are the most useful reports at this stage. Contributions are welcome; see CONTRIBUTING.md.

Or connect on LinkedIn: feedback, questions, and ideas are all welcome.

Licensed under Apache 2.0.

Directories

Path Synopsis
api
v1alpha3
Package v1alpha3 contains API Schema definitions for the configbutler.ai v1alpha3 API group.
Package v1alpha3 contains API Schema definitions for the configbutler.ai v1alpha3 API group.
cmd
gitea-signing-debug command
gitea-signing-debug drives the full SSH-signed-commit flow against a live Gitea and reports Gitea's commit verification verdict at every stage.
gitea-signing-debug drives the full SSH-signed-commit flow against a live Gitea and reports Gitea's commit verification verdict at every stage.
manifest-analyzer command
manifest-analyzer is a standalone, read-only CLI that analyzes a folder of Kubernetes manifests.
manifest-analyzer is a standalone, read-only CLI that analyzes a folder of Kubernetes manifests.
mutation-capture-lab command
Command mutation-capture-lab is the mutation-capture lab binary: a minimal set of recorders (native watch, audit webhook, validating admission webhook) whose output is a versioned corpus of the exact structures Kubernetes emits.
Command mutation-capture-lab is the mutation-capture lab binary: a minimal set of recorders (native watch, audit webhook, validating admission webhook) whose output is a versioned corpus of the exact structures Kubernetes emits.
hack
crdfields command
Command crdfields renders the settings index in docs/configuration.md from the CRD schemas in config/crd/bases, so the table cannot drift from the API.
Command crdfields renders the settings index in docs/configuration.md from the CRD schemas in config/crd/bases, so the table cannot drift from the API.
doccheck command
Command doccheck verifies that every documentation reference in the repository resolves to a file that exists.
Command doccheck verifies that every documentation reference in the repository resolves to a file that exists.
internal
attribution
Package attribution holds the per-audit-route bookkeeping the attribution pipeline shares across its two halves: the audit ingress that PUBLISHES facts and the watch resolver that READS them.
Package attribution holds the per-audit-route bookkeeping the attribution pipeline shares across its two halves: the audit ingress that PUBLISHES facts and the watch resolver that READS them.
audit/outcome
Package outcome defines the single, bounded vocabulary for what the audit ingestion pipeline did with one event — its "outcome" — and records it on the gitopsreverser_audit_events_total counter.
Package outcome defines the single, bounded vocabulary for what the audit ingestion pipeline did with one event — its "outcome" — and records it on the gitopsreverser_audit_events_total counter.
auditutil
Package auditutil contains shared Kubernetes audit-event parsing helpers.
Package auditutil contains shared Kubernetes audit-event parsing helpers.
authz
Package authz holds the ClusterProvider namespace-admission decision.
Package authz holds the ClusterProvider namespace-admission decision.
controller
Package controller contains shared constants for all controllers.
Package controller contains shared constants for all controllers.
git
Package git provides Git repository operations and abstractions for the GitOps Reverser controller.
Package git provides Git repository operations and abstractions for the GitOps Reverser controller.
git/manifestedit
Package manifestedit is an isolated proof of concept for the manifest-inventory "file-agnostic placement" feature.
Package manifestedit is an isolated proof of concept for the manifest-inventory "file-agnostic placement" feature.
giteaclient
Package giteaclient is a small, focused Gitea REST client used by e2e tests and debug tools.
Package giteaclient is a small, focused Gitea REST client used by e2e tests and debug tools.
kubeconfig
Package kubeconfig resolves and safety-checks a source-cluster kubeconfig held in a Secret, shared by the watch data plane's SourceClusterResolver and the GitTarget controller's Validated gate so both apply exactly one contract: Flux's value→value.yaml key order, and a REJECT — not Flux's silent strip — of exec auth providers and insecure-skip-tls-verify.
Package kubeconfig resolves and safety-checks a source-cluster kubeconfig held in a Secret, shared by the watch data plane's SourceClusterResolver and the GitTarget controller's Validated gate so both apply exactly one contract: Flux's value→value.yaml key order, and a REJECT — not Flux's silent strip — of exec auth providers and insecure-skip-tls-verify.
layoutfixture
Package layoutfixture reads the expected-*-status.yaml fixtures under test/fixtures/layout-corpus.
Package layoutfixture reads the expected-*-status.yaml fixtures under test/fixtures/layout-corpus.
manifestanalyzer
Package manifestanalyzer is a runtime-independent analyzer for a folder of Kubernetes manifests.
Package manifestanalyzer is a runtime-independent analyzer for a folder of Kubernetes manifests.
manifestreport
Package manifestreport is the integration layer that drives the cluster-free manifestedit library against a real repository and cluster state.
Package manifestreport is the integration layer that drives the cluster-free manifestedit library against a real repository and cluster state.
mutationlab
Package mutationlab holds the mutation-capture lab: a small, separate application that records the exact structures Kubernetes exposes through native watches, audit webhooks, and validating admission webhooks, and commits those structures as a versioned corpus.
Package mutationlab holds the mutation-capture lab: a small, separate application that records the exact structures Kubernetes exposes through native watches, audit webhooks, and validating admission webhooks, and commits those structures as a versioned corpus.
mutationlab/corpus
Package corpus turns captured records into the browsable golden tree: one directory per scenario, one file per emitted moment, named so an ordered fan-out is self-describing (watch.deleted.cm-a.yaml, ...).
Package corpus turns captured records into the browsable golden tree: one directory per scenario, one file per emitted moment, named so an ordered fan-out is self-describing (watch.deleted.cm-a.yaml, ...).
mutationlab/labserver
Package labserver assembles the lab's HTTP surface: the read/clear records API and the health endpoint.
Package labserver assembles the lab's HTTP surface: the read/clear records API and the health endpoint.
mutationlab/normalize
Package normalize rewrites the volatile fields of captured Kubernetes payloads to stable, relational placeholders so the lab corpus changes only when behavior changes — never when a run merely produces fresh UIDs, resource versions, or timestamps.
Package normalize rewrites the volatile fields of captured Kubernetes payloads to stable, relational placeholders so the lab corpus changes only when behavior changes — never when a run merely produces fresh UIDs, resource versions, or timestamps.
mutationlab/recorder
Package recorder turns live Kubernetes observations — audit webhook posts, admission reviews, and native watch events — into mutationlab.Record values in the store.
Package recorder turns live Kubernetes observations — audit webhook posts, admission reviews, and native watch events — into mutationlab.Record values in the store.
mutationlab/store
Package store is the lab's in-memory record store.
Package store is the lab's in-memory record store.
rulestore
Package rulestore manages the in-memory cache of compiled WatchRule configurations.
Package rulestore manages the in-memory cache of compiled WatchRule configurations.
sanitize
Package sanitize removes server-generated fields from Kubernetes objects.
Package sanitize removes server-generated fields from Kubernetes objects.
ssh
Package ssh provides SSH authentication utilities for Git operations.
Package ssh provides SSH authentication utilities for Git operations.
telemetry
Package telemetry provides the OpenTelemetry-based metrics exporter for GitOps Reverser.
Package telemetry provides the OpenTelemetry-based metrics exporter for GitOps Reverser.
types
Package types provides common type definitions used across the GitOps Reverser.
Package types provides common type definitions used across the GitOps Reverser.
typeset
Package typeset is GitOps Reverser's single decision surface for "is this resource type followable, and if not, what is the one reason it is not?".
Package typeset is GitOps Reverser's single decision surface for "is this resource type followable, and if not, what is the one reason it is not?".
watch
Package watch drives the api-source-of-truth reconcile: it keeps the followability registry and the per-GitTarget watched-type tables fresh, and mirrors each watched type into Git from a long-lived Kubernetes watch per claimed (GitTarget, GVR, scope).
Package watch drives the api-source-of-truth reconcile: it keeps the followability registry and the per-GitTarget watched-type tables fresh, and mirrors each watched type into Git from a long-lived Kubernetes watch per claimed (GitTarget, GVR, scope).
yamlstyle
Package yamlstyle owns the one YAML serialization style this product writes to Git.
Package yamlstyle owns the one YAML serialization style this product writes to Git.
pkg
manifestanalyzer
Package manifestanalyzer is the public answer to two questions a tool built around GitOps Reverser needs to ask about a Git repository, without a cluster and without writing anything:
Package manifestanalyzer is the public answer to two questions a tool built around GitOps Reverser needs to ask about a Git repository, without a cluster and without writing anything:
test
e2e
Package e2e provides helper functions for end-to-end testing of the GitOps Reverser controller.
Package e2e provides helper functions for end-to-end testing of the GitOps Reverser controller.
e2e/tools/ginkgo-allure command
ginkgo-allure converts Ginkgo JSON reports into Allure result files.
ginkgo-allure converts Ginkgo JSON reports into Allure result files.
e2e/tools/spec-timings command
spec-timings reads a Ginkgo JSON report and prints a duration-sorted table of specs with each spec's share of the total wallclock.
spec-timings reads a Ginkgo JSON report and prints a duration-sorted table of specs with each spec's share of the total wallclock.
e2e/tools/ts command
ts prefixes each stdin line with the elapsed seconds since startup and the current wallclock, then writes it to stdout.
ts prefixes each stdin line with the elapsed seconds since startup and the current wallclock, then writes it to stdout.
loadtest command
SPDX-License-Identifier: Apache-2.0
SPDX-License-Identifier: Apache-2.0
mutationlab/e2e
Package e2e drives the mutation-capture lab against a live cluster: it runs each scenario, drains the lab's records API, and compares (or, with MUTATIONLAB_UPDATE=1, rewrites) the golden corpus.
Package e2e drives the mutation-capture lab against a live cluster: it runs each scenario, drains the lab's records API, and compares (or, with MUTATIONLAB_UPDATE=1, rewrites) the golden corpus.
utils
Package utils provides utility functions for e2e testing.
Package utils provides utility functions for e2e testing.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL