Close RPF-WP-0025, WP-0043 T01/T05, WP-0045 T05; retire bao.coulomb.social defaults
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 3s

- WP-0025-T03 done: public listener retracted 2026-09-15; bao.coulomb.social
  is retired, tunnel is the operator path; DNS withdrawal handed to S1.
- WP-0043-T01 done: ArgoCD Core reconciles railiance01 at main (evidence).
- WP-0043-T05 done: direct-apply gap inventory and founder proposal.
- WP-0045-T05 cancelled (no rollback needed); T06 preconditions recorded.
- Operator scripts default BAO_ADDR to the openbao-ui-railiance01 tunnel.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 150322@bnt-lap001
Assistant-Session: 16a7b788-374e-4915-a1df-fc87ffd9a5e4
This commit is contained in:
codex 2026-09-22 22:47:42 +02:00
parent 8a7ebce5c6
commit b666301487
11 changed files with 133 additions and 14 deletions

View file

@ -0,0 +1,40 @@
# Direct-apply gap inventory (RPF-WP-0043-T05)
Inventory date: 2026-09-22. Source: `Makefile` on `main` at `8a7ebce`.
Nothing here changes a target's behaviour.
## Declaration
Under the Kubernetes change gate (`the-custodian/docs/kubernetes-change-gate-decision.md`),
a platform object without a readiness state defaults to the production tier,
and the production row is `CONSTRUCT` through git and ArgoCD. The targets
below change railiance01 directly (`kubectl apply`, `helm upgrade`, or the
OpenBao API). Their evidence is `target-audited` only. **They do not conform
to the production row.** Until the founder rules on each group, every run is
`ADMINISTER @ realm:kubernetes/railiance01`, `activation=APPROVED`, and is
recorded as a production-tier change.
ArgoCD Core has run on railiance01 since 2026-09-21. The evidence is
`docs/evidence/2026-09-22-argocd-railiance01-status.json`. A reconciled lane
therefore exists for Kubernetes objects. It does not exist for OpenBao API
state.
## Inventory and proposal
| Group | Targets | What they change | Proposal |
| --- | --- | --- | --- |
| A. CNPG clusters, backups, NetworkPolicies | `db-deploy`, `forgejo-db-deploy`, `apps-pg-deploy`, `apps-pg-backup-deploy`, `apps-pg-overflow-deploy`, `net-kingdom-pg-inter-hub-networkpolicy-deploy` | `helm/*.yaml` via `kubectl apply` | **Move to a reconciled lane.** Adopt one Application per database cell, one at a time, as in RPF-WP-0044. The first sync is manual with prune off, and CNPG `Cluster` needs an AppProject whitelist entry. `db-deploy` (gitea-db) may be retirement, not adoption: check first whether gitea-db still exists. |
| B. ClusterSecretStores | the `openbao-secretstore` Application (already adopted) plus stores patched by hand during recoveries (RPF-WP-0037, RPF-WP-0045) | `ClusterSecretStore` specs | **Move to reconciled.** Store auth blocks belong in `argocd/`-tracked manifests. A hand patch now counts as drift to reconcile back into git. |
| C. OpenBao package | `openbao-deploy`, `openbao-overlay-apply`, `openbao-public-listener-retract`, `openbao-public-listener-rollback` | Helm release and gateway overlay in `openbao` | **Move to reconciled, with rapp-openbao.** The package belongs to `rapp-openbao`, so its Application should come from there. The listener retract and rollback stay direct as attended break-glass under `APPROVED`. |
| D. OpenBao configuration | `openbao-configure-initial`, `-ssh`, `-external-secrets-*`, `-workload-kv-lanes`, `-credential-change-appliers`, `-token-grants`, `credential-change-applier-apply` | policies, roles and mounts over the OpenBao API | **Stay direct under `APPROVED`.** ArgoCD cannot reconcile API state. The controls are compare-before-write scripts, attended login and per-run evidence. A declarative reconciler is a separate decision. |
| E. ArgoCD bootstrap | `argocd-bootstrap-deploy`, `argocd-repo-apply` | AppProjects, root Application, repository Secret | **`argocd-bootstrap-deploy`: never against railiance01.** It renders the old root with automated prune and self-heal. Use `argocd/railiance01/bootstrap/` (RPF-WP-0044). Retire or guard the target when coulombcore is retired (RPF-WP-0044-T08). `argocd-repo-apply` stays direct under `APPROVED` because it carries SOPS-encrypted credentials. |
| F. Not Kubernetes | `forgejo-package-prune`, `backup`, `forgejo-backup` | Forgejo API, backup uploads | Outside the Kubernetes gate. Stays direct. |
| Fail-closed | `pg-deploy`, `valkey-deploy` | nothing (refuse) | No action. |
Read-only targets (`*-status`, `*-logs`, `*-dry-run`, `*-verify*`,
`assurance-*`) change nothing and are not in scope.
## Founder decision requested
Accept or amend the per-group proposal. Adopting groups A to C is later work.
It is not started by this inventory.

View file

@ -0,0 +1,30 @@
{
"schema": "railiance-platform.argocd-railiance01-status.v1",
"task": "RPF-WP-0043-T01",
"observed_at": "2026-09-22T20:40:00Z",
"method": "read-only `ssh railiance01 'kubectl get ...'`; nothing applied, patched or synced",
"node": {
"name": "239.62.205.92.host.secureserver.net",
"internal_ip": "92.205.62.239",
"roles": "control-plane,etcd",
"kubelet": "v1.35.1+k3s1",
"ready": true
},
"argocd_namespace_workloads": {
"deployment/argocd-applicationset-controller": "1/1",
"deployment/argocd-redis": "1/1",
"deployment/argocd-repo-server": "1/1",
"statefulset/argocd-application-controller": "1/1",
"age": "30h (Argo CD Core v3.5.3, phase A install 2026-09-21)"
},
"applications": {
"railiance-apps-root": {"sync": "Synced", "health": "Healthy", "revision": "8a7ebce5c600026cb9eb818e27538e5e8990faa8"},
"openbao-secretstore": {"sync": "Synced", "health": "Healthy", "revision": "d2dbc19c254247652c49fda8721c80d53bca206a"},
"target-revenue": {"sync": "Synced", "health": "Healthy", "revision": "f1109d54eeda9f187daa215cf1c7163610d35d0a"}
},
"forgejo_main_revision": "8a7ebce5c600026cb9eb818e27538e5e8990faa8",
"root_revision_matches_main": true,
"supersedes": "the-custodian message cc3acf71 (2026-09-21, 'ArgoCD is not on railiance01'): that check predates the phase A install and ran before the workstation kubeconfig fix recorded in the-custodian/docs/agent-environment-orientation.md section 1",
"conclusion": "ArgoCD is installed on railiance01 and reconciles the root application at Forgejo main. The production row of the change gate has a working path; RPF-WP-0043 continues.",
"credential_values_emitted": false
}

View file

@ -52,7 +52,7 @@ def main():
helper = Path.home()/'.vault-token' helper = Path.home()/'.vault-token'
private(helper, 0o600) private(helper, 0o600)
token = helper.read_text().strip() token = helper.read_text().strip()
req = Request('https://bao.coulomb.social/v1/platform/data/workloads/secrets-engine/approval-client?version=1',headers={'X-Vault-Token':token}) req = Request(os.environ.get('BAO_ADDR','http://127.0.0.1:18200')+'/v1/platform/data/workloads/secrets-engine/approval-client?version=1',headers={'X-Vault-Token':token})
with transport(req,timeout=20) as response: with transport(req,timeout=20) as response:
data = response.read(65537) data = response.read(65537)
if len(data)>65536: if len(data)>65536:

View file

@ -103,7 +103,7 @@ class Operator:
if not token: if not token:
raise ProcedureError("OpenBao token file is empty") raise ProcedureError("OpenBao token file is empty")
self.remote = remote self.remote = remote
self.bao_env = dict(os.environ, BAO_ADDR="https://bao.coulomb.social", BAO_TOKEN=token) self.bao_env = dict(os.environ, BAO_ADDR=os.environ.get("BAO_ADDR", "http://127.0.0.1:18200"), BAO_TOKEN=token)
def bao(self, args: list[str], *, label: str, input_text: str | None = None, allow_missing: bool = False) -> subprocess.CompletedProcess[str]: def bao(self, args: list[str], *, label: str, input_text: str | None = None, allow_missing: bool = False) -> subprocess.CompletedProcess[str]:
return safe_run(["bao", *args], label=label, env=self.bao_env, input_text=input_text, allow_missing=allow_missing) return safe_run(["bao", *args], label=label, env=self.bao_env, input_text=input_text, allow_missing=allow_missing)

View file

@ -102,7 +102,7 @@ class Operator:
if not token: if not token:
raise ProcedureError("OpenBao token file is empty") raise ProcedureError("OpenBao token file is empty")
self.remote = remote self.remote = remote
self.bao_env = dict(os.environ, BAO_ADDR="https://bao.coulomb.social", BAO_TOKEN=token) self.bao_env = dict(os.environ, BAO_ADDR=os.environ.get("BAO_ADDR", "http://127.0.0.1:18200"), BAO_TOKEN=token)
def bao(self, args: list[str], *, label: str, input_text: str | None = None, allow_missing: bool = False) -> subprocess.CompletedProcess[str]: def bao(self, args: list[str], *, label: str, input_text: str | None = None, allow_missing: bool = False) -> subprocess.CompletedProcess[str]:
return safe_run(["bao", *args], label=label, env=self.bao_env, input_text=input_text, allow_missing=allow_missing) return safe_run(["bao", *args], label=label, env=self.bao_env, input_text=input_text, allow_missing=allow_missing)

View file

@ -123,7 +123,7 @@ class Operator:
self.remote = contract["authority"]["remote"] self.remote = contract["authority"]["remote"]
self.bao_env = dict( self.bao_env = dict(
os.environ, os.environ,
BAO_ADDR=os.environ.get("BAO_ADDR", "https://bao.coulomb.social"), BAO_ADDR=os.environ.get("BAO_ADDR", "http://127.0.0.1:18200"),
BAO_TOKEN=token, BAO_TOKEN=token,
) )

View file

@ -46,4 +46,4 @@ for mount in $MOUNTS; do
done done
printf '\nVerify unauthenticated UI mount listing:\n' printf '\nVerify unauthenticated UI mount listing:\n'
curl -fsS "https://bao.coulomb.social/v1/sys/internal/ui/mounts" | python3 -m json.tool curl -fsS "${BAO_ADDR:-http://127.0.0.1:18200}/v1/sys/internal/ui/mounts" | python3 -m json.tool

View file

@ -13,7 +13,7 @@ import urllib.request
from typing import Any from typing import Any
DEFAULT_ADDR = "https://bao.coulomb.social" DEFAULT_ADDR = "http://127.0.0.1:18200" # openbao-ui-railiance01 tunnel
DATA_PATH = "platform/data/workloads/issue-core/issue-core/issue-core-runtime" DATA_PATH = "platform/data/workloads/issue-core/issue-core/issue-core-runtime"
METADATA_PATH = ( METADATA_PATH = (
"platform/metadata/workloads/issue-core/issue-core/issue-core-runtime" "platform/metadata/workloads/issue-core/issue-core/issue-core-runtime"

View file

@ -4,12 +4,12 @@ type: workplan
title: "Retract public OpenBao listener behind operator-only access" title: "Retract public OpenBao listener behind operator-only access"
domain: financials domain: financials
repo: railiance-platform repo: railiance-platform
status: active status: finished
flavor: implementation flavor: implementation
owner: codex owner: codex
topic_slug: railiance topic_slug: railiance
created: "2026-08-23" created: "2026-08-23"
updated: "2026-09-15" updated: "2026-09-22"
related: related:
- RMASTER-WP-0020-T09 - RMASTER-WP-0020-T09
- RAPP-OPENBAO-WP-0002 - RAPP-OPENBAO-WP-0002
@ -55,7 +55,7 @@ It deletes only the Ingress and provides an exact rollback path.
```task ```task
id: RPF-WP-0025-T03 id: RPF-WP-0025-T03
status: progress status: done
priority: high priority: high
state_hub_task_id: "8850d742-7cd7-5a1b-ba52-4dbc4bdeba7e" state_hub_task_id: "8850d742-7cd7-5a1b-ba52-4dbc4bdeba7e"
``` ```
@ -152,3 +152,21 @@ HTTP 404. Evidence:
Remaining T03: public DNS withdrawal with railiance-infra and non-secret Remaining T03: public DNS withdrawal with railiance-infra and non-secret
acceptance to Railiance Master. Rollback phrase remains available during the acceptance to Railiance Master. Rollback phrase remains available during the
observation window. observation window.
## Closure — 2026-09-22
T03 done. The operator-only cutover is complete on the platform side:
loopback MFA login passed, and the public Ingress was retracted on 2026-09-15
(`docs/evidence/2026-09-15-openbao-public-listener-retract.json`). **Intended
end state: `bao.coulomb.social` is retired.** The named tunnel
`openbao-ui-railiance01` (`http://127.0.0.1:18200`) is the only operator path,
and workloads use `openbao.openbao.svc:8200`. No certificate or route will be
restored for the public name. The dangling DNS record (it still resolves to
92.205.62.239, where Traefik serves its default certificate and returns 404)
was handed to its S1 publisher, railiance-infra, for withdrawal. Non-secret
acceptance went to railiance-master for RMASTER-WP-0020-T09, and the end
state was sent to user-engine (hub message `e8ccbc2f`). Operator scripts no
longer default `BAO_ADDR` to the public name. The one exception is
`scripts/audit-core-database-lease-recovery.py`: the WP-0024 owner-review
contract pins its digest, so it keeps the old literal until the next owner
review.

View file

@ -9,7 +9,7 @@ flavor: planning
owner: railiance-platform owner: railiance-platform
topic_slug: railiance topic_slug: railiance
created: "2026-09-21" created: "2026-09-21"
updated: "2026-09-21" updated: "2026-09-22"
due: "2026-12-21" due: "2026-12-21"
related: [RPF-WP-0022] related: [RPF-WP-0022]
state_hub_workstream_id: "ec41a4bd-df18-5b07-9b63-ccb80d9f001c" state_hub_workstream_id: "ec41a4bd-df18-5b07-9b63-ccb80d9f001c"
@ -43,7 +43,7 @@ stays untouched and unwrapped while this lane moves.
```task ```task
id: RPF-WP-0043-T01 id: RPF-WP-0043-T01
status: todo status: done
priority: high priority: high
state_hub_task_id: "d6a7a480-6251-504a-aa5f-ab9c04fe1e79" state_hub_task_id: "d6a7a480-6251-504a-aa5f-ab9c04fe1e79"
``` ```
@ -205,7 +205,7 @@ Rollback:
```task ```task
id: RPF-WP-0043-T05 id: RPF-WP-0043-T05
status: todo status: done
priority: medium priority: medium
state_hub_task_id: "91431560-79d4-5352-a6b5-f12850315dd2" state_hub_task_id: "91431560-79d4-5352-a6b5-f12850315dd2"
``` ```
@ -218,3 +218,16 @@ 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 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 stay direct under `APPROVED`. Do not change a target's behaviour as part of
the inventory. the inventory.
## Execution record — 2026-09-22
- **T01 done.** Read-only check over `ssh railiance01`. Node 92.205.62.239 is
Ready. Argo CD Core runs 1/1 in `argocd`. `railiance-apps-root` is
Synced/Healthy at `8a7ebce`, which equals Forgejo `main`. `openbao-secretstore`
and `target-revenue` are Synced/Healthy. Evidence:
`docs/evidence/2026-09-22-argocd-railiance01-status.json`. This supersedes
the custodian's 2026-09-21 "not installed" reading, which predates the phase A
install and the kubeconfig fix. The plan continues.
- **T05 done.** Gap declared and inventoried in
`docs/direct-apply-gap-inventory.md`, with a per-group proposal for the
founder. No target changed.

View file

@ -9,7 +9,7 @@ flavor: implementation
owner: railiance-platform owner: railiance-platform
topic_slug: railiance topic_slug: railiance
created: "2026-09-21" created: "2026-09-21"
updated: "2026-09-21" updated: "2026-09-22"
related: [RPF-WP-0037] related: [RPF-WP-0037]
state_hub_workstream_id: "0edc6426-9cc8-5bbe-88ea-c27d9862d96e" state_hub_workstream_id: "0edc6426-9cc8-5bbe-88ea-c27d9862d96e"
--- ---
@ -200,7 +200,7 @@ other than the header. The earlier 37-count had 5 failing; expect 0.
```task ```task
id: RPF-WP-0045-T05 id: RPF-WP-0045-T05
status: wait status: cancel
priority: medium priority: medium
state_hub_task_id: "6bccc229-3428-588a-9488-3fd5d5a07e92" state_hub_task_id: "6bccc229-3428-588a-9488-3fd5d5a07e92"
``` ```
@ -277,3 +277,21 @@ and `email-connect/deploy/k8s/railiance/openbao-eso-token-apply.sh`, and update
- **T03 done.** SAs created; both stores switched to Kubernetes auth. - **T03 done.** SAs created; both stores switched to Kubernetes auth.
- **T04 done.** Both stores `Ready=True` ("store validated"); forced refresh; all five ExternalSecrets `SecretSynced` at 17:39Z; **37/37 ExternalSecrets ready cluster-wide.** - **T04 done.** Both stores `Ready=True` ("store validated"); forced refresh; all five ExternalSecrets `SecretSynced` at 17:39Z; **37/37 ExternalSecrets ready cluster-wide.**
- **T06** (delete the two dead token Secrets) is due after 24 h of clean syncs, i.e. not before 2026-09-22T17:40Z. - **T06** (delete the two dead token Secrets) is due after 24 h of clean syncs, i.e. not before 2026-09-22T17:40Z.
## Execution record — 2026-09-22
- **T05 cancelled.** Rollback is not needed. At 20:40Z both stores are
`Valid`/Ready on Kubernetes auth, and all five ExternalSecrets synced at
20:39Z, more than 24 h after the T04 switch.
- **T06 preconditions met, deletion not run.** The no-reference check returned
no output: no store references either token Secret. The deletion was blocked
by the session's permission guard, so it waits for the founder to run it:
`ssh railiance01 'kubectl -n external-secrets delete secret openbao-activity-core-eso-token openbao-email-connect-eso-token'`.
The bootstrap-target retirements in activity-core and email-connect were
handed to their owners.
- **New incident, outside this plan:** `openbao-core-hub-database` and
`openbao-tenant-engine-database` (both static-token) have failed
`lookup-self` with 403 since about 2026-09-21T22:00Z. Six ExternalSecrets are
failing (four in core-hub, two in tenant-engine). This is the failure mode
named in the Risks section above. It needs the same Kubernetes-auth
migration, with an attended admin login.