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
280 lines
12 KiB
Markdown
280 lines
12 KiB
Markdown
---
|
||
id: RPF-WP-0044
|
||
type: workplan
|
||
title: "ArgoCD phase B: adopt the four existing Applications on railiance01"
|
||
domain: financials
|
||
repo: railiance-platform
|
||
status: active
|
||
flavor: planning
|
||
owner: railiance-platform
|
||
topic_slug: railiance
|
||
created: "2026-09-21"
|
||
updated: "2026-09-21"
|
||
related: [RPF-WP-0043, RPF-WP-0022]
|
||
state_hub_workstream_id: "98140775-3b9a-5cf9-9af6-722502d487dc"
|
||
---
|
||
|
||
The founder, Bernd Worsch, decided ArchitectureBlueprint §5.6 on 2026-09-21,
|
||
exercising `GOVERN @ estate`: option 1, **adopt properly**. Phase A installed
|
||
Argo CD Core v3.5.3 on railiance01 (record:
|
||
`the-custodian/docs/kubernetes-change-gate-decision.md`). This plan is phase B:
|
||
bring the four Applications in `argocd/applications/` under that ArgoCD, **one
|
||
at a time, each with its own founder go-ahead**. Terms follow SecurityCanon
|
||
Mode of Authority v0.2.0 (draft).
|
||
|
||
**This plan changes nothing live.** Every task that would change railiance01
|
||
is `wait` on the founder. Each live step is `ADMINISTER @ realm:kubernetes/railiance01`
|
||
(bootstrap) or `CONSTRUCT @ railiance-platform` with the merge as the gate,
|
||
both `activation=APPROVED`.
|
||
|
||
Related, kept separate: `RPF-WP-0043` onboards `rapp-policy-nexus`, which is
|
||
not one of these four. It needs T02 of this plan (a working root app on
|
||
railiance01) before its own T04. Phase C, retiring coulombcore's ArgoCD, is
|
||
not planned here (T08).
|
||
|
||
## Read-only evidence, 2026-09-21
|
||
|
||
Full record: `docs/evidence/argocd-phase-b-adoption-diff-2026-09-21.json`.
|
||
Rendered locally (`helm template`, `kubectl kustomize`), then `kubectl diff`
|
||
client-side and `--server-side` dry run over `ssh railiance01`. Nothing was
|
||
applied.
|
||
|
||
| App | Live spec diff | Would prune | Repo credential | Blocker |
|
||
| --- | --- | --- | --- | --- |
|
||
| openbao-secretstore | none (1 ClusterSecretStore) | nothing | none, public | none |
|
||
| issue-core | none **after repointing** | nothing | **new**: `rapp-issue-core` is private | declared path `issue-core.git k8s/railiance` no longer exists |
|
||
| target-revenue | none on Deployment/Service/Ingress/ExternalSecret/Cluster | nothing | none, public | CNPG `Cluster` not in `railiance-tenants` whitelist; two Sync-hook Jobs |
|
||
| external-secrets | none (39 objects, 20 CRDs) | nothing now | none, public Helm repo | cluster-wide blast radius; CRDs must never be pruned |
|
||
|
||
Nothing is pruned at adoption because no live object carries an ArgoCD
|
||
tracking annotation; ArgoCD only prunes what it tracks. The one change every
|
||
adoption makes is that tracking annotation, on metadata only, so no rollout.
|
||
|
||
Sync settings found: `railiance-apps-root` and all four children declare
|
||
`automated: {prune: true, selfHeal: true}`; none has a finalizer. Applying
|
||
`argocd/bootstrap/` as committed would therefore adopt all four at once. T01
|
||
removes that before anything is applied.
|
||
|
||
Also found: the `external-secrets` Helm release is stuck at revision 7,
|
||
`pending-upgrade`, since 2026-07-07 (values identical to the Application);
|
||
`issue-core` is now server-side applied by `rapp-issue-core`;
|
||
`target-revenue` runs a mutable image tag (`0.1.4`). ArgoCD on railiance01
|
||
has 0 AppProjects, 0 Applications, 0 repository Secrets, and there is no
|
||
`argocd` CLI on the node, so the commands below use `kubectl` against the
|
||
Application resource. Not verified: repo-server egress to Forgejo and
|
||
`charts.external-secrets.io`; T02 proves it.
|
||
|
||
## Neutralise automated sync and fix the declared state
|
||
|
||
```task
|
||
id: RPF-WP-0044-T01
|
||
status: wait
|
||
priority: high
|
||
state_hub_task_id: "2d7230f5-50c8-5eee-a050-1501ce5af62a"
|
||
```
|
||
|
||
**Waits on the founder's choice of layout.** Repository-only; applying
|
||
nothing. But coulombcore's ArgoCD still syncs `argocd/applications/` on
|
||
`main`, so an edit there is a live change on coulombcore.
|
||
|
||
- **Option A (recommended):** a railiance01-only source path,
|
||
`argocd/railiance01/applications/`, starting empty, and the railiance01 root
|
||
pointing at it. `argocd/applications/` is left untouched for coulombcore
|
||
until phase C. No commit in this plan then reaches coulombcore.
|
||
- **Option B:** edit `argocd/applications/` in place. Every change also lands
|
||
on coulombcore's ArgoCD; needs its own go-ahead and a coulombcore read first.
|
||
|
||
Declared changes (Option A):
|
||
|
||
1. Root app for railiance01 with **no `automated` block** and path
|
||
`argocd/railiance01/applications`.
|
||
2. `railiance-tenants`: add `postgresql.cnpg.io/Cluster` to
|
||
`namespaceResourceWhitelist` (target-revenue).
|
||
3. Child Application drafts kept in this workplan until their task; each
|
||
without `automated`, without finalizers, with `targetRevision` pinned to a
|
||
commit or chart version.
|
||
4. `issue-core` draft repointed to `rapp-issue-core.git`, path `manifests`.
|
||
5. Add `argocd/repositories/rapp-issue-core.repository.sops.yaml.template`
|
||
(no value). Mark `issue-core.repository.sops.yaml.template` obsolete.
|
||
|
||
AppProjects and the root are applied by hand (T02), not synced by the root,
|
||
so these edits change nothing live until T02.
|
||
|
||
## Apply the AppProjects and the root app, automated sync off (live)
|
||
|
||
```task
|
||
id: RPF-WP-0044-T02
|
||
status: wait
|
||
priority: high
|
||
state_hub_task_id: "7d97ee9c-b2fb-520f-8468-3ecf7eea6c01"
|
||
```
|
||
|
||
**Waits on the founder's go-ahead.** Depends on T01.
|
||
|
||
```bash
|
||
ssh railiance01 'kubectl apply --dry-run=server -f -' < <(kubectl kustomize argocd/bootstrap)
|
||
ssh railiance01 'kubectl apply -f -' < <(kubectl kustomize argocd/bootstrap)
|
||
ssh railiance01 'kubectl -n argocd get appprojects,applications'
|
||
```
|
||
|
||
Expect three AppProjects and `railiance-apps-root` Synced with zero children
|
||
(the path is empty). A `ComparisonError` means the repo-server cannot reach
|
||
Forgejo: stop and fix egress before any app task. No repository Secret is
|
||
needed; `coulomb/railiance-platform` is public.
|
||
|
||
Rollback: `kubectl -n argocd delete application railiance-apps-root` (no
|
||
finalizer, no children), then delete the three AppProjects. Nothing else is
|
||
touched.
|
||
|
||
## Per-app procedure (T03–T06)
|
||
|
||
Every adoption task follows the same steps. `<app>` and `<sha>` vary.
|
||
|
||
1. **Diff.** Re-run the evidence render and `kubectl diff --server-side` at
|
||
the commit or chart version being pinned. Any spec change, any missing
|
||
whitelist kind, or a different image: stop.
|
||
2. **Merge** the child Application (no `automated`) into the railiance01
|
||
path, then sync the root by hand so it creates the child object only:
|
||
```bash
|
||
ssh railiance01 'kubectl -n argocd patch application railiance-apps-root --type merge -p "{\"operation\":{\"initiatedBy\":{\"username\":\"founder-approved\"},\"sync\":{\"revision\":\"<sha>\",\"prune\":false}}}"'
|
||
```
|
||
3. **ArgoCD's own diff:** read
|
||
`kubectl -n argocd get application <app> -o jsonpath='{.status.sync.status} {.status.health.status}'`
|
||
and `.status.resources`. Only metadata differences are acceptable.
|
||
4. **Manual sync, prune off, hooks skipped** (`apply` strategy):
|
||
```bash
|
||
ssh railiance01 'kubectl -n argocd patch application <app> --type merge -p "{\"operation\":{\"initiatedBy\":{\"username\":\"founder-approved\"},\"sync\":{\"revision\":\"<sha>\",\"prune\":false,\"syncStrategy\":{\"apply\":{}}}}}"'
|
||
```
|
||
Then verify Synced/Healthy and run the owner's live check.
|
||
5. **Prove it:** Synced and Healthy for at least 24 hours (7 days for
|
||
external-secrets). Only then, with a **second** go-ahead, a merge adds
|
||
`automated: {selfHeal: true}`; `prune: true` is a third step once the
|
||
app's tracked set is confirmed complete.
|
||
6. Record `docs/evidence/<date>-<app>-argocd-adoption.json`.
|
||
|
||
Rollback:
|
||
|
||
- **R1, before automated sync:** revert the merge, then
|
||
`kubectl -n argocd delete application <app>`. No finalizer, so nothing
|
||
cascades; the workload keeps running. Tracking annotations stay and are
|
||
harmless.
|
||
- **R2, after automated sync:** revert the automated-sync commit first and
|
||
sync the root by hand, then R1. Never hand-patch a child while the root
|
||
self-heals; that is `BREAK_GLASS`, recorded and reconciled into git.
|
||
- **Workload restore:** only once ArgoCD no longer manages the app, with the
|
||
previous deploy method named in the task.
|
||
|
||
## Adopt openbao-secretstore (lowest risk)
|
||
|
||
```task
|
||
id: RPF-WP-0044-T03
|
||
status: wait
|
||
priority: high
|
||
state_hub_task_id: "6d5fc801-e361-579b-bcd8-6fe719a82e94"
|
||
```
|
||
|
||
**Waits on the founder's go-ahead.** Depends on T02. One object,
|
||
`ClusterSecretStore/openbao`, zero diff, public source. It is the store
|
||
issue-core's `ExternalSecret` reads, so adopt it before issue-core. The 24
|
||
other ClusterSecretStores on railiance01 are not in the kustomization and stay
|
||
untracked (declared gap, RPF-WP-0043-T05). Live check:
|
||
`kubectl get clustersecretstore openbao` stays `Valid`, and
|
||
`externalsecret/issue-core-runtime` stays `SecretSynced`. Workload restore:
|
||
`kubectl apply -f argocd/platform-addons/openbao-secretstore/openbao.clustersecretstore.yaml`.
|
||
|
||
## Adopt issue-core (from rapp-issue-core)
|
||
|
||
```task
|
||
id: RPF-WP-0044-T04
|
||
status: wait
|
||
priority: high
|
||
state_hub_task_id: "0e619658-0453-51ed-b9a0-bed28b5544fd"
|
||
```
|
||
|
||
**Waits on the founder's go-ahead**, and on two preconditions:
|
||
|
||
- `rapp-issue-core` agrees (hub message) that ArgoCD owns production and its
|
||
`make deploy` stops being a production path; it would fight self-heal.
|
||
Image releases become commits to `manifests/30-runtime.yaml`.
|
||
- A CCR for the ArgoCD repository credential, source at
|
||
`platform/operators/argocd/repositories/rapp-issue-core`, read-only deploy
|
||
token. Applied with `make argocd-repo-apply`; no value is handled in this
|
||
plan.
|
||
|
||
Zero diff against live for both the raw `manifests/` and the rendered output
|
||
(digest `sha256:a56c80cc…b92cf`). All kinds are in the `railiance-tenants`
|
||
whitelist. Live check: `rapp-issue-core make verify-live`. Workload restore:
|
||
`rapp-issue-core make rollback`.
|
||
|
||
## Adopt target-revenue
|
||
|
||
```task
|
||
id: RPF-WP-0044-T05
|
||
status: wait
|
||
priority: medium
|
||
state_hub_task_id: "d418068a-6fb0-5416-aac5-d23c93924d9c"
|
||
```
|
||
|
||
**Waits on the founder's go-ahead.** Preconditions: T01 item 2 (CNPG
|
||
`Cluster` whitelisted) applied to the AppProject by hand, and `target-revenue`
|
||
confirms the two Sync-hook Jobs (`target-revenue-migrate`,
|
||
`target-revenue-bootstrap-binky`) are safe to re-run against production. They
|
||
are absent live and would run on every sync with the default hook strategy.
|
||
The adoption sync uses the `apply` strategy, which skips them. Once automated
|
||
sync is enabled they run on each sync, so the second go-ahead depends on that
|
||
confirmation. Ask `target-revenue` to pin an image digest instead of `0.1.4`.
|
||
Live check: `https://revenue.coulomb.social` health and the CNPG cluster
|
||
healthy. Workload restore: `kubectl apply -k k8s/railiance` from the previous
|
||
target-revenue commit.
|
||
|
||
## Adopt external-secrets (highest risk)
|
||
|
||
```task
|
||
id: RPF-WP-0044-T06
|
||
status: wait
|
||
priority: medium
|
||
state_hub_task_id: "4ca54868-c31f-5e3d-b7d3-a15b84c15f80"
|
||
```
|
||
|
||
**Waits on the founder's go-ahead.** Zero diff, but ESO serves every
|
||
`ExternalSecret` on the cluster (25 ClusterSecretStores). Before the merge,
|
||
add `crds.annotations: {argocd.argoproj.io/sync-options: "Prune=false,Delete=false"}`
|
||
to the values and re-run the diff; pruning a CRD would delete every
|
||
`ExternalSecret` and store. Adopt with `ServerSideApply=true` as declared.
|
||
Watch for drift on `caBundle` fields injected by the cert-controller; if
|
||
ArgoCD shows it, add `ignoreDifferences` before enabling self-heal. After
|
||
adoption, retire the stale `pending-upgrade` Helm release record (its
|
||
`sh.helm.release.v1.external-secrets.v*` Secrets) under a separate go-ahead;
|
||
ArgoCD does not track them. Live check: all ClusterSecretStores keep their
|
||
current `Valid` state (two, `openbao-activity-core` and
|
||
`openbao-email-connect`, are already `ValidationFailed`). Workload restore:
|
||
`helm template` 0.16.1 with the same values, `kubectl apply --server-side`.
|
||
|
||
## Turn the root back to the production lane
|
||
|
||
```task
|
||
id: RPF-WP-0044-T07
|
||
status: wait
|
||
priority: medium
|
||
state_hub_task_id: "844ff115-def2-55ca-ab01-413ba7e6857d"
|
||
```
|
||
|
||
**Waits on the founder's go-ahead.** After T03–T06 are proven, restore
|
||
`automated: {prune: true, selfHeal: true}` on `railiance-apps-root`, as the
|
||
change-gate decision's production row expects. From then on a merge to the
|
||
railiance01 path is a live production change, and `RPF-WP-0043-T04`
|
||
(policy-nexus) can use it.
|
||
|
||
## Plan phase C: retire coulombcore's ArgoCD
|
||
|
||
```task
|
||
id: RPF-WP-0044-T08
|
||
status: todo
|
||
priority: low
|
||
state_hub_task_id: "55d1382f-5862-5321-a1c9-96767764ba43"
|
||
```
|
||
|
||
Planning only. Needs a read-only check of coulombcore's ArgoCD, outside this
|
||
session's scope. Under Option A, retiring it also removes `argocd/applications/`.
|
||
Also hand back to the cluster layer: the phase A install is not declared in
|
||
any repository and its pods have no resource requests (BestEffort).
|