Skip to content

clcollins/gort

Repository files navigation

GORT — GitOps Reconciliation Tool

GORT

CI codecov

GORT closes the feedback loop after a merge to main. It watches for GitHub push events, polls Flux until reconciliation completes, and if anything goes wrong it uses a configurable AI provider to analyze the failure and automatically opens a fix PR.

Problem

After a merge to main there is no automated feedback loop verifying that Flux successfully reconciled declared resources, or that the result matches the intent described in docs/ plan documents. Failed deployments require manual investigation of Flux status, pod logs, and events — then manual authoring of a fix PR.

How It Works

GitHub push to main
       │
       ▼
GORT webhook handler (HMAC-validated)
       │  reads GitOpsWatcher CRDs to find matching apps
       ▼
Flux status polling (in-cluster, read-only K8s API)
       │  Kustomizations, HelmReleases, pods, logs, events
       ▼
       ├── Flux FAILURE ──► AI provider: analyze failure + plan docs
       │                               └──► GitHub API: open fix PR
       │
       └── Flux SUCCESS ──► Collect runtime state (pods, deployments, events)
                            └──► AI provider: validate intent vs plan docs
                                 ├── Intent MET: done
                                 └── Intent NOT MET ──► GitHub API: open fix PR

Interface Extensibility

Interface Default Extensible To
pkg/gitops.Client Flux CD ArgoCD, Rancher Fleet
pkg/vcs.Client GitHub GitLab, Gitea
pkg/ai.Client Claude (Anthropic), GitHub Models, Ollama OpenAI, Gemini

GitOps App Configuration (CRD)

GORT uses a GitOpsWatcher CRD to define which apps to watch:

apiVersion: gitops.gort.io/v1alpha1
kind: GitOpsWatcher
metadata:
  name: cluster-config
spec:
  type: flux
  appName: cluster-config       # Flux Kustomization name
  namespace: flux-system        # Namespace where Flux resources live
  targetRepo: clcollins/cluster-config  # Watch pushes to this repo
  fixRepo: clcollins/cluster-config     # Open fix PRs on this repo
  docsPaths:
    - docs/plans/               # Where to find plan documents
  reconcileTimeout: 10m

Observability

GORT runs two HTTP servers so that Prometheus scrape traffic is independent of webhook ingress:

Port Purpose Endpoints
8080 Webhook (application traffic) POST /webhook
8081 Metrics + health probes GET /metrics, GET /healthz, GET /readyz
  • /metrics — Prometheus metrics (port 8081); scraped via the ServiceMonitor in config/prometheus/
  • /healthz — liveness probe (port 8081)
  • /readyz — readiness probe (port 8081)
  • Alertmanager rules in config/alerting/alerts.yaml:
    • FluxReconcileFailed (critical)
    • FluxReconcileTimeout (warning)
    • ResourceDeploymentFailed (warning)
    • FixPRCreationFailed (warning)
    • IntentNotMet (warning)
    • IntentValidationError (warning)

Documentation Convention

Every PR to this repository should include a plan document in docs/plans/. CI checks that at least one plan document exists. Use descriptive filenames (e.g., setup-documentation.md); numeric prefixes are optional.

GORT follows the same convention when opening fix PRs on target repos.

Setup

1. GitHub Personal Access Token

GORT needs a GitHub token to read repositories and create fix PRs.

  1. Go to GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens
  2. Click Generate new token
  3. Set the Repository access to the repos GORT will manage
  4. Grant the permissions listed below
  5. Copy the token and set it as GORT_GITHUB_TOKEN

Note: If you plan to use GORT_AI_PROVIDER=github-models and want GORT_GITHUB_TOKEN to double as the models token, you must use a classic PAT instead (with repo and models:read scopes), since fine-grained PATs do not support the models:read scope. Alternatively, set GORT_GITHUB_MODELS_TOKEN to a separate classic PAT.

Required Fine-Grained PAT Permissions

Permission Access Why
Contents Read and write Read repo files and plan docs; create fix branches and commits
Pull requests Read and write Open fix PRs

2. GitHub Webhook

GORT receives push events via a GitHub webhook with HMAC signature validation.

  1. Generate a webhook secret:

    openssl rand -hex 32
  2. In your target repository, go to Settings → Webhooks → Add webhook

  3. Configure the webhook:

    Field Value
    Payload URL http://<your-gort-host>:8080/webhook (GORT does not terminate TLS; use a reverse proxy or Ingress for HTTPS)
    Content type application/json
    Secret The value generated in step 1
  4. Under Which events would you like to trigger this webhook?, select Just the push event

  5. Click Add webhook

  6. Set the same secret value as GORT_GITHUB_WEBHOOK_SECRET

3. AI Provider

GORT uses an AI provider to analyze failures and validate intent. Choose one:

Claude (default)

  1. Create an API key at console.anthropic.com
  2. Set GORT_CLAUDE_API_KEY to the key value
  3. Optionally set GORT_CLAUDE_MODEL (default: claude-sonnet-4-6)

GitHub Models (alternative)

  1. Set GORT_AI_PROVIDER=github-models
  2. Set GORT_GITHUB_MODELS_TOKEN to a GitHub token that can access GitHub Models (defaults to GORT_GITHUB_TOKEN if not set — a classic PAT with models:read scope is required; fine-grained PATs do not support this scope)
  3. Optionally set GORT_GITHUB_MODELS_MODEL (default: openai/gpt-4.1)

Ollama (local)

  1. Set GORT_AI_PROVIDER=ollama
  2. Optionally set GORT_OLLAMA_URL (default: http://localhost:11434)
  3. Optionally set GORT_OLLAMA_MODEL (default: llama3)

No API key required — Ollama runs without authentication by default.

Development

Prerequisites

  • Go 1.25.1+
  • Podman (or set CONTAINER_SUBSYS=docker)
  • markdownlint-cli2 (for markdown linting)

Quick Start

# Run all checks
make all

# Run tests only
make test

# Build binary
make build

# Build container image (podman)
make image-build

# Build with docker instead
CONTAINER_SUBSYS=docker make image-build

# Generate CRD manifests
make manifests

# Generate DeepCopy methods
make generate

Environment Variables

Variable Required Default Description
GORT_GITHUB_WEBHOOK_SECRET yes GitHub webhook HMAC secret (GORT_WEBHOOK_SECRET also accepted, deprecated)
GORT_GITHUB_TOKEN yes GitHub personal access token (fine-grained with Contents + Pull requests permissions, or classic with repo scope)
GORT_CLAUDE_API_KEY if GORT_AI_PROVIDER=claude Anthropic Claude API key
GORT_CLAUDE_MODEL no claude-sonnet-4-6 Claude model to use when GORT_AI_PROVIDER=claude
GORT_AI_PROVIDER no claude AI provider (claude, github-models, or ollama)
GORT_GITHUB_MODELS_TOKEN no GORT_GITHUB_TOKEN GitHub Models API token — either this or the fallback GORT_GITHUB_TOKEN must be a classic PAT with models:read scope
GORT_GITHUB_MODELS_MODEL no openai/gpt-4.1 Model to use when GORT_AI_PROVIDER=github-models
GORT_OLLAMA_URL no http://localhost:11434 Ollama server URL when GORT_AI_PROVIDER=ollama
GORT_OLLAMA_MODEL no llama3 Model to use when GORT_AI_PROVIDER=ollama
GORT_LISTEN_ADDR no :8080 Webhook server listen address (host:port, e.g. :8080 for all interfaces or 127.0.0.1:8080 for localhost only)
GORT_METRICS_ADDR no :8081 Metrics + health probe server listen address (host:port, e.g. :8081 for all interfaces or 127.0.0.1:8081 for localhost only)

Project Layout

cmd/gort/             — main entrypoint
internal/
  claudeai/           — Claude AI client (implements pkg/ai.Client)
  ghmodels/           — GitHub Models AI client (implements pkg/ai.Client)
  ollama/             — Ollama AI client (implements pkg/ai.Client)
  flux/               — Flux GitOps client (implements pkg/gitops.Client)
  github/             — GitHub VCS client (implements pkg/vcs.Client)
  k8s/                — Kubernetes client wrapper
  metrics/            — Prometheus metric definitions
  reconciler/         — Core reconcile orchestration (only non-pure package)
  webhook/            — HTTP webhook handler + pure parse functions
pkg/
  ai/                 — AI interface + types
  gitops/             — GitOps interface + types
  vcs/                — VCS interface + types
api/v1alpha1/         — GitOpsWatcher CRD Go types
config/
  crd/                — Generated CRD manifests
  rbac/               — ClusterRole + ClusterRoleBinding
  alerting/           — PrometheusRule manifest
  service/            — Kubernetes Service manifests (webhook + metrics)
  prometheus/         — ServiceMonitor for Prometheus Operator
docs/plans/           — Plan documents (required per PR)
hack/                 — Code generation scripts and headers

License

This project is licensed under the MIT License. Copyright © 2026 Christopher Collins.

Note on AI-Generated Content: This software was developed with the assistance of AI tools. To the extent that any AI-generated content incorporated in this software is protectable by copyright, the copyright holder asserts that such content is covered by, and licensed under, the MIT License.

The legal status of AI-generated content with respect to copyright is unsettled. This notice reflects the copyright holder's present intent and is subject to revision as law and legal interpretation develop.

About

GORT - GitOps Reconciliation Tool

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors