From 07ed6217b74a057960f11da2f4c266de2fa1182a Mon Sep 17 00:00:00 2001 From: codex Date: Thu, 20 Aug 2026 23:31:47 +0200 Subject: [PATCH] Advance RPF-WP-0021 platform onboarding --- WORK-RECORDS.md | 2 +- .../core-hub.externalsecrets.yaml | 60 +++++++++++++++++ ...-core-hub-database.clustersecretstore.yaml | 25 +++++++ ...-postgres-capacity-admission-2026-08-20.md | 67 +++++++++++++++++++ .../core-hub-shadow-preflight-2026-08-20.md | 42 ++++++++++++ .../external-secrets-core-hub-database.hcl | 11 +++ tests/test_credential_change.py | 42 ++++++++++++ ...PF-WP-0021-core-hub-platform-onboarding.md | 40 +++++++++-- 8 files changed, 284 insertions(+), 5 deletions(-) create mode 100644 argocd/platform-addons/openbao-secretstore/core-hub.externalsecrets.yaml create mode 100644 argocd/platform-addons/openbao-secretstore/openbao-core-hub-database.clustersecretstore.yaml create mode 100644 docs/evidence/core-hub-postgres-capacity-admission-2026-08-20.md create mode 100644 docs/evidence/core-hub-shadow-preflight-2026-08-20.md create mode 100644 openbao/policies/external-secrets-core-hub-database.hcl diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index b7c225c..b0b3dff 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -101,7 +101,7 @@ | task | RPF-WP-0020-T03 | todo | — | workplans/RPF-WP-0020-ccr-schema-drift.md | | task | RPF-WP-0020-T04 | todo | — | workplans/RPF-WP-0020-ccr-schema-drift.md | | task | RPF-WP-0021-T01 | done | — | workplans/RPF-WP-0021-core-hub-platform-onboarding.md | -| task | RPF-WP-0021-T02 | progress | — | workplans/RPF-WP-0021-core-hub-platform-onboarding.md | +| task | RPF-WP-0021-T02 | done | — | workplans/RPF-WP-0021-core-hub-platform-onboarding.md | | task | RPF-WP-0021-T03 | progress | — | workplans/RPF-WP-0021-core-hub-platform-onboarding.md | | task | RPF-WP-0021-T04 | progress | — | workplans/RPF-WP-0021-core-hub-platform-onboarding.md | | task | RPF-WP-0021-T05 | wait | — | workplans/RPF-WP-0021-core-hub-platform-onboarding.md | diff --git a/argocd/platform-addons/openbao-secretstore/core-hub.externalsecrets.yaml b/argocd/platform-addons/openbao-secretstore/core-hub.externalsecrets.yaml new file mode 100644 index 0000000..4dfb562 --- /dev/null +++ b/argocd/platform-addons/openbao-secretstore/core-hub.externalsecrets.yaml @@ -0,0 +1,60 @@ +# RPF-WP-0021: three disjoint materialized Secrets for the Core Hub package. +# Apply only after the static CCR is approved/seeded and the database roles +# plus exact-scope renewable ESO token exist. +--- +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: core-hub-api-token + namespace: core-hub +spec: + refreshInterval: 5m + secretStoreRef: + kind: ClusterSecretStore + name: openbao-core-hub-runtime + target: + name: core-hub-api-token + creationPolicy: Owner + deletionPolicy: Retain + data: + - secretKey: CORE_HUB_API_TOKEN + remoteRef: + key: workloads/core-hub/runtime + property: CORE_HUB_API_TOKEN +--- +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: core-hub-runtime-database + namespace: core-hub +spec: + refreshInterval: 5m + secretStoreRef: + kind: ClusterSecretStore + name: openbao-core-hub-database + target: + name: core-hub-runtime-database + creationPolicy: Owner + deletionPolicy: Retain + # A single extract mints one lease and keeps its username/password paired. + dataFrom: + - extract: + key: creds/core-hub-runtime +--- +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: core-hub-migration-database + namespace: core-hub +spec: + refreshInterval: 5m + secretStoreRef: + kind: ClusterSecretStore + name: openbao-core-hub-database + target: + name: core-hub-migration-database + creationPolicy: Owner + deletionPolicy: Retain + dataFrom: + - extract: + key: creds/core-hub-migration diff --git a/argocd/platform-addons/openbao-secretstore/openbao-core-hub-database.clustersecretstore.yaml b/argocd/platform-addons/openbao-secretstore/openbao-core-hub-database.clustersecretstore.yaml new file mode 100644 index 0000000..dbaddac --- /dev/null +++ b/argocd/platform-addons/openbao-secretstore/openbao-core-hub-database.clustersecretstore.yaml @@ -0,0 +1,25 @@ +# Railiance01-local database-engine store for RPF-WP-0021. The renewable +# orphan parent token is streamed into the referenced Secret by an attended +# platform operation; its value never enters this repository. +apiVersion: external-secrets.io/v1 +kind: ClusterSecretStore +metadata: + name: openbao-core-hub-database + labels: + app.kubernetes.io/part-of: railiance-gitops + railiance-platform/component: external-secrets + app.kubernetes.io/name: core-hub +spec: + provider: + vault: + server: http://openbao.openbao.svc:8200 + path: database + version: v1 + auth: + tokenSecretRef: + name: openbao-core-hub-eso-token + namespace: external-secrets + key: token + conditions: + - namespaces: + - core-hub diff --git a/docs/evidence/core-hub-postgres-capacity-admission-2026-08-20.md b/docs/evidence/core-hub-postgres-capacity-admission-2026-08-20.md new file mode 100644 index 0000000..32f1330 --- /dev/null +++ b/docs/evidence/core-hub-postgres-capacity-admission-2026-08-20.md @@ -0,0 +1,67 @@ +# Core Hub PostgreSQL capacity admission — 2026-08-20 + +Workplan: `RPF-WP-0021-T02` + +Target: railiance01 `databases/platform-pg` + +Mode: bounded live occupancy probe; no application data or credential value +was read or recorded. + +## Question + +Can Core Hub occupy the fourth and final declaration slot on `platform-pg` +without raising the current 1Gi pod limit or exceeding the explicit +100-connection aggregate? + +The four declarations are `audit-core`, `tenant-engine`, `core-hub`, and the +counted isolation probe. Each workload declaration reserves 12 runtime plus +two migration connections; the probe's smaller allowance is deliberately +overstated here, making 56 a conservative declared-pool occupancy. + +## Method + +A temporary namespace labeled as a `platform-pg` client received the existing +bootstrap Secret by namespace-to-namespace projection without printing its +values. A separate client pod opened 56 sessions in four named groups of 14. +This kept client-process memory outside the database pod. The run then repeated +the same 56-session occupancy with 13 idle and one bounded active sort query +per group. The namespace and copied Secret were deleted after the run. + +The active query materialized and sorted 100,000 generated integers, then held +the backend briefly for observation. It did not access an application table. + +## Observations + +| Signal | Before | Declared occupancy / observed peak | +| --- | ---: | ---: | +| Probe sessions | 0 | 56 (14 in each named group) | +| All PostgreSQL sessions | 9 | 65 | +| Active / idle sessions | 1 / 2 | 5 / 54 in the mixed run | +| PostgreSQL pod memory | 89Mi | **174Mi maximum observed** | +| PostgreSQL pod CPU | 7m | 82m during connection startup | +| Node memory | 6,210Mi / 16Gi | 6,342Mi / 16Gi (39%) | +| Node CPU | 470m | 2,002m transient startup peak | + +The 85Mi database-pod increase across 56 opened backends is approximately +1.52Mi per connection at declared pool occupancy. That replaces ADR-0004's +unmeasured 10Mi literature estimate for idle/backend occupancy on this live +instance; it does not claim every query operator is limited to that figure. + +The earlier quiet preflight observed 362Mi. Conservatively adding the entire +measured connection delta to that higher historical sample gives 447Mi, under +44% of the 1Gi limit. The four bounded active queries did not exceed the +174Mi maximum already observed during connection startup. + +## Decision + +Core Hub is accepted as the fourth declaration on `platform-pg` without a +memory-limit increase. It reaches the declared ceiling and does not authorize +a fifth declaration. The renderer must continue to reject any additional +consumer on this cell; the named overflow remains `platform-pg-2`. + +This is capacity admission evidence, not a latency or workload-peak guarantee. +Core Hub is latency-critical beside batch `audit-core`, and community +PostgreSQL provides no service-class priority. The private shadow run must +therefore capture application health, query latency, pod memory, and backend +counts; material regression is a placement trigger, not a reason to reinterpret +this probe as stronger evidence. diff --git a/docs/evidence/core-hub-shadow-preflight-2026-08-20.md b/docs/evidence/core-hub-shadow-preflight-2026-08-20.md new file mode 100644 index 0000000..8a98990 --- /dev/null +++ b/docs/evidence/core-hub-shadow-preflight-2026-08-20.md @@ -0,0 +1,42 @@ +# Core Hub private-shadow preflight — 2026-08-20 + +This records non-secret source/target metadata gathered for +`RPF-WP-0021-T05`. It is preparation only: no source data was dumped or +restored because the governed credential projections under T04 are not yet +live. + +## Target admission + +At `2026-08-20T21:31:13Z` on railiance01: + +- namespace `core-hub` was Active and carried + `railiance.io/postgres-client=platform-pg` plus the package identity labels; +- CNPG `Database/core-hub` reported `status.applied=true` against + `platform-pg`, with reclaim policy `retain` and owner `core_hub_owner`; +- the `core_hub` database existed but intentionally contained no application + tables before the rehearsal restore; and +- the KV store, database store and three ExternalSecret manifests all passed + Kubernetes server-side dry-run after namespace creation. + +## Source snapshot inventory + +A read-only query ran through the existing `coulombcore` SSH route against +`apps-pg/core_hub`. No Secret or connection value was read. The live source +writer remained enabled, so these counts are a rehearsal baseline and must be +captured again with the eventual consistent dump. + +| Object | Rows | +| --- | ---: | +| `public.alembic_version` | 1 | +| `public.api_consumers` | 3 | +| `public.api_keys` | 10 | +| `public.hub_capability_manifests` | 2 | +| `public.hubs` | 2 | +| `public.interaction_events` | 3 | +| `public.migration_runs` | 2 | +| `public.widgets` | 16 | + +The source Alembic revision was `20260627_0002`. No user-defined sequence was +reported. The table set and counts are small enough for a bounded logical-copy +rehearsal, but the restore remains gated on T04 so that migration and runtime +checks use the intended distinct dynamic leases rather than a bootstrap login. diff --git a/openbao/policies/external-secrets-core-hub-database.hcl b/openbao/policies/external-secrets-core-hub-database.hcl new file mode 100644 index 0000000..981962d --- /dev/null +++ b/openbao/policies/external-secrets-core-hub-database.hcl @@ -0,0 +1,11 @@ +# Exact-scope parent policy for the renewable ESO token that projects Core Hub +# PostgreSQL leases. Static API-token custody uses the separate +# workload-kv-read-core-hub-runtime policy and Kubernetes-auth store. + +path "database/creds/core-hub-runtime" { + capabilities = ["read"] +} + +path "database/creds/core-hub-migration" { + capabilities = ["read"] +} diff --git a/tests/test_credential_change.py b/tests/test_credential_change.py index eef1c62..7d80006 100644 --- a/tests/test_credential_change.py +++ b/tests/test_credential_change.py @@ -104,6 +104,48 @@ class CredentialChangeTests(unittest.TestCase): self.assertEqual(store["spec"]["provider"]["vault"]["path"], "platform") self.assertEqual(store["spec"]["conditions"][0]["namespaces"], ["core-hub"]) + database_policy = ( + REPO_DIR / "openbao/policies/external-secrets-core-hub-database.hcl" + ).read_text() + self.assertIn('path "database/creds/core-hub-runtime"', database_policy) + self.assertIn('path "database/creds/core-hub-migration"', database_policy) + self.assertNotIn("platform/data/", database_policy) + + database_store = credential_change.load_yaml( + REPO_DIR + / "argocd/platform-addons/openbao-secretstore/openbao-core-hub-database.clustersecretstore.yaml" + ) + self.assertEqual(database_store["spec"]["provider"]["vault"]["path"], "database") + self.assertEqual(database_store["spec"]["provider"]["vault"]["version"], "v1") + self.assertEqual(database_store["spec"]["conditions"][0]["namespaces"], ["core-hub"]) + + with ( + REPO_DIR + / "argocd/platform-addons/openbao-secretstore/core-hub.externalsecrets.yaml" + ).open() as stream: + external_secrets = list(credential_change.yaml.safe_load_all(stream)) + self.assertEqual( + {item["metadata"]["name"] for item in external_secrets}, + { + "core-hub-api-token", + "core-hub-runtime-database", + "core-hub-migration-database", + }, + ) + for item in external_secrets: + self.assertEqual(item["metadata"]["namespace"], "core-hub") + self.assertEqual(item["spec"]["refreshInterval"], "5m") + runtime = next( + item for item in external_secrets + if item["metadata"]["name"] == "core-hub-runtime-database" + ) + migration = next( + item for item in external_secrets + if item["metadata"]["name"] == "core-hub-migration-database" + ) + self.assertEqual(runtime["spec"]["dataFrom"][0]["extract"]["key"], "creds/core-hub-runtime") + self.assertEqual(migration["spec"]["dataFrom"][0]["extract"]["key"], "creds/core-hub-migration") + def test_target_rapp_rejects_non_slug(self) -> None: path = self.unapproved_ccr() data = credential_change.load_yaml(path) diff --git a/workplans/RPF-WP-0021-core-hub-platform-onboarding.md b/workplans/RPF-WP-0021-core-hub-platform-onboarding.md index 67234cf..19f1390 100644 --- a/workplans/RPF-WP-0021-core-hub-platform-onboarding.md +++ b/workplans/RPF-WP-0021-core-hub-platform-onboarding.md @@ -108,7 +108,7 @@ State Hub; workplan UUID assignment remains with the production registrar. ```task id: RPF-WP-0021-T02 -status: progress +status: done priority: high ``` @@ -125,8 +125,7 @@ baseline. The untracked `consumers/issue-core.yaml` is not a real consumer — `ISSUE-WP-0007` explicitly removed Postgres from that package — and must not silently consume a slot. -Live apply, database role creation, namespace labeling, and OpenBao database -role configuration remain attended operations after declaration review. +OpenBao database role configuration remains an attended operation under T04. Read-only preflight 2026-08-20: `platform-pg-1` used 9m CPU and 362Mi memory against a 1Gi limit, with only `audit_core` and operator sessions present; @@ -134,6 +133,23 @@ against a 1Gi limit, with only `audit_core` and operator sessions present; does not satisfy ADR-0004's representative multi-consumer peak measurement. Evidence: `docs/evidence/core-hub-postgres-capacity-preflight-2026-08-20.md`. +Capacity admission completed 2026-08-20. A bounded live probe opened 56 +sessions in four declaration groups and repeated the occupancy with one active +bounded sort per group. `platform-pg` peaked at 174Mi/1Gi; the 85Mi increase is +about 1.52Mi per opened backend. Even adding that delta to the earlier 362Mi +sample yields 447Mi, under 44% of the limit. Core Hub is accepted as the fourth +and final declaration without raising the pod limit. Evidence: +`docs/evidence/core-hub-postgres-capacity-admission-2026-08-20.md`. + +Completed 2026-08-20. `rapp-postgres` commit `4f316cc` accepts and publishes +the Core Hub declaration as the fourth `platform-pg` consumer. All 31 package +tests and consumer rendering passed; the railiance01 server-side dry-run +accepted the generated resource. The idempotent package provisioner then +created `core_hub`, its `core_hub` schema, distinct NOLOGIN owner/migration/ +runtime roles and bounded role settings. CNPG `Database/core-hub` reports +`status.applied=true`, targets `platform-pg`, retains the database on resource +deletion, and names `core_hub_owner` as owner. + ## T03 — Correct the runtime credential delivery contract ```task @@ -207,6 +223,16 @@ high-risk boundary to deny direct data reads on the new path. Database-engine policy/store manifests wait for the accepted Postgres declaration and the T03 consumer correction. +Progress 2026-08-20 after T02 admission: added an exact two-path database ESO +policy, a namespace-limited `openbao-core-hub-database` store, and three +disjoint ExternalSecrets. Runtime and migration each use one `dataFrom.extract` +so a username/password pair comes from one lease; both refresh every five +minutes. The database store and all projections passed railiance01 server-side +dry-run. The package namespace is live with the reviewed `platform-pg` client +label. Live role/store activation correctly stopped because the current +OpenBao session receives 403 and CCR-2026-0013 is still proposed; no authority +or placeholder Secret was improvised. + ## T05 — Support the private shadow restore and verification ```task @@ -224,11 +250,17 @@ the private endpoint. This task ends with repeatable shadow evidence. It does not flip `hub.coulomb.social`, freeze production writes, or remove CoulombCore. +Preflight 2026-08-20: the source has Alembic revision `20260627_0002`, eight +public tables and 39 total rows. The admitted target intentionally has no +application tables before restore. No dump or restore was attempted ahead of +the T04 credential gate. Evidence: +`docs/evidence/core-hub-shadow-preflight-2026-08-20.md`. + ## Acceptance - [x] Private Forgejo repo exists, validated commit `4403c9b` is preserved, and the registered workplan prefix is consistent. -- [ ] Core Hub is an accepted, capacity-reviewed `rapp-postgres` consumer. +- [x] Core Hub is an accepted, capacity-reviewed `rapp-postgres` consumer. - [ ] Static API-token and dynamic database credentials have distinct, least-privilege delivery paths. - [ ] Database credential rotation cannot leave a running pod using an expired