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
304 lines
11 KiB
Markdown
304 lines
11 KiB
Markdown
# 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
|
|
|
|
```text
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```text
|
|
platform/operators/argocd/repositories/<repo-name>
|
|
```
|
|
|
|
Create an encrypted repository Secret from the matching template:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```text
|
|
k8s/railiance/
|
|
```
|
|
|
|
Default Application shape:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```text
|
|
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:
|
|
|
|
```text
|
|
platform/workloads/issue-core/issue-core/issue-core-runtime
|
|
```
|
|
|
|
with properties such as:
|
|
|
|
```text
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
make argocd-bootstrap-deploy
|
|
make argocd-status
|
|
kubectl -n external-secrets get deploy,pod
|
|
kubectl get clustersecretstore.external-secrets.io openbao
|
|
```
|