railiance-platform/docs/argocd-gitops.md
codex 4ca88a14f0
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Has been cancelled
RPF-WP-0044: T01 done; exact T02/T03 commands pinned to c3ebd6d.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 63291@bnt-lap001
Assistant-Session: 8bd77868-ca68-4f49-bb1e-d539ecc0d703
2026-09-21 19:05:56 +02:00

11 KiB

ArgoCD GitOps Contract

Railiance ArgoCD is the cluster sync engine. This repo owns only the shared platform contract around GitOps trust, guardrails, and OpenBao-backed secret delivery. Application workload manifests remain in the owning application repos.

Ownership Boundary

railiance-platform owns:

  • ArgoCD AppProject guardrails for Railiance tenant workloads.
  • The root app-of-apps entrypoint.
  • Repository credential registration templates.
  • Secret delivery conventions through OpenBao and External Secrets Operator.

Tenant repos own:

  • Container images.
  • Workload manifests under k8s/railiance/.
  • The proposed ArgoCD Application manifest for platform review.
  • Application config that contains no secret values.

Cluster/runtime ownership remains outside this repo: installing or upgrading ArgoCD itself belongs with the cluster layer.

Production Change Path (founder decision, 2026-09-21)

Source: the-custodian/docs/kubernetes-change-gate-decision.md. Bernd Worsch, the founder, decided it exercising GOVERN @ estate. Terms follow SecurityCanon Mode of Authority v0.2.0, which is still a draft.

The railiance-apps-root lane in this repository is the change path for workloads whose ADR-0006 readiness_state is production-approved. The gate is a quality gate, not an authorization decision. No PDP sits in this path.

Target readiness state Path for a change Activation
declared, installed, verified Direct ADMINISTER @ realm:kubernetes (ops-mason) APPROVED: founder approval of the construction plan
production-approved CONSTRUCT @ the tenant manifest repository, plus a reviewed Application here; ArgoCD reconciles APPROVED for the change; the merge is the gate
production-approved, emergency Direct ADMINISTER BREAK_GLASS, recorded, then reconciled back into the repository

Platform objects that carry no readiness state default to the production tier. A change through git leaves external-audited evidence. A direct apply leaves only target-audited evidence. That difference is why production changes go through git.

What this means for platform review in this repository:

  • A merge to argocd/applications/ is a live production change. Once the commit is on main, railiance-apps-root syncs it with prune and self-heal (today on coulombcore; on railiance01 only after RPF-WP-0044 applies the bootstrap). Treat the merge as the APPROVED gate, not as a draft.
  • A new Application for a workload that is already running is an adoption. Follow the adoption steps in workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md: manual sync first, a diff, no resources finalizer, and the founder's go-ahead.
  • Many platform objects are still changed by direct Make targets: ClusterSecretStores on railiance01, OpenBao configuration and the bootstrap itself. Under this decision they default to the production tier. That is a declared gap (RPF-WP-0043-T05), not conformance.

Transition. rapp-policy-nexus is production-approved but not yet an Application here. Until 2026-12-21 its changes keep activation=APPROVED by founder plan approval, and each one is recorded as production-tier. Onboarding is due by 2026-12-21 (RPF-WP-0043).

ArgoCD on railiance01: installed, not yet reconciling anything (2026-09-21). The custodian's read-only check found no ArgoCD on railiance01 (namespace and CRDs NotFound; RPF-WP-0043-T01 evidence). The founder then decided ArchitectureBlueprint §5.6 as option 1, adopt properly, and Argo CD Core v3.5.3 was installed there (phase A). As of this record it has 0 AppProjects, 0 Applications and 0 repository Secrets: the bootstrap in argocd/bootstrap/ is not applied, so railiance-apps-root does not yet sync anything on railiance01. The records calling the controller "coulombcore ArgoCD" were right about the past; coulombcore's ArgoCD still reads argocd/applications/ until phase C retires it.

Do not apply the bootstrap as committed. The root app and all four children declare automated sync with prune and self-heal, so applying it would adopt all four live workloads at once. Phase B (RPF-WP-0044) first neutralises automated sync, then adopts one application at a time, each with its own founder go-ahead. Read-only diff evidence: docs/evidence/argocd-phase-b-adoption-diff-2026-09-21.json.

railiance01 uses its own path (founder's Option A, RPF-WP-0044-T01). argocd/railiance01/bootstrap/ declares the railiance01 AppProjects and a root app with no automated block, reading argocd/railiance01/applications/ (empty until an adoption task). Child drafts wait in argocd/railiance01/drafts/, which no root syncs. Never apply argocd/bootstrap/ (coulombcore's, automated) on railiance01; it and argocd/applications/ stay unchanged for coulombcore until phase C.

Bootstrap Layout

argocd/bootstrap/
  00-railiance-bootstrap-project.yaml
  01-railiance-tenants-project.yaml
  10-railiance-apps-root.application.yaml

argocd/applications/
  *.application.yaml

argocd/repositories/
  *.repository.sops.yaml

The bootstrap is applied once by an operator. If the Git source is private, apply the encrypted railiance-platform repository Secret first so the root Application can sync this repo:

ARGOCD_REPOSITORY_SECRET=argocd/repositories/railiance-platform.repository.sops.yaml \
  make argocd-repo-apply
make argocd-bootstrap-dry-run
make argocd-bootstrap-deploy
make argocd-status

After that, railiance-apps-root syncs tenant Application manifests from argocd/applications/.

Repository Registration

Every Git source repo used by ArgoCD must be registered in the argocd namespace with an ArgoCD repository Secret. Use one read-only deploy token or deploy key per repo unless an operator approves a narrower shared credential model.

Repository credentials are operator credentials, not workload secrets. Store their source material in OpenBao under:

platform/operators/argocd/repositories/<repo-name>

Create an encrypted repository Secret from the matching template:

cp argocd/repositories/issue-core.repository.sops.yaml.template \
  argocd/repositories/issue-core.repository.sops.yaml
sops -e -i argocd/repositories/issue-core.repository.sops.yaml

Apply only encrypted files:

ARGOCD_REPOSITORY_SECRET=argocd/repositories/issue-core.repository.sops.yaml \
  make argocd-repo-apply

Do not commit plaintext deploy tokens, passwords, SSH private keys, OpenBao tokens, or ArgoCD API tokens.

Tenant Application Contract

Tenant Applications are thin routing manifests reviewed into argocd/applications/. The workload source remains in the tenant repo, normally:

k8s/railiance/

Default Application shape:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: example-service
  namespace: argocd
spec:
  project: railiance-tenants
  source:
    repoURL: https://forgejo.coulomb.social/coulomb/example-service.git
    targetRevision: main
    path: k8s/railiance
  destination:
    server: https://kubernetes.default.svc
    namespace: example-service
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
      - ApplyOutOfSyncOnly=true
      - PruneLast=true

Use sync waves only for real dependency ordering. Secret delivery resources should sync before Deployments that consume them.

Secret Delivery

OpenBao is the canonical runtime secret custody authority.

Default pattern:

  1. Store workload secret material in OpenBao.
  2. Use External Secrets Operator to materialize a Kubernetes Secret in the workload namespace.
  3. Reference that Kubernetes Secret from the Deployment, Job, or CronJob.

Path convention for workload credential custody:

platform/workloads/<tenant-or-org>/<workload>/<secret-purpose>

Kubernetes namespace and service-account bounds belong in the OpenBao auth role or External Secrets binding, not in the tenant segment unless the namespace is itself the approved workload identity.

Use CSI-mounted files only for workloads that need file references, sharper mount boundaries, or refresh behavior that should not rewrite application manifests. Do not use the OpenBao injector in the current deployment.

For issue-core, the expected custody shape is:

platform/workloads/issue-core/issue-core/issue-core-runtime

with properties such as:

ISSUE_CORE_API_KEY
GITEA_BACKEND_TOKEN

The exact ExternalSecret manifest belongs with issue-core workload manifests, because it is part of that service's runtime deployment.

AppProject Guardrails

railiance-bootstrap allows the root app to manage ArgoCD Application objects in the argocd namespace.

railiance-tenants allows ordinary namespaced workload resources and namespace creation. It does not allow tenant Applications to create CRDs, ClusterRoles, ClusterRoleBindings, or other cluster-admin resources.

If a tenant needs a cluster-scoped platform resource, create a new platform-owned workplan instead of broadening the tenant project by default.

Platform Add-ons

External Secrets Operator is a platform-owned add-on because it installs CRDs, webhooks, and cluster RBAC. Tenant Applications must not install or upgrade it.

The GitOps contract uses:

  • railiance-platform-addons AppProject for cluster add-ons.
  • external-secrets ArgoCD Application for the public Helm chart.
  • openbao-secretstore ArgoCD Application for the in-cluster OpenBao ClusterSecretStore on coulombcore (openbao only).
  • OpenBao Kubernetes auth role external-secrets-issue-core for the issue-core pilot.

ClusterSecretStore/openbao is limited to the issue-core namespace on coulombcore.

Railiance01 stores use the private railiance01 OpenBao service and are not managed by coulombcore ArgoCD. Apply per workload:

Store Bootstrap
openbao-forgejo Dedicated Kubernetes auth; docs/eso-auth-recovery.md
openbao-activity-core activity-core make openbao-eso-token-apply + kubectl apply -f …/openbao-activity-core.clustersecretstore.yaml
openbao-reuse Dedicated Kubernetes auth; docs/eso-auth-recovery.md
openbao-target-revenue Dedicated Kubernetes auth; docs/eso-auth-recovery.md

The three recovered stores no longer use the old *-eso-token-apply bootstrap targets. Do not recreate static delivery tokens for them.

Manifests live under argocd/platform-addons/openbao-secretstore/ but are excluded from the coulombcore kustomization. Broaden or add stores only with platform review.

Configure the OpenBao side without printing token values:

OPENBAO_TOKEN_FILE=~/.local/openbao/platform-admin.token \
  make openbao-configure-external-secrets-issue-core

OPENBAO_TOKEN_FILE=~/.local/openbao/platform-admin.token \
  make openbao-configure-external-secrets-activity-core

The helper keeps Kubernetes auth in local-reviewer mode: OpenBao rereads its own mounted service-account token and CA file instead of storing an expiring reviewer JWT.

Then sync ArgoCD and verify:

make argocd-bootstrap-deploy
make argocd-status
kubectl -n external-secrets get deploy,pod
kubectl get clustersecretstore.external-secrets.io openbao