Close RPF-WP-0025, WP-0043 T01/T05, WP-0045 T05; retire bao.coulomb.social defaults
- 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:
parent
8a7ebce5c6
commit
b666301487
11 changed files with 133 additions and 14 deletions
40
docs/direct-apply-gap-inventory.md
Normal file
40
docs/direct-apply-gap-inventory.md
Normal 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.
|
||||||
30
docs/evidence/2026-09-22-argocd-railiance01-status.json
Normal file
30
docs/evidence/2026-09-22-argocd-railiance01-status.json
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue