RPF-WP-0018: mark unevidenced P1 claims; seed RPF-WP-0019 for apps-pg
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Has been cancelled

Placement policy 3.1 records that the P1 levels for vergabe and
coulomb_social are provisioning declarations without the SS13 isolation
probes - apps-pg has none. 3.2 records that apps-pg cannot produce the
noisy-neighbour artifact at all until per-consumer controls exist, per
SS13.4: with nothing to bind, a probe could only demonstrate degradation.

RPF-WP-0019 seeded for the three live defects the disclosure work found:
no backup, no per-consumer controls, no isolation probes. Kept separate
from RPF-WP-0018 so declaring is not confused with fixing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
codex 2026-08-17 23:01:57 +02:00
parent cc4e659a9e
commit b15215d191
2 changed files with 174 additions and 9 deletions

View file

@ -0,0 +1,130 @@
---
id: RPF-WP-0019
type: workplan
title: "apps-pg: backup, per-consumer controls, and the isolation probes they make possible"
domain: financials
repo: railiance-platform
status: proposed
owner: codex
topic_slug: railiance
created: "2026-08-17"
updated: "2026-08-17"
related:
- RPF-WP-0018
origin: residual
origin_ref: RPF-WP-0018
---
# RPF-WP-0019 — apps-pg recoverability and per-consumer controls
## Goal
Close the three defects `RPF-WP-0018` surfaced in `apps-pg` by writing its
quota disclosure. Documentation found them; only this workplan fixes them.
## Why this is separate from RPF-WP-0018
That workplan declared posture and policy. This one changes a live cluster.
Keeping them apart matters: a declaration workplan that quietly starts editing
production is how "we wrote it down" becomes indistinguishable from "we fixed
it". The declaration is published as-is, with the defects visible, and this is
the record of closing them.
## The three defects
**D1 — `apps-pg` has no backup.** No `barmanObjectStore`, no `retentionPolicy`,
nothing. This is not a short retention window; it is no recovery path at all,
on a cluster holding two S5 application databases. Its R level is `R0` and
`R0` here means unrecoverable, not merely un-erasable.
**D2 — no per-consumer controls.** The connection pool is unpartitioned, so
one consumer can exhaust the cluster while staying politely inside its own
expectations. No `statement_timeout`, no `idle_in_transaction_session_timeout`,
no CPU or memory limits — the pod is BestEffort QoS and is the first thing
evicted under node pressure.
**D3 — no isolation probes**, so the `P1` levels recorded for `vergabe` and
`coulomb_social` in `docs/placement-policy.md` §3.1 are provisioning
declarations without the §13 artifact.
## Sequencing, and why it is not the obvious one
**D1 first.** It is the only one whose failure is unrecoverable. A cluster
with no backup is one bad afternoon from data loss that no amount of isolation
evidence compensates for.
**D2 before D3, necessarily.** Tenancy Posture §13.4: an artifact must assert
something achievable. The noisy-neighbour artifact requires showing the
governance controls *bind*. With no controls there is nothing to bind, so a
probe written now could only demonstrate degradation — an artifact that "can
only fail, or that passes by being run gently enough". Writing the probe first
would produce an overclaim wearing the costume of evidence.
D1 also depends on a backup target, which is `resource-control`'s bucket and
the `platform-pg-backup-s3` credential lane — the same handoff
`make postgres-backup-deploy` waits on. Check whether that is now live before
assuming this is blocked.
## Tasks
```task
id: RPF-WP-0019-T01
status: todo
priority: high
```
**Establish a backup target for `apps-pg`.** Confirm the state of the
`resource-control` bucket and the `platform-pg-backup-s3` OpenBao Secret; if
live, configure `barmanObjectStore` and a `retentionPolicy` on the cluster. If
not live, record the dependency and say so — do not leave the absence
undocumented a second time.
```task
id: RPF-WP-0019-T02
status: todo
priority: high
```
**Declare and enforce per-consumer controls.** Per-consumer connection
allowance, `statement_timeout`, `idle_in_transaction_session_timeout`, and
pod resource requests/limits to lift `apps-pg` off BestEffort QoS. Publish
every value in `docs/s3-consumer-interfaces.md` before it takes effect —
§10.2 is a disclosure rule, and applying a timeout consumers learn about by
hitting it would breach the rule while implementing it.
```task
id: RPF-WP-0019-T03
status: todo
priority: medium
```
**Declare the ceiling and overflow target.** Owed under this repo's own
Rule P-4.1 before `apps-pg`'s third consumer; it is at two. Name the binding
resource per Rule P-4.2 — memory or connections — and a named overflow
substrate per P-4.3.
```task
id: RPF-WP-0019-T04
status: todo
priority: medium
```
**Isolation probes, after T02.** Consumer-boundary probes on the
`rapp-postgres` model, then the §13 noisy-neighbour artifact: per-consumer
baseline, saturation run, evidence the controls bind, degradation measured and
judged against each consumer's declared service class. Update
`docs/placement-policy.md` §3.1 and `docs/tenancy-posture.md` when the P1
claims become evidenced.
## Boundaries
- `apps-pg` only. `platform-pg`'s equivalents are `rapp-postgres`'s.
- No consumer is migrated. This changes the cluster, not who is on it.
- Values are published before they are enforced, never after.
## Risks
**Applying limits to a live cluster breaks a consumer that was relying on
their absence.** Most likely with `statement_timeout`. Mitigation is the
disclosure-first ordering in T02, which gives consumers a window to object.
**T01 stays blocked on a handoff outside this repo and the cluster keeps no
backup meanwhile.** Mitigation is that T01 requires the dependency be recorded
explicitly rather than left as a silent absence — which is exactly how D1
survived this long.