GitOps-managed desired state for the Foundry homelab Kubernetes cluster, reconciled by Argo CD.
  • Shell 57.8%
  • Open Policy Agent 28.7%
  • Makefile 7.3%
  • Go Template 6.2%
Find a file
Josh Butler c688b44f31
All checks were successful
Validate / Render, lint, scan, and test policy (push) Successful in 14s
feat(bootstrap): add guarded Foundry workflow
2026-08-05 21:59:37 +00:00
.forgejo/workflows chore: add initial GitOps repository scaffolding 2026-08-05 16:08:24 +00:00
.github/workflows chore: add initial GitOps repository scaffolding 2026-08-05 16:08:24 +00:00
applications/smoke/chart feat(smoke): convert test workload to Helm 2026-08-05 20:25:55 +00:00
bootstrap feat(argocd): wire the Foundry app-of-apps 2026-08-05 21:54:18 +00:00
clusters/foundry feat(argocd): wire the Foundry app-of-apps 2026-08-05 21:54:18 +00:00
docs feat(bootstrap): add guarded Foundry workflow 2026-08-05 21:59:37 +00:00
policies/conftest feat(argocd): wire the Foundry app-of-apps 2026-08-05 21:54:18 +00:00
scripts feat(bootstrap): add guarded Foundry workflow 2026-08-05 21:59:37 +00:00
.editorconfig chore: add initial GitOps repository scaffolding 2026-08-05 16:08:24 +00:00
.gitattributes feat(argocd): add pinned bootstrap package 2026-08-05 21:46:21 +00:00
.gitignore chore: add initial GitOps repository scaffolding 2026-08-05 16:08:24 +00:00
.gitleaks.toml chore: add initial GitOps repository scaffolding 2026-08-05 16:08:24 +00:00
.pre-commit-config.yaml chore: add initial GitOps repository scaffolding 2026-08-05 16:08:24 +00:00
.yamllint.yaml chore: add initial GitOps repository scaffolding 2026-08-05 16:08:24 +00:00
CONTRIBUTING.md feat(bootstrap): add guarded Foundry workflow 2026-08-05 21:59:37 +00:00
Makefile feat(bootstrap): add guarded Foundry workflow 2026-08-05 21:59:37 +00:00
mise.toml chore: add initial GitOps repository scaffolding 2026-08-05 16:08:24 +00:00
README.md feat(bootstrap): add guarded Foundry workflow 2026-08-05 21:59:37 +00:00
renovate.json5 chore: add initial GitOps repository scaffolding 2026-08-05 16:08:24 +00:00
versions.yaml feat(bootstrap): add guarded Foundry workflow 2026-08-05 21:59:37 +00:00

Anvil

Anvil is the GitOps-managed desired state for the Kubernetes cluster running on foundry.

This repository intentionally starts small. It contains a secure sample workload, a pinned Argo CD package, and manual-sync Argo Applications. It does not contain credentials or a CI workflow that talks to Kubernetes. Nothing has been applied to a cluster by building this repository.

The mental model

Anvil will have three distinct layers:

  1. foundry and Ansible create a healthy host and K3s substrate.
  2. Argo CD reads this repository from its GitHub recovery mirror.
  3. Kubernetes controllers turn the declarations in this repository into running workloads and, later, selected external cloud resources.

CI sits before Argo CD. It renders and checks declarations, but never applies them.

What exists now

applications/smoke/chart/                Local Helm chart and foundry values
bootstrap/argocd/                         Pinned Argo CD installation package
bootstrap/root/                           One-time root Application package
clusters/foundry/                         Projects and child Applications
policies/conftest/                        Repository safety rules and tests
scripts/render.sh                         Turns charts into ordinary YAML
scripts/validate.sh                       Fast and full validation entry point
mise.toml                                 Exact local and CI tool versions
versions.yaml                             Human-readable version inventory
docs/decisions/                           Architecture decision records

The foundry-root Application reads clusters/foundry/ from the public GitHub mirror. That path defines separate platform and workload projects plus child Applications for Argo CD and the smoke chart. Every Application starts in manual sync mode and has no cascading deletion finalizer.

Chart versus values

The smoke chart answers: “What resources does this application need?” Its templates produce a Deployment and a ClusterIP Service.

The chart's default values.yaml contains visible, reviewable runtime choices such as the image, replica count, probes, resources, and security contexts. The small values/foundry.yaml file contains Foundry-specific labels. Argo CD will own namespace creation and Pod Security labels in the next repository slice; the chart deliberately does not own a Namespace.

Rendering combines both layers:

make render

Inspect the result under .rendered/. Rendering is a useful learning step: the output is approximately what Argo CD will compare with the Kubernetes API.

Understanding the smoke workload

The sample uses podinfo, a small HTTP service:

  • The selected application version is recorded in versions.yaml and the image uses an explicit version tag rather than latest. Before this workload is connected to Argo, that reference should be upgraded to a registry digest; version tags are clearer than latest but can still be moved by a publisher.
  • A Deployment keeps one pod running.
  • Readiness decides when the pod may receive Service traffic.
  • Liveness lets Kubernetes restart an unhealthy process.
  • The ClusterIP Service is reachable only inside the cluster.
  • CPU and memory requests help scheduling; limits bound accidental consumption.
  • The pod receives no Kubernetes API token because it does not need one.
  • It runs as a non-root user with all Linux capabilities dropped, a read-only root filesystem, and the runtime-default seccomp profile.
  • There is no Ingress, persistent storage, cloud resource, or secret.

Validation workflow

For the checks that work with the repository's minimum installed dependencies:

make validate-fast

This rejects obvious mutable images and plaintext Kubernetes Secrets, then renders every application chart with its foundry values.

For the complete CI-equivalent suite, install mise, then install the exact tool versions and run validation:

make install-tools
mise exec -- make validate

Full validation adds:

  • YAML and shell linting.
  • GitHub Actions syntax validation.
  • Kubernetes 1.36 schema validation.
  • Conftest policy tests and manifest policy evaluation.
  • Working-tree secret scanning.

The Forgejo and GitHub workflows run the same make validate command. Keeping the logic in repository scripts makes local behavior visible and avoids two different CI implementations.

Safe change loop

  1. Read the relevant chart templates, values, and decision record.
  2. Make one small declarative change.
  3. Run make render and inspect the generated YAML.
  4. Run mise exec -- make validate.
  5. Open a Forgejo pull request and review the rendered behavior, not only the source diff.
  6. Merge only after CI passes.

Once Argo CD exists, deployment will still happen by merging reviewed Git—not by running kubectl apply from a workstation or CI.

Bootstrap

The repository is prepared for a guarded, one-time Argo CD bootstrap. Plan mode contacts no cluster:

make bootstrap-plan CONTEXT=YOUR_CONTEXT

Execution is an explicit operator action and still leaves every Argo Application in manual sync mode. Read docs/bootstrap.md completely before running it. This repository build did not execute the bootstrap.

Repository boundary

The foundry repository owns Ubuntu, users, SSH, Tailscale, disks, mounts, packages, K3s installation and lifecycle, node enrollment, stable node labels, and transitional Docker Compose services.

Anvil owns Kubernetes namespaces, RBAC, Argo CD, Helm releases, Kustomize overlays, ingress, certificates, workloads, Kubernetes storage objects, Crossplane, policies, observability, backups, and Kubernetes recovery procedures.

See docs/architecture.md and the records in docs/decisions for the reasoning behind this structure.

Next slice

After the manual bootstrap and smoke synchronization have been observed and understood, the next safe slice is a shadow Traefik deployment on high NodePorts. It must not compete with Caddy for ports 80 and 443.