- Shell 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
binjovi/ci Binjovi completed the frozen plan
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 |
||
| apis | ||
| crossplane-rbac | ||
| docs | ||
| instances | ||
| instances-post | ||
| tests | ||
| .gitattributes | ||
| CHANGELOG.md | ||
| LICENSE | ||
| NOTICE | ||
| README.md | ||
| VERSION | ||
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=platform → minio (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-transformprovider-kubernetes, with a defaultProviderConfigprovider-gcp, for theXCluster-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.