Crossplane platform APIs (XRDs + Compositions) — pipeline head; codeberg someara/platform-seanfarm is the flux source + rebuild seed
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Sean OMeara f6599ee180
All checks were successful
binjovi/ci Binjovi completed the frozen plan
fix(platform): orphan the shared OpenBaoConnection on XR retirement
The openbao-connection composed Object sets no deletionPolicy, so it takes
the provider default, Delete. Retiring this XR would therefore delete the
shared OpenBaoConnection record.

Five live OpenBaoDynamicSecrets resolve credentials through that record, by
name, over two hops: OpenBaoDynamicSecret -> openbaoAuthRef -> OpenBaoAuth ->
openbaoConnectionRef openbao/openbao. They are forgejo-db-user, n8n-secrets,
hydra-secrets, kratos-secrets and outline-database, across four tenant
namespaces. Deleting the record breaks credential renewal in all four until
Flux restores it.

The CR is already declared in seanfarm
kubernetes/flux/infrastructure/openbao-platform, so Orphan hands it to Flux
instead of destroying it.

The other nine composed OpenBao CRs are unaffected: they carry an inline
openbaoRef.connection.address, not a named reference to this record.

Nothing changes while the XR exists. A deletion policy only takes effect when
the Object is removed.

Claude-Session: https://claude.ai/code/session_01GfkEuwuvGSqGyXVuxwT7PA
2026-09-14 18:24:09 +02:00
apis fix(platform): orphan the shared OpenBaoConnection on XR retirement 2026-09-14 18:24:09 +02:00
crossplane-rbac refactor: rename openbao-resource-operator -> openbao-operator; bump v0.11.0 2026-06-10 18:28:44 +02:00
docs docs: record Binjovi GitOps shadow 2026-08-28 17:06:35 +02:00
instances feat(zelnet): retire platform branch protection 2026-09-05 02:54:50 +02:00
instances-post feat(zelnet): retire platform branch protection 2026-09-05 02:54:50 +02:00
tests feat(zelnet): retire platform branch protection 2026-09-05 02:54:50 +02:00
.gitattributes chore: adopt semver (VERSION + CHANGELOG) for the gitops-flux release beat 2026-07-06 16:47:47 +02:00
CHANGELOG.md feat(portal): link Tetragon dashboard 2026-08-05 13:31:30 +02:00
LICENSE chore: add Apache-2.0 LICENSE + NOTICE 2026-06-16 09:20:13 +00:00
NOTICE chore: add Apache-2.0 LICENSE + NOTICE 2026-06-16 09:20:13 +00:00
README.md docs: describe native platform build and deploy ownership 2026-09-06 15:12:36 +02:00
VERSION chore: devbump 0.1.126 -> 0.1.127-dev 2026-07-27 14:06:56 +00:00

platform-seanfarm

This repository holds Crossplane XRDs and Compositions for the SeanFarm platform.

The seanfarm cluster uses this repository. Flux pulls the apis/ and instances/ directories. Crossplane builds the platform when a user applies a claim.

Layout

Path Purpose
apis/<name>/ One XRD + Composition + Kustomization per platform API
crossplane-rbac/ ClusterRoles aggregated into Crossplane (e.g. openbao.sean.farm/* permissions)
instances/ Cluster claims — Cluster, Platform, Stack, PlatformUser, PlatformGroup, GitMirror
package/ Reserved for future Crossplane Configuration packaging
docs/ Crossplane primer and other API-level reference

Compositions

Layered roughly from cluster-wide down to per-user:

Layer API kind Purpose
Cluster XCluster Tier-1 shared infra: openebs-zfs, cert-manager, cnpg, external-secrets, minio-operator, alloy, openbao-operator, etc. — wired via Flux Kustomizations.
Platform singleton XPlatform Instantiated exactly once (Platform/seanfarm). Owns the SHARED stateful backends + cluster-wide secrets/TLS authority: the XPostgres/XMinIO singletons below, the OpenBao consumer CRs (internal-CA PKI root+intermediate, transit/cosign key, policies), and the GCP secrets-engine mount. Goes Ready before the tenant Stacks.
Platform → backend XPostgres The SHARED CNPG Cluster — instantiated once by XPlatform as name=platform → ns cnpg (5 instances). Per-tenant DBs are logical slices via TenantDatabase.
Platform → backend XMinIO The SHARED MinIO Tenant — instantiated once by XPlatform as name=platformminio (5 servers × 1 volume, EC:3+2). Per-tenant buckets are logical slices via TenantBucket.
Stack (per-tenant fan-out) XStack Top-level per-tenant claim that fans out into the per-tenant logical slices + app components below.
Stack → slice XTenantDatabase Per-tenant {tenant}_{app} databases + OpenBao-rotated roles in the shared CNPG.
Stack → slice XTenantBucket Per-tenant {tenant}-* buckets + a scoped MinIO key in the shared MinIO.
Stack XPortal Portal nginx + the edge Gateway with all olly/forgejo/argocd/registry/workflows certs. In practice the CLUSTER edge, not a tenant's — see apis/stack/README.md.
Stack XForgejo Forgejo Deployment + admin bootstrap + OIDC config Job + admin token. Lands in the bare forgejo singleton namespace.
Singleton XArgoCD ArgoCD Server/Controller + OIDC config. No longer a Stack child — claimed once as a singleton (instances/argocd-platform.yaml).
Per-user XPlatformUser KratosIdentity + ForgejoUser per (user, stack).
Per-group XPlatformGroup ForgejoTeam per (group, stack).
Per-repo XGitMirror One-shot ForgejoRepository import + a push-only backup CronWorkflow (Forgejo → codeberg) + a pipeline-bot ForgejoCollaborator.

Retired APIs. The team deleted ten apis/ directories. Their concerns moved out of Crossplane. This list stays here because the table above advertised them long after they were gone. XOIDCProvider moved from apis/oidc to ArgoCD sean/ory. XMetrics, XLogs, XTraces, and XGrafana moved from apis/{metrics,logs,traces,grafana} to ArgoCD sean/olly. XKomoplane and XTektonDashboard moved to sean/olly. XRegistry and the registry sibling of XStaticSite moved from apis/registry to ArgoCD sean/pipelines. XWorkflows moved from apis/workflows to sean/pipelines. apis/obsbucket was deleted. apis/openbao moved to Flux raw manifests. apis/vault was deleted.

OpenBao is the cluster secrets backend. It replaced HashiCorp Vault on 2026-06-03. OpenBao has no Crossplane composition. Flux deploys its server from the seanfarm repo (kubernetes/flux/infrastructure/openbao) into openbao (plus a per-tenant openbao-{tenant}). XPlatform owns only the OpenBao consumer CRs: the shared OpenBaoConnection address record, plus PKI, transit, policy, database, and GCP CRs.

Lifecycle

Bootstrap order is Cluster → Platform → Stacks. The Flux layer enforces this order. XCluster lands the operators. Then the Platform/seanfarm singleton brings up the shared backends and the CA. Then each tenant Stack fans out its slices and apps.

Cluster claim (instances/seanfarm-cluster.yaml)
    └─> XCluster/seanfarm            → operators + Flux-wired tier-1 infra

Platform claim (instances/platform.yaml)
    └─> XPlatform/seanfarm
            ├─> XPostgres/platform   → cnpg  (shared CNPG, 5 instances)
            ├─> XMinIO/platform      → minio (shared MinIO, EC:3+2)
            ├─> shared OpenBaoConnection + internal-CA PKI + transit/cosign + GCP mount
            └─> (OpenBao SERVER itself is Flux-deployed, not Crossplane)

Stack claim (instances/alice.yaml)
    └─> XStack/alice
            ├─> XTenantDatabase/alice  → alice_{app} DBs in the shared CNPG
            ├─> XTenantBucket/alice    → alice-* buckets in the shared MinIO
            ├─> XPortal/alice          → portal-alice   (the cluster edge Gateway)
            ├─> XForgejo/alice         → forgejo        (bare singleton ns)
            └─> the cosign signer bridge (OpenBaoKubernetesAuthRole + Role/RoleBinding in kyverno)

Singleton claims (not Stack children)
    ├─> XArgoCD/platform     (instances/argocd-platform.yaml)  → argocd-platform
    └─> XGitMirror × 28      (instances/gitmirrors-alice.yaml) → forgejo + workflows

The Stack now has only five composed resources. Ory, observability, n8n, ArgoCD, DNS, registry, workflows, buildkit, and pkg all moved to ArgoCD or Flux singletons during the 2026-07 collapse. See docs/platform-collapse/README.md in the seanfarm repo.

Each XR's Composition runs the function-patch-and-transform pipeline. The pipeline takes a static base: manifest with literal placeholder sentinels and overwrites them with patches:. See docs/CROSSPLANE-PRIMER.md for how patches and the placeholder pattern work.

Iterating

# In the cluster repo (~/src/seanfarm)
cd ~/src/platform-seanfarm
$EDITOR apis/oidc/composition.yaml
git commit -am "fix(oidc): ..." && git push

# Back in the cluster repo
cd ~/src/seanfarm
KUBECONFIG=./kubeconfig flux reconcile source git platform-seanfarm
KUBECONFIG=./kubeconfig flux reconcile kustomization platform-seanfarm-apis

A schema removal can leave orphan fields on existing XRs. Validation then refuses to bind the claim. To fix this, nudge each affected claim:

kubectl annotate <claim>/<name> crossplane.io/paused=false --overwrite

Prerequisites

The seanfarm cluster already has these installed. This list is for reference only.

  • Crossplane v2.x
  • function-patch-and-transform
  • provider-kubernetes, with a default ProviderConfig
  • provider-gcp, for the XCluster-managed cert-manager and external-dns paths
  • openbao-operator, which manages the openbao.sean.farm/* CRDs that postgres and ESO use
  • forgejo-operator, which manages ForgejoRepository, ForgejoUser, ForgejoTeam, and similar CRDs
  • kratos-identity-operator, which manages KratosIdentity

License

MIT

This repo flows through Binjovi (gitops-flux, 2026-08-29)

Like seanfarm, this repo is a gitops-flux project. Forgejo sean/platform-seanfarm is the writable head. Codeberg stays the LIVE flux source and rebuild seed. Only the deploy's fast-forward-only push advances codeberg. Routine work uses an ordinary Forgejo pull request into trunk. Binjovi creates one Build for the exact pull request revision. A requested Release first tries a fast-forward-only merge for each selected Build. If an earlier train car advanced trunk, Binjovi uses the permitted rebase merge for the next car. It tests the combined revision once, then advances main. The default Forgejo merge style stays fast-forward-only. Merge commits, rebase-merge commits, and squash merges stay disabled. The typed Deploy advances Codeberg and waits for the four Flux Kustomizations to report Ready at that revision.

The typed Deploy uses the native platform Flux agent as a non-root service identity. It mounts the repository-bounded Codeberg credential read-only for that worker. Project Build code cannot read or replace the deploy credential or command. The seanfarm Flux boundary decrypts that SOPS-managed credential before it creates the Secret. The agent must never receive literal ENC[...] ciphertext as a key or known_hosts file.

The deploy gates on this repo's 4 flux Kustomizations (apis, instances, instances-post, rbac) reaching Ready@sha. Break-glass codeberg pushes still work, because flux applies them. The next deploy's drift gate catches them. To recover, run make -C ../seanfarm seanfarm-resync REPO=platform-seanfarm.

The build policy and recipe metadata are in sean/binjovi-plans. Each build uses the active plan revision. Change that repository to adjust validation without a Binjovi runtime deployment. PR and combined-trunk builds use native agents. Deployment success requires Flux to report the exact release revision.