diff --git a/docs/direct-apply-gap-inventory.md b/docs/direct-apply-gap-inventory.md new file mode 100644 index 0000000..aa3b0f8 --- /dev/null +++ b/docs/direct-apply-gap-inventory.md @@ -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. diff --git a/docs/evidence/2026-09-22-argocd-railiance01-status.json b/docs/evidence/2026-09-22-argocd-railiance01-status.json new file mode 100644 index 0000000..4867046 --- /dev/null +++ b/docs/evidence/2026-09-22-argocd-railiance01-status.json @@ -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 +} diff --git a/scripts/approval-client-delivery-check.py b/scripts/approval-client-delivery-check.py index fe53d3a..18659b7 100644 --- a/scripts/approval-client-delivery-check.py +++ b/scripts/approval-client-delivery-check.py @@ -52,7 +52,7 @@ def main(): helper = Path.home()/'.vault-token' private(helper, 0o600) 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: data = response.read(65537) if len(data)>65536: diff --git a/scripts/audit-core-whitehat-e2-credentials-02.py b/scripts/audit-core-whitehat-e2-credentials-02.py index ccacb05..f169499 100755 --- a/scripts/audit-core-whitehat-e2-credentials-02.py +++ b/scripts/audit-core-whitehat-e2-credentials-02.py @@ -103,7 +103,7 @@ class Operator: if not token: raise ProcedureError("OpenBao token file is empty") 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]: return safe_run(["bao", *args], label=label, env=self.bao_env, input_text=input_text, allow_missing=allow_missing) diff --git a/scripts/audit-core-whitehat-e2-credentials.py b/scripts/audit-core-whitehat-e2-credentials.py index 6b63fbc..8984f43 100755 --- a/scripts/audit-core-whitehat-e2-credentials.py +++ b/scripts/audit-core-whitehat-e2-credentials.py @@ -102,7 +102,7 @@ class Operator: if not token: raise ProcedureError("OpenBao token file is empty") 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]: return safe_run(["bao", *args], label=label, env=self.bao_env, input_text=input_text, allow_missing=allow_missing) diff --git a/scripts/custody-projection.py b/scripts/custody-projection.py index a6f120a..c888a21 100755 --- a/scripts/custody-projection.py +++ b/scripts/custody-projection.py @@ -123,7 +123,7 @@ class Operator: self.remote = contract["authority"]["remote"] self.bao_env = dict( 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, ) diff --git a/scripts/openbao-tune-auth-listing.sh b/scripts/openbao-tune-auth-listing.sh index 8093cbe..c2de95f 100755 --- a/scripts/openbao-tune-auth-listing.sh +++ b/scripts/openbao-tune-auth-listing.sh @@ -46,4 +46,4 @@ for mount in $MOUNTS; do done printf '\nVerify unauthenticated UI mount listing:\n' -curl -fsS "https://bao.coulomb.social/v1/sys/internal/ui/mounts" | python3 -m json.tool \ No newline at end of file +curl -fsS "${BAO_ADDR:-http://127.0.0.1:18200}/v1/sys/internal/ui/mounts" | python3 -m json.tool \ No newline at end of file diff --git a/scripts/verify_coding_agent_approle.py b/scripts/verify_coding_agent_approle.py index c19fba4..3a917e1 100644 --- a/scripts/verify_coding_agent_approle.py +++ b/scripts/verify_coding_agent_approle.py @@ -13,7 +13,7 @@ import urllib.request 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" METADATA_PATH = ( "platform/metadata/workloads/issue-core/issue-core/issue-core-runtime" diff --git a/workplans/RPF-WP-0025-openbao-operator-only-access.md b/workplans/RPF-WP-0025-openbao-operator-only-access.md index cb25b84..58a33fd 100644 --- a/workplans/RPF-WP-0025-openbao-operator-only-access.md +++ b/workplans/RPF-WP-0025-openbao-operator-only-access.md @@ -4,12 +4,12 @@ type: workplan title: "Retract public OpenBao listener behind operator-only access" domain: financials repo: railiance-platform -status: active +status: finished flavor: implementation owner: codex topic_slug: railiance created: "2026-08-23" -updated: "2026-09-15" +updated: "2026-09-22" related: - RMASTER-WP-0020-T09 - RAPP-OPENBAO-WP-0002 @@ -55,7 +55,7 @@ It deletes only the Ingress and provides an exact rollback path. ```task id: RPF-WP-0025-T03 -status: progress +status: done priority: high 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 acceptance to Railiance Master. Rollback phrase remains available during the 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. diff --git a/workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md b/workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md index 7573cdf..9783577 100644 --- a/workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md +++ b/workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md @@ -9,7 +9,7 @@ flavor: planning owner: railiance-platform topic_slug: railiance created: "2026-09-21" -updated: "2026-09-21" +updated: "2026-09-22" due: "2026-12-21" related: [RPF-WP-0022] state_hub_workstream_id: "ec41a4bd-df18-5b07-9b63-ccb80d9f001c" @@ -43,7 +43,7 @@ stays untouched and unwrapped while this lane moves. ```task id: RPF-WP-0043-T01 -status: todo +status: done priority: high state_hub_task_id: "d6a7a480-6251-504a-aa5f-ab9c04fe1e79" ``` @@ -205,7 +205,7 @@ Rollback: ```task id: RPF-WP-0043-T05 -status: todo +status: done priority: medium 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 stay direct under `APPROVED`. Do not change a target's behaviour as part of 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. diff --git a/workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md b/workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md index 1586a89..4680d36 100644 --- a/workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md +++ b/workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md @@ -9,7 +9,7 @@ flavor: implementation owner: railiance-platform topic_slug: railiance created: "2026-09-21" -updated: "2026-09-21" +updated: "2026-09-22" related: [RPF-WP-0037] 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 id: RPF-WP-0045-T05 -status: wait +status: cancel priority: medium 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. - **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. + +## 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.