railiance-platform/workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md
custodian-sync 288c40b9e9 chore(consistency): renormalize lifecycle state [auto]
Updated by fix-consistency on 2026-09-21:
  - workplan status: ready → active

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

220 lines
9 KiB
Markdown

---
id: RPF-WP-0043
type: workplan
title: "Onboard rapp-policy-nexus to the ArgoCD production lane"
domain: financials
repo: railiance-platform
status: active
flavor: planning
owner: railiance-platform
topic_slug: railiance
created: "2026-09-21"
updated: "2026-09-21"
due: "2026-12-21"
related: [RPF-WP-0022]
state_hub_workstream_id: "ec41a4bd-df18-5b07-9b63-ccb80d9f001c"
---
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
state_hub_task_id: "d6a7a480-6251-504a-aa5f-ab9c04fe1e79"
```
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/<date>-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
state_hub_task_id: "49143c5e-a455-526e-a26f-403dc2fb075a"
```
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
state_hub_task_id: "2afb1aac-66dd-5e1e-92e1-fc020a59a927"
```
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: <pinned commit>
path: helm/policy-nexus
helm:
releaseName: policy-nexus
valueFiles: [<agreed values file>]
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
state_hub_task_id: "8638fa73-439d-5dcd-85c7-81aa125170e1"
```
**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/<date>-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=<revision from T03>`. Otherwise
self-heal fights the rollback.
## Declare the platform's own direct-apply gap
```task
id: RPF-WP-0043-T05
status: todo
priority: medium
state_hub_task_id: "91431560-79d4-5352-a6b5-f12850315dd2"
```
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.