diff --git a/argocd/applications/README.md b/argocd/applications/README.md index 87d57e4..5e13b81 100644 --- a/argocd/applications/README.md +++ b/argocd/applications/README.md @@ -13,6 +13,13 @@ tenant workloads is: k8s/railiance/ ``` +Since the founder's decision of 2026-09-21, this directory is the change path +for `production-approved` workloads (see `docs/argocd-gitops.md` § Production +Change Path). A merge here is a live production change. Adopting a workload +that is already running (for example `rapp-policy-nexus`, RPF-WP-0043) needs the +founder's go-ahead and a diff first. Do not add an Application for a +running workload directly. + Do not commit Kubernetes Secret values, ArgoCD repository credentials, OpenBao tokens, deploy keys, or API keys here. diff --git a/docs/argocd-gitops.md b/docs/argocd-gitops.md index 78a0ed4..406749a 100644 --- a/docs/argocd-gitops.md +++ b/docs/argocd-gitops.md @@ -24,6 +24,69 @@ Tenant repos own: 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. + 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). + +**Not verified: that ArgoCD syncs on railiance01.** This repository does not +show it, and some records point the other way: + +- The only record of ArgoCD's health is the RPF-WP-0022 inbox evidence from + 2026-06: "installed and healthy on `railiance01`", with zero Applications. + That is from before the 2026-07-02 naming correction. Until then, "railiance01 + cluster" often meant coulombcore. +- The later records in this file and in + `argocd/platform-addons/openbao-secretstore/kustomization.yaml` name the + ArgoCD that runs these Applications "coulombcore ArgoCD". They say the + railiance01 stores are "not managed by coulombcore ArgoCD". +- Every Application uses `destination.server: https://kubernetes.default.svc`, + so it reconciles whichever cluster ArgoCD itself runs in. +- No evidence file in this repository records a sync of `railiance-apps-root` + on railiance01 (92.205.62.239). + +Confirming it takes an attended **read-only** check against railiance01, not +coulombcore: check the node IP, the `argocd` namespace, and the `railiance-apps-root` +sync and health status, for example `make argocd-status` with a kubeconfig whose +server is verified to be 92.205.62.239. Record the result as evidence. If ArgoCD +is not running there, the production row has no working path on railiance01 +yet. Installing ArgoCD belongs to the cluster layer and needs its own go-ahead +from the founder. This is RPF-WP-0043-T01. + ## Bootstrap Layout ```text diff --git a/workplans/README.md b/workplans/README.md index e043d74..5107dae 100644 --- a/workplans/README.md +++ b/workplans/README.md @@ -14,6 +14,7 @@ implementations or independent incidents. | [RPF-WP-0035](RPF-WP-0035-credential-lane-implementation.md) | Three remaining lanes: secrets-engine JWT, Fluid operator KV, KeyCape approval clients | Signing T04 is complete; T05 admission answered and awaiting owner approval plus a founder-attended window. | | [RPF-WP-0036](RPF-WP-0036-platform-service-assurance.md) | Implemented local assurance/admission; waits for recurring restore evidence, Q2 reception and owner handoff | Run the assurance commands; live acceptance and external ownership remain gated. | | [RPF-WP-0038](RPF-WP-0038-forgejo-scaleway-primary-coverage.md) | Native backup, full Scaleway archive recovery and 273 MiB Nextcloud essentials recovery verified; scheduled tier cutover remains | Bind recurring caller/dependencies, verified inventory, quota checks and separate owner retention. | +| [RPF-WP-0043](RPF-WP-0043-policy-nexus-argocd-onboarding.md) | Onboard production-approved rapp-policy-nexus to the ArgoCD lane by 2026-12-21; T01 confirms ArgoCD reconciles on railiance01 (unverified) | Plan only; adoption waits on the founder's go-ahead and rapp-policy-nexus's manifest decision. | RPF-WP-0036-T02/T05/T07 are complete; T03/T04/T06 retain the remaining diff --git a/workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md b/workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md new file mode 100644 index 0000000..e58355f --- /dev/null +++ b/workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md @@ -0,0 +1,214 @@ +--- +id: RPF-WP-0043 +type: workplan +title: "Onboard rapp-policy-nexus to the ArgoCD production lane" +domain: financials +repo: railiance-platform +status: ready +flavor: planning +owner: railiance-platform +topic_slug: railiance +created: "2026-09-21" +updated: "2026-09-21" +due: "2026-12-21" +related: [RPF-WP-0022] +--- + +The founder, Bernd Worsch, decided on 2026-09-21, exercising `GOVERN @ estate`, +that changes to `production-approved` workloads go through `CONSTRUCT @` the +manifest repository, reconciled by ArgoCD through `railiance-apps-root`. The +record is `the-custodian/docs/kubernetes-change-gate-decision.md`. Terms follow +SecurityCanon Mode of Authority v0.2.0, which is still a draft. + +`rapp-policy-nexus` is `production-approved` (reef-railiance +`bindings/rapps.yaml`), but no ArgoCD Application manages it. Its changes +today are Helm releases, `make deploy` in rapp-policy-nexus: `helm upgrade +--install policy-nexus` on railiance01, namespace `policy-nexus`. Until +2026-12-21 those changes keep `activation=APPROVED` by founder plan approval, +and each one is recorded as production-tier. This plan onboards policy-nexus by +2026-12-21. + +**This plan only prepares the change. It does not perform it.** Adding +`argocd/applications/policy-nexus.application.yaml` is a live production change. +Once it is on `main`, `railiance-apps-root` syncs it with prune and self-heal, +and ArgoCD takes ownership of running resources. Nothing in this plan is +applied, pushed as an Application, or synced without the founder's go-ahead +(T04). + +Out of scope: the legacy whynot-design npm location (RPF-WP-0035-T07). It +stays untouched and unwrapped while this lane moves. + +## Confirm ArgoCD actually reconciles on railiance01 + +```task +id: RPF-WP-0043-T01 +status: todo +priority: high +``` + +This repository cannot confirm it; see `docs/argocd-gitops.md` § Production +Change Path. The only record of ArgoCD's health is from 2026-06 and predates +the correction of the railiance01/coulombcore naming. Later records call the +controller "coulombcore ArgoCD", and all Applications target +`kubernetes.default.svc`. + +Attended, read-only, against a kubeconfig whose server is verified to be +92.205.62.239: + +1. `kubectl get nodes -o wide`: confirm the node IP. +2. `kubectl -n argocd get deploy,sts` and `make argocd-status`. +3. `kubectl -n argocd get application railiance-apps-root -o jsonpath='{.status.sync.status} {.status.health.status} {.status.sync.revision}'`. + Compare the revision with Forgejo `main`. + +Record the output as `docs/evidence/-argocd-railiance01-status.json`. +If ArgoCD is absent or not syncing on railiance01, stop this plan. Report to the +founder and the-custodian that the production row has no working path. An +ArgoCD install belongs to the cluster layer and needs its own go-ahead. + +## Agree where policy-nexus's manifests live + +```task +id: RPF-WP-0043-T02 +status: wait +priority: high +``` + +Per the tenant contract, workload manifests stay in the tenant repository. +`rapp-policy-nexus` owns that repository +(`forgejo.coulomb.social/coulomb/rapp-policy-nexus`) and decides this itself. +We coordinate through the hub and do not decide it here. + +Proposal to put to rapp-policy-nexus: + +- The source is the existing chart `helm/policy-nexus`. +- A committed values file, for example `k8s/railiance/values-railiance01.yaml`, + pins `image.digest`, `publicationManifestDigest`, `sourceInventoryDigest` and + `sourceSetDigest`. Today they are passed as `--set-string` at deploy time. + After adoption, a release is a commit that changes these pins. +- `helm.releaseName: policy-nexus`, so that rendered names and the + `app.kubernetes.io/instance` label match the live release. +- Once adopted, `make deploy` / `helm upgrade` stops being a production path. + It would fight self-heal. Whether to keep it for pre-production is for + rapp-policy-nexus to decide. + +Platform checks: + +- The chart renders Deployment, Service, ServiceAccount, Ingress and + NetworkPolicy. All five are in the `railiance-tenants` whitelist. +- Repository access: if the repository is private, it needs an ArgoCD + repository credential at `platform/operators/argocd/repositories/rapp-policy-nexus`. + That credential goes through its own CCR. No value is handled in this plan. + +**Unblock:** rapp-policy-nexus replies with the path, the values file, and +whether the repository is public. + +## Prepare the Application and the dry-run evidence + +```task +id: RPF-WP-0043-T03 +status: wait +priority: high +``` + +Draft the Application in this workplan or in a review branch, **not** under +`argocd/applications/` on `main`. Draft shape for adoption: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: policy-nexus # no finalizers: deletion must not cascade + namespace: argocd + labels: + app.kubernetes.io/part-of: railiance-gitops +spec: + project: railiance-tenants + source: + repoURL: https://forgejo.coulomb.social/coulomb/rapp-policy-nexus.git + targetRevision: + path: helm/policy-nexus + helm: + releaseName: policy-nexus + valueFiles: [] + destination: + server: https://kubernetes.default.svc + namespace: policy-nexus + syncPolicy: # adoption stage: no `automated` block + syncOptions: + - CreateNamespace=false + - ApplyOutOfSyncOnly=true + - PruneLast=true +``` + +Read-only evidence to collect before T04, in an attended session: + +1. `helm -n policy-nexus get values policy-nexus` and `helm history`: the live + digests and the current revision number, which is the rollback target. Do + not print anything else from the release. +2. Render the chart with the agreed values file. Run `kubectl diff + --server-side` of the render against the live namespace. Expected result: + no spec change, and only label or annotation differences. +3. List every object in `policy-nexus`. Mark which ones the render does not + contain, for example Helm release Secrets `sh.helm.release.v1.*`. Only + resources carrying ArgoCD's tracking label are pruned. Confirm that none of + the unrendered objects carry it. + +Stop conditions: any Deployment spec or image change, any object that the +render lacks but that carries the tracking label, or a digest mismatch. + +## Adopt policy-nexus (live, founder go-ahead) + +```task +id: RPF-WP-0043-T04 +status: wait +priority: high +``` + +**Waits on the founder's go-ahead.** This is a `CONSTRUCT @ railiance-platform` +change with `activation=APPROVED`. The merge is the gate, and it is a live +production change. Do not take it on the basis of this plan alone. + +1. Merge the Application exactly as in T03, with no `automated` sync. + `railiance-apps-root` creates the Application object. It does not touch + policy-nexus resources, because the child has no automated sync. +2. `argocd app diff policy-nexus`, or read the UI diff. It must match T03 + step 2. Any other result means stop, then roll back (step R1). +3. `argocd app sync policy-nexus` with pruning left off, which is the default + for a manual sync. Then run rapp-policy-nexus `make verify-live` with the + pinned digests. +4. After the workload has been Synced and Healthy for at least 24 hours, a + second merge adds `automated: {prune: true, selfHeal: true}`, matching the + tenant contract. This needs a second go-ahead. +5. Record evidence as `docs/evidence/-policy-nexus-argocd-adoption.json`. + Notify rapp-policy-nexus and ops-mason that policy-nexus is now in the + production row. Its transition exception ends. + +Rollback: + +- **R1, before step 4:** revert the merge commit. The root app prunes the + child Application object. The child has no finalizer, so the deletion does + not cascade and the workload keeps running unchanged. +- **R2, after step 4:** revert the automated-sync commit first, then R1. + Do not patch the child Application by hand, because the root app's + self-heal restores it from git. A hand patch is a direct `ADMINISTER` under + `BREAK_GLASS`. It is recorded and reconciled back into git. +- **Workload restore:** only after ArgoCD no longer manages policy-nexus, run + rapp-policy-nexus `make rollback REVISION=`. Otherwise + self-heal fights the rollback. + +## Declare the platform's own direct-apply gap + +```task +id: RPF-WP-0043-T05 +status: todo +priority: medium +``` + +Under the decision, platform objects without a readiness state default to +the production tier. This repository still changes several of them through +direct Make targets. Examples are the railiance01 ClusterSecretStores, +OpenBao configuration and the ArgoCD bootstrap. That is `target-audited` +evidence only. Inventory those targets, declare the gap as not conforming, +and propose to the founder which ones move to a reconciled lane and which +stay direct under `APPROVED`. Do not change a target's behaviour as part of +the inventory.