RPF-WP-0018 T01-T06: publish S3 posture, placement policy, quotas, ADR surface

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>
This commit is contained in:
codex 2026-08-17 21:55:11 +02:00
parent b83194741d
commit e7e4e33bb8
9 changed files with 809 additions and 12 deletions

View file

@ -1,8 +1,8 @@
# S3 consumer service interfaces
Work record: `RAILIANCE-WP-0016` item 17
Date: 2026-08-15
Contract version of this index: `1.0.0`
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
@ -27,6 +27,26 @@ unless the consumer-visible fields change.
`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
@ -35,6 +55,30 @@ provisions the role and CNPG `Database`, labels the namespace
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
@ -42,6 +86,26 @@ Consumer is a `consumers[].declaration`. Boundary unit 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