T01 docs/tenancy-posture.md - vector set per service rather than one repo vector, with the provider-versus-consumer finding routed to net-kingdom. T02/T03/T06 docs/placement-policy.md - accepts placement ownership scoped to rule-here/number-there, records a placement owner per workload, reports the latency-critical + batch co-residency on platform-pg, marks the connection-ceiling trigger unmonitored pending railiance-telemetry, and answers the retention floor/ceiling question. T04 s3-consumer-interfaces 1.1.0 - quota disclosure per SS10.2. Surfaces that apps-pg has no backup, no resource limits and no tuned parameters. T05 docs/adr/ created with a mandatory-frontmatter convention and the first three ADRs. This repo previously held none. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
118 lines
6.3 KiB
Markdown
118 lines
6.3 KiB
Markdown
# S3 consumer service interfaces
|
|
|
|
Work record: `RAILIANCE-WP-0016` item 17; `RPF-WP-0018` T04
|
|
Date: 2026-08-15, revised 2026-08-17
|
|
Contract version of this index: `1.1.0`
|
|
|
|
Consumers bind to these contracts, not to Helm chart names or pod topology.
|
|
Changing an engine behind a versioned interface does not bump the interface
|
|
unless the consumer-visible fields change.
|
|
|
|
| Interface | Version | Who it is for | Authority |
|
|
| --- | --- | --- | --- |
|
|
| Shared application database | `apps-pg.v1` | S5 apps that need an isolated database on `apps-pg` | `docs/apps-pg.md` |
|
|
| Isolated platform database | `rapp-postgres.v1` | Platform services that need a CNPG consumer on `platform-pg` | `rapp-postgres/declarations/rapp.yaml` `consumer_contract` |
|
|
| Standing runtime secret | `ccr-workload-kv.v1` | Workloads that need a KV path via External Secrets | `schemas/credential-change-request.schema.yaml` |
|
|
| Short-lived lease | `credential-grant.v1` | Operators/agents and dynamic DB roles | `credential-grants/catalog.yaml` |
|
|
| Rapp credential bind | `rapp-lane.v1` | How a rapp names the two lanes above | `docs/rapp-credential-lane-binding.md` |
|
|
| Consumption-mode gate | `consumption-mode.v1` | New orders for a restricted financial entity | `docs/consumption-mode-enforcement.md` |
|
|
|
|
## Compatibility rules
|
|
|
|
- A `vN` document may add optional fields. Removing or renaming a
|
|
required field is `vN+1`.
|
|
- Values never appear in an interface document. Paths, grant ids, CCR
|
|
ids, and role names are the contract.
|
|
- `make pg-deploy` / Bitnami DSN
|
|
`postgresql-ha-pgpool.platform.svc.cluster.local` is **not** an
|
|
interface. That stack is retired.
|
|
|
|
## Quota disclosure
|
|
|
|
Added in `1.1.0` — additive, so no consumer contract breaks.
|
|
|
|
Tenancy Posture §10.2: *quotas are disclosed, not discovered.* Every consumer
|
|
is told at provisioning what the platform will refuse to do for it, and told
|
|
again when that changes. **A consumer learning its statement timeout by hitting
|
|
it in production is a disclosure failure, not a consumer bug.**
|
|
|
|
The obligation this places on S3 is a standing one: a change to any value in
|
|
the tables below is announced to bound consumers before it takes effect. The
|
|
values themselves are read from the cluster CR and are the package owner's to
|
|
set — this index publishes them, it does not choose them.
|
|
|
|
Retention disclosure carries the additional §7 rule from
|
|
`docs/placement-policy.md`: on a shared cluster the **effective erasure
|
|
horizon is the maximum declared across co-residents**, not the value the
|
|
consumer requested. That number is disclosed, and it moves when a co-resident
|
|
changes.
|
|
|
|
## `apps-pg.v1`
|
|
|
|
Consumer supplies: app name, namespace, database name, role name. Platform
|
|
provisions the role and CNPG `Database`, labels the namespace
|
|
`railiance.io/postgres-client=apps-pg`, and publishes a runtime Secret.
|
|
RW endpoint: `apps-pg-rw.databases.svc.cluster.local:5432`. Bootstrap
|
|
role `apps_admin` is not a consumer credential.
|
|
|
|
**Quotas and limits** — read from `helm/apps-pg-cluster.yaml`, 2026-08-17:
|
|
|
|
| Property | Value | Note |
|
|
| --- | --- | --- |
|
|
| `max_connections` | **PostgreSQL default (100)** | Not tuned in the CR. Not partitioned per consumer — there is no per-consumer connection allowance, so one consumer can exhaust the cluster. |
|
|
| `statement_timeout` | **none** | Not set. A runaway query runs until it finishes. |
|
|
| `idle_in_transaction_session_timeout` | **none** | Not set. |
|
|
| CPU / memory limits | **none declared** | The pod is BestEffort QoS and is the first thing evicted under node pressure. |
|
|
| Instances | 1 | No HA. Restart recovery only, per the single-node rail. |
|
|
| Storage | 10Gi | |
|
|
| Backup | **none configured** | No `barmanObjectStore`, no `retentionPolicy`. |
|
|
| Effective erasure horizon | **undefined** | Follows from the line above. |
|
|
|
|
**Read the backup row before provisioning against this cluster.** `apps-pg`
|
|
has no backup at all — not a short retention, none. Recovery from data loss is
|
|
not available on this interface today. This is disclosed rather than fixed
|
|
because §10.2 requires disclosure now and the fix needs a backup target; it is
|
|
recorded as the R-axis gap in `docs/tenancy-posture.md` and is the highest
|
|
value item that declaration surfaced.
|
|
|
|
The unpartitioned connection pool is the same shape of risk `rapp-postgres`
|
|
reported for `platform-pg`: the bound is aggregate, so every consumer can be
|
|
politely inside its own expectations while the cluster still fails.
|
|
|
|
## `rapp-postgres.v1`
|
|
|
|
Consumer is a `consumers[].declaration`. Boundary unit is
|
|
`database-per-consumer`. Credential type is
|
|
`openbao-dynamic-database-credential` via grant
|
|
`rapp-postgres/<consumer>-<role>`. Tenant identifiers are opaque.
|
|
|
|
**Quotas and limits** — authority is `rapp-postgres`'s
|
|
`helm/platform-pg-cluster.yaml`; reproduced here for disclosure, 2026-08-17:
|
|
|
|
| Property | Value | Note |
|
|
| --- | --- | --- |
|
|
| `max_connections` | 100 | Aggregate, not per consumer. ~14 per consumer (12 runtime + 2 migration) puts the practical ceiling near 6. |
|
|
| Memory limit | 1Gi | Expected to bind before connections do — ~10MB per backend. Memory pressure OOM-kills; connection exhaustion errors cleanly. |
|
|
| CPU | 100m request / 1 limit | |
|
|
| Instances | 1 | No HA. |
|
|
| Storage | 20Gi | |
|
|
| Backup retention | 30d | Instance-wide, per `rapp-postgres` ADR-0002. |
|
|
| Effective erasure horizon | **30d, or the max across co-residents** | A consumer needing shorter cannot have it at P1. See `docs/placement-policy.md` §7. |
|
|
| Statement timeout | consumer-set | `tenant-engine` sets 5s for its own path; not a platform default. |
|
|
| Declared ceiling / overflow target | **not yet declared** | Owed by `rapp-postgres` before the third consumer, per `docs/placement-policy.md` Rule P-4.1. |
|
|
|
|
Service classes currently co-resident on `platform-pg`: `latency-critical`
|
|
(`tenant-engine`) and `batch` (`audit-core`). Nothing prioritises between them
|
|
— community PostgreSQL has no resource governor. Reported per Tenancy Posture
|
|
§8.3.3.
|
|
|
|
## `ccr-workload-kv.v1` and `credential-grant.v1`
|
|
|
|
Unchanged semantics from `docs/credential-change-approval.md` and
|
|
`docs/credential-broker.md`. The version name exists so a rapp can cite
|
|
the lane without restating TTL, delivery, or revocation rules.
|
|
|
|
## Not yet an interface
|
|
|
|
Valkey and in-cluster MinIO have capability blocks in `SCOPE.md` and no
|
|
live service. They do not receive a `v1` until a consumer exists.
|