TEN-WP-0009-T01: target platform-pg via rapp-postgres consumer declaration
Corrects the credential assumption: shared-cluster access is a broker lease, not a CNPG-minted secretKeyRef. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
358dc506fa
commit
05d12fe39f
1 changed files with 90 additions and 22 deletions
|
|
@ -48,15 +48,33 @@ than a formality wrapped around one implementation.
|
|||
|
||||
## Credential handling
|
||||
|
||||
CloudNativePG mints the connection secret itself: a cluster named `X` produces
|
||||
secret `X-app` with key `uri`. The Deployment references it via
|
||||
`secretKeyRef`, exactly as `user-engine` does with
|
||||
`USER_ENGINE_DATABASE_URL` ← `user-engine-pg-app/uri`.
|
||||
**Corrected 2026-08-17.** This section originally said CloudNativePG mints an
|
||||
`X-app` secret we reference with `secretKeyRef`, as `user-engine` does. That is
|
||||
true for a cluster's *own* app database — and wrong for the path we are
|
||||
actually taking.
|
||||
|
||||
So **no secret enters this repo, this workplan, or any evidence dump**, and no
|
||||
`warden route` lookup is needed — this is not a credential we fetch, custody,
|
||||
or rotate. Runtime secret custody remains OpenBao's per `SCOPE.md`; an
|
||||
operator-minted in-cluster secret is outside that lane.
|
||||
`railiance-platform/docs/rapp-postgres-boundary.md` is explicit: `rapp-postgres`
|
||||
owns the role and database provisioning surface for the shared cluster, and
|
||||
*"the workload receives a short-lived lease through the platform broker"* — the
|
||||
OpenBao database secrets engine — rather than a static secret. `railiance-platform`
|
||||
retains cluster-wide governance and the credential-broker grant catalog.
|
||||
|
||||
Consequences, and the second one is real work:
|
||||
|
||||
- **We do not provision our own database.** We *declare a consumer request* and
|
||||
`rapp-postgres` provisions it. Writing a CNPG `Database` manifest into this
|
||||
repo's `deploy/`, or adding a managed role to a shared cluster's spec, would
|
||||
both be reaching across that boundary — the latter also mutating shared
|
||||
infrastructure other consumers depend on.
|
||||
- **Credentials are leased, not mounted.** A short-lived lease means the
|
||||
connection string can change under a running pod, so the store cannot read a
|
||||
DSN once at startup and hold it forever. T03 and T05 must account for
|
||||
credential refresh; a long-lived pool built on an expired lease fails at the
|
||||
worst possible moment.
|
||||
|
||||
Either way **no secret enters this repo, this workplan, or any evidence dump**.
|
||||
Runtime secret custody stays OpenBao's per `SCOPE.md`, which is exactly what
|
||||
the broker lease implements.
|
||||
|
||||
## Placement: shared by default, movable by design
|
||||
|
||||
|
|
@ -110,16 +128,62 @@ priority: high
|
|||
state_hub_task_id: "b4701256-f235-4474-9dcf-7cb09f62b873"
|
||||
```
|
||||
|
||||
Provision a dedicated **database** on an existing **shared** cluster, per the
|
||||
principles above. Candidates are `net-kingdom-pg` (already exists, and
|
||||
`tenant-engine` is a NetKingdom service) and `apps-pg`. Prefer the one whose
|
||||
grouping matches the product family, since that is the axis a future split
|
||||
would most likely follow — moving is cheap, but moving less often is cheaper.
|
||||
**Target: `platform-pg`, via a `PostgresConsumer` declaration to
|
||||
`rapp-postgres`.** Not `net-kingdom-pg` or `apps-pg`, and not provisioned by us.
|
||||
|
||||
Note the deviation from precedent and why: `user-engine` runs a dedicated
|
||||
Reasoning, revised 2026-08-17 after finding the actual contract. The candidate
|
||||
list originally weighed product-family fit and would have picked
|
||||
`net-kingdom-pg`. But `platform-pg` is the shared cluster that has a
|
||||
*governed provisioning path* — `rapp-postgres` owns its consumer declarations,
|
||||
role provisioning, isolation tests, and recovery procedure. The other clusters
|
||||
carry their roles as hand-added entries in the shared cluster spec
|
||||
(`net-kingdom-pg` has `privacyidea`, `apps-pg` has `vergabe` and
|
||||
`coulomb_social`). Choosing product-family fit would mean improvising outside
|
||||
the one contract that exists, to gain an affinity that portability makes cheap
|
||||
to change later anyway. `audit-core` is the precedent: a platform service, on
|
||||
`platform-pg`, declared as a consumer.
|
||||
|
||||
Note the deviation from `user-engine` and why: they run a dedicated
|
||||
`user-engine-pg`. We are deliberately *not* copying that. Their choice is fine
|
||||
and ours is reversible in an afternoon; picking dedicated now would spend
|
||||
capacity on an isolation guarantee nothing has asked for yet.
|
||||
and ours is reversible; picking dedicated now would spend capacity on an
|
||||
isolation guarantee nothing has asked for yet.
|
||||
|
||||
The declaration to request, modelled on `consumers/audit-core.yaml`:
|
||||
|
||||
```yaml
|
||||
apiVersion: rapp-postgres.railiance.io/v1alpha1
|
||||
kind: PostgresConsumer
|
||||
metadata:
|
||||
name: tenant-engine
|
||||
spec:
|
||||
database: tenant_engine
|
||||
schema: tenant_engine
|
||||
costAttributionKey: platform:tenant-engine
|
||||
clientNamespaces: [tenant-engine]
|
||||
roles:
|
||||
owner: tenant_engine_owner
|
||||
migration: tenant_engine_migrate
|
||||
runtime: tenant_engine_app
|
||||
limits:
|
||||
migrationConnections: 2
|
||||
runtimeConnections: 12
|
||||
statementTimeout: 30s
|
||||
idleInTransactionSessionTimeout: 15s
|
||||
tenantKeyingRequired: true
|
||||
tenantIsolation: consumer-service-boundary
|
||||
```
|
||||
|
||||
Two things to raise with them rather than assume:
|
||||
|
||||
- **`tenantKeyingRequired: true`** is right for `audit-core`, whose rows are
|
||||
per-tenant. Ours are *about* tenants — `tenant_id` is the primary key of the
|
||||
`tenants` table, not a partition key on someone else's data. Confirm that
|
||||
their isolation tests read our shape correctly rather than flagging a false
|
||||
positive.
|
||||
- **Statement timeout.** 30s is generous for this workload; every query here is
|
||||
a single-row lookup or a small transaction. A tighter timeout is a better
|
||||
failure mode for a service `flex-auth` calls synchronously on the
|
||||
authorization path — a slow query should fail closed fast, not hold the PDP.
|
||||
|
||||
Record what would trigger a move to dedicated, so the reversal is a judgement
|
||||
already made rather than one improvised under pressure. Candidate triggers:
|
||||
|
|
@ -133,13 +197,17 @@ backup story at all, so this is a gain to claim explicitly rather than inherit
|
|||
by accident — and a shared cluster means inheriting *someone else's* retention
|
||||
choice, which is worth checking rather than assuming.
|
||||
|
||||
Write whatever manifest the choice needs into `deploy/` — a CNPG `Database`
|
||||
resource against the shared cluster, not a new `Cluster`, if that is the
|
||||
correct shape.
|
||||
**Do not write a CNPG `Database` or `Cluster` manifest into this repo's
|
||||
`deploy/`.** That surface belongs to `rapp-postgres`. Our `deploy/` gains only
|
||||
the consumption side — how the workload reaches the leased credential.
|
||||
|
||||
Done when the database exists on shared capacity, the connection reaches the
|
||||
app only through an injected URL, and the move-to-dedicated trigger list and
|
||||
runbook are recorded.
|
||||
Done when `rapp-postgres` has provisioned the consumer, the connection reaches
|
||||
the app only through an injected, refreshable credential, and the
|
||||
move-to-dedicated trigger list and runbook are recorded.
|
||||
|
||||
Status 2026-08-17: declaration requested from `rapp-postgres`; awaiting their
|
||||
provisioning and an answer on how a workload consumes the broker lease in
|
||||
practice. Nothing to apply on our side until that returns.
|
||||
|
||||
## T02 - Implement `PostgresTenantStore`
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue