RPF-WP-0044-T06: ESO adoption prep, readiness receipts and founder runbook
All checks were successful
CCR intake gate / gate (push) Successful in 2m10s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Source gate passes; predecessor proofs stale/missing smoke; live diff and
store baseline blocked on agent cluster read access after CUST-WP-0073.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 527080@bnt-lap001
Assistant-Session: 5a0e864a-67be-4605-a77a-16635f7dd292
This commit is contained in:
codex 2026-10-21 00:25:59 +02:00
parent 8761da9027
commit fd2f808cd8
4 changed files with 164 additions and 1 deletions

View file

@ -0,0 +1,67 @@
{
"schema": "railiance-platform.eso-adoption-prep/v1",
"task": "RPF-WP-0044-T06",
"captured_at": "2026-10-21",
"approval_ref": "89f1058f-33bc-47a8-8a49-0b44d426c67e",
"readiness_gate": {
"receipt": "readiness-r1.json",
"source_ready": true,
"missing_or_invalid": [
"RPF-WP-0044-T04",
"RPF-WP-0044-T05"
],
"T04_reason": "docs/evidence/2026-10-20-issue-core-verify-live.json lacks authenticated_ingest_status=201 and authenticated_close_status=200 (verify-live ran without --smoke) and is older than the 300 s freshness window",
"T05_reason": "gate requires a fresh (<=300 s) argocd-workload-adoption-proof/v1 receipt; the 2026-10-11 receipt is stale. A fresh read-only run (scripts/workload_adoption_proof.py --live) failed because ssh railiance01 (user tegwick) can no longer read /etc/rancher/k3s/k3s.yaml after the CUST-WP-0073 cutover",
"smoke_not_run": "verify-live --smoke creates and closes a Forgejo issue; not read-only, so not run under the preparation-only boundary"
},
"offline_render": {
"chart": "external-secrets 0.16.1",
"values_source": "argocd/railiance01/drafts/external-secrets.application.yaml",
"render_sha256": "55236645afc9f2d9d376c28e73aed70bd31727d0e6f166664b3ebd568d5f136f",
"objects": 39,
"kinds": {
"ServiceAccount": 3,
"Secret": 1,
"CustomResourceDefinition": 20,
"ClusterRole": 5,
"ClusterRoleBinding": 2,
"Role": 1,
"RoleBinding": 1,
"Service": 1,
"Deployment": 3,
"ValidatingWebhookConfiguration": 2
},
"crds": 20,
"crds_prune_delete_protected": 20,
"store_or_externalsecret_objects_rendered": [],
"kinds_with_cabundle": [
"CustomResourceDefinition/clustergenerators.generators.external-secrets.io",
"CustomResourceDefinition/clustersecretstores.external-secrets.io",
"CustomResourceDefinition/secretstores.external-secrets.io",
"CustomResourceDefinition/vaultdynamicsecrets.generators.external-secrets.io",
"CustomResourceDefinition/webhooks.generators.external-secrets.io"
],
"credential_kind_objects_rendered": [
"external-secrets-webhook"
],
"notes": [
"20/20 CRDs carry argocd.argoproj.io/sync-options Prune=false,Delete=false",
"no ClusterSecretStore/SecretStore/ExternalSecret objects rendered, so adoption cannot delete or replace any store or ExternalSecret",
"the one rendered credential-kind object is the empty webhook certificate holder (metadata only, no data key); server-side apply does not take ownership of the cert-controller-injected data",
"ValidatingWebhookConfigurations declare no caBundle and no CRD declares a conversion webhook (caBundle hits are schema text); no ignoreDifferences added because no diff has shown drift yet"
]
},
"draft_policy": {
"automated": false,
"finalizers": false,
"ServerSideApply": true,
"Force_or_Replace": false,
"prune_in_sync_command": false
},
"not_done_live": {
"fresh_native_client_side_diff": "blocked: no agent kube read path on railiance01 (tegwick kubeconfig removed; custodian-agent observer role has no CRD/RBAC/webhook/ESO read)",
"store_baseline": "blocked: same reason; expected ValidationFailed: openbao-activity-core, openbao-email-connect"
},
"native_mutation": false,
"secret_values_observed_by_agent": false
}

View file

@ -0,0 +1 @@
{"approval_ref": "89f1058f-33bc-47a8-8a49-0b44d426c67e", "checks": {"source": {"chart_pin": "0.16.1", "crd_prune_delete_protected": true, "manual_sync_only": true, "server_side_apply": true}}, "founder_approval_required": false, "live_or_credential_calls": false, "missing_or_invalid": ["RPF-WP-0044-T04", "RPF-WP-0044-T05"], "native_mutation": false, "next_action": "wait_for_predecessor_automated_proof", "ready_for_fresh_native_render_diff": false, "schema": "eso-adoption-readiness/v1", "source_ready": true, "task": "RPF-WP-0044-T06"}

View file

@ -0,0 +1,93 @@
# RPF-WP-0044-T06 — adopt external-secrets into Argo (founder runbook)
Authority: D2 (`89f1058f-33bc-47a8-8a49-0b44d426c67e`). No further founder approval is needed.
Highest-risk adoption: ESO serves every ExternalSecret on railiance01. Prep receipt:
`docs/evidence/2026-10-21-eso-adoption-prep/prep-receipt.json`.
## State at preparation (2026-10-21, agent, offline only)
- Draft `argocd/railiance01/drafts/external-secrets.application.yaml`: chart `0.16.1`,
`ServerSideApply=true`, no `automated`, no finalizer, no Force/Replace, and CRD values
`crds.annotations: argocd.argoproj.io/sync-options: Prune=false,Delete=false`. The readiness gate
passes the source check.
- Offline render (render sha256 `55236645…f136f`): 39 objects, made up of 20 CRDs (all 20
prune/delete protected), 3 Deployments, 3 ServiceAccounts, 5 ClusterRoles, 2 ClusterRoleBindings,
1 Role, 1 RoleBinding, 1 Service, 2 ValidatingWebhookConfigurations and the empty webhook-cert
holder (metadata only). **No ClusterSecretStore or ExternalSecret is rendered**, so adoption cannot
delete or replace a store. The webhooks declare no `caBundle`, so server-side apply does not take
it from the cert-controller. `ignoreDifferences` is added only if the diff below shows drift.
- **Not done by the agent:** the fresh live diff, the store baseline and the fresh predecessor proofs.
Since the CUST-WP-0073 cutover (2026-10-20) the agent's `ssh railiance01` login (`tegwick`) cannot
read the k3s kubeconfig, and the custodian-agent observer role has no CRD, RBAC, webhook or ESO read.
Run every `ssh railiance01 "kubectl …"` below through a login that still has cluster-admin read.
## 1. Pre-check (founder, or agent once read access exists)
```bash
cd ~/railiance-platform && git pull --ff-only
D=docs/evidence/2026-10-21-eso-adoption-prep
# a) fresh predecessor proofs (gate window is 300 s, so run all three back to back)
( cd ~/rapp-issue-core && make -s verify-live VERIFY_ARGS="--expected-revision 186c817b67d3919a64acdddbd3889e539841f946 --expected-source-url ssh://git@forgejo.coulomb.social:30022/coulomb/rapp-issue-core.git --smoke" ) > $D/issue-core-proof.json
python3 scripts/workload_adoption_proof.py --live --source-root ~/target-revenue --observation $D/target-revenue-observation.json --receipt $D/target-revenue-proof.json
python3 scripts/eso_adoption_readiness.py --draft argocd/railiance01/drafts/external-secrets.application.yaml --issue-core-proof $D/issue-core-proof.json --target-revenue-proof $D/target-revenue-proof.json | tee $D/readiness-r2.json
# must print "ready_for_fresh_native_render_diff": true
# b) fresh render + client-side diff (T05 method); credential data in diff output is masked by kubectl
python3 -c "import yaml;print(yaml.safe_load(open('argocd/railiance01/drafts/external-secrets.application.yaml'))['spec']['source']['helm']['values'])" > /tmp/eso-values.yaml
helm template external-secrets external-secrets --repo https://charts.external-secrets.io --version 0.16.1 -n external-secrets -f /tmp/eso-values.yaml > /tmp/eso-render.yaml
ssh railiance01 "kubectl diff -f -" < /tmp/eso-render.yaml > $D/client-diff.txt; echo rc=$?
# c) store baseline
ssh railiance01 "kubectl get clustersecretstores -o custom-columns=NAME:.metadata.name,STATUS:.status.conditions[0].reason --no-headers" | tee $D/store-baseline-before.txt
```
Go only if all of these hold:
- the readiness gate is true;
- the diff touches CRDs in metadata only (the protective annotation) and changes no CRD `spec`;
- nothing is deleted or replaced;
- the baseline shows every store `Valid` except `openbao-activity-core` and `openbao-email-connect` (`ValidationFailed`).
If the diff shows webhook `caBundle` drift, an agent adds `ignoreDifferences` for
`ValidatingWebhookConfiguration` `/webhooks/0/clientConfig/caBundle` and `/webhooks/1/clientConfig/caBundle` and re-runs b.
## 2. Create the Application (an agent, after the gate is green)
The agent moves the draft to `argocd/railiance01/applications/external-secrets.application.yaml`,
commits and pushes, runs `make argocd-pin-drift`, then creates the child only with
`ssh railiance01 "kubectl apply --dry-run=server -f -" < <file>` followed by the same command without `--dry-run`.
The root also carries other OutOfSync children. Creating the Application syncs nothing.
## 3. The founder's sync command (one command)
```bash
ssh railiance01 "kubectl -n argocd patch application external-secrets --type merge -p '{\"operation\":{\"initiatedBy\":{\"username\":\"rpf-wp-0044-t06\"},\"sync\":{\"revision\":\"0.16.1\",\"prune\":false,\"syncOptions\":[\"ServerSideApply=true\"],\"syncStrategy\":{\"apply\":{\"force\":false}}}}}'"
```
(Local double quotes; the remote shell receives the JSON inside single quotes. Prune off, force off, SSA.)
## 4. Post-sync checks
```bash
ssh railiance01 "kubectl -n argocd get application external-secrets -o custom-columns=SYNC:.status.sync.status,HEALTH:.status.health.status,PHASE:.status.operationState.phase,REV:.status.sync.revision"
# expect Synced Healthy Succeeded 0.16.1
ssh railiance01 "kubectl -n external-secrets get deploy" # 3/3 available, no restarts beyond rollout
ssh railiance01 "kubectl get crd -o name | grep -c external-secrets.io" # still 20
ssh railiance01 "kubectl get clustersecretstores -o custom-columns=NAME:.metadata.name,STATUS:.status.conditions[0].reason --no-headers" > $D/store-baseline-after.txt
diff $D/store-baseline-before.txt $D/store-baseline-after.txt && echo baseline-unchanged
# ExternalSecret refresh sample (issue-core): force a refresh and confirm Ready
ssh railiance01 "kubectl -n issue-core annotate externalsecret issue-core-runtime force-sync=\$(date +%s) --overwrite && sleep 20 && kubectl -n issue-core get externalsecret issue-core-runtime -o custom-columns=READY:.status.conditions[0].status,REASON:.status.conditions[0].reason"
# expect True SecretSynced
```
The agent records these as `docs/evidence/<date>-eso-argocd-adoption.json`, re-runs issue-core
`verify-live`, and marks T06 done after the proving period.
## Rollback (abort owner: railiance-platform)
Never prune and never delete the Application with cascade. Revert the Application to the previously
reviewed chart `0.16.1` values in Git (`git revert <adoption commit>`), re-apply the child, then
run the same sync command as above. To stop Argo management without touching workloads, delete only
the Application object. It has no finalizer, so no cascade happens, and the CRDs are protected anyway.
## Separate later step (not part of adoption)
Retire the stale `pending-upgrade` Helm release record (`sh.helm.release.v1.external-secrets.v*`)
under its own reviewed cleanup. ArgoCD does not track it, and it must not be replaced silently.