diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index b553aac..1dfa2b6 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -20,7 +20,7 @@ | workplan | RAILIANCE-WP-0016 | finished | — | workplans/RAILIANCE-WP-0016-apps-pg-resource-evidence.md | | workplan | RAILIANCE-WP-0016 | finished | — | workplans/RAILIANCE-WP-0016-architecture-cleanup-backlog.md | | workplan | RAILIANCE-WP-0017 | finished | — | workplans/RAILIANCE-WP-0017-consumption-mode-enforcement.md | -| workplan | RPF-WP-0018 | active | — | workplans/RPF-WP-0018-policy-surface-alignment.md | +| workplan | RPF-WP-0018 | proposed | — | workplans/RPF-WP-0018-policy-surface-alignment.md | | task | RAILIANCE-WP-0005-T01 | done | — | workplans/RAILIANCE-WP-0005-credential-request-and-lease-broker.md | | task | RAILIANCE-WP-0005-T02 | done | — | workplans/RAILIANCE-WP-0005-credential-request-and-lease-broker.md | | task | RAILIANCE-WP-0005-T03 | done | — | workplans/RAILIANCE-WP-0005-credential-request-and-lease-broker.md | @@ -81,10 +81,10 @@ | task | RAILIANCE-WP-0016-T04 | done | — | workplans/RAILIANCE-WP-0016-architecture-cleanup-backlog.md | | task | RAILIANCE-WP-0016-T05 | done | — | workplans/RAILIANCE-WP-0016-architecture-cleanup-backlog.md | | task | RAILIANCE-WP-0017-T01 | done | — | workplans/RAILIANCE-WP-0017-consumption-mode-enforcement.md | -| task | RPF-WP-0018-T01 | done | — | workplans/RPF-WP-0018-policy-surface-alignment.md | -| task | RPF-WP-0018-T02 | done | — | workplans/RPF-WP-0018-policy-surface-alignment.md | -| task | RPF-WP-0018-T03 | done | — | workplans/RPF-WP-0018-policy-surface-alignment.md | -| task | RPF-WP-0018-T04 | done | — | workplans/RPF-WP-0018-policy-surface-alignment.md | -| task | RPF-WP-0018-T05 | done | — | workplans/RPF-WP-0018-policy-surface-alignment.md | -| task | RPF-WP-0018-T06 | done | — | workplans/RPF-WP-0018-policy-surface-alignment.md | -| task | RPF-WP-0018-T07 | done | — | workplans/RPF-WP-0018-policy-surface-alignment.md | +| task | RPF-WP-0018-T01 | todo | — | workplans/RPF-WP-0018-policy-surface-alignment.md | +| task | RPF-WP-0018-T02 | todo | — | workplans/RPF-WP-0018-policy-surface-alignment.md | +| task | RPF-WP-0018-T03 | todo | — | workplans/RPF-WP-0018-policy-surface-alignment.md | +| task | RPF-WP-0018-T04 | todo | — | workplans/RPF-WP-0018-policy-surface-alignment.md | +| task | RPF-WP-0018-T05 | todo | — | workplans/RPF-WP-0018-policy-surface-alignment.md | +| task | RPF-WP-0018-T06 | todo | — | workplans/RPF-WP-0018-policy-surface-alignment.md | +| task | RPF-WP-0018-T07 | todo | — | workplans/RPF-WP-0018-policy-surface-alignment.md | diff --git a/docs/placement-policy.md b/docs/placement-policy.md index a4e0208..b68b28e 100644 --- a/docs/placement-policy.md +++ b/docs/placement-policy.md @@ -70,50 +70,15 @@ has an answer that is not archaeology. The **placement owner** decides placement changes for that workload and is accountable for watching its triggers. It is not the same role as the operator of the cluster. -| Workload | Substrate | Provided level | Placement owner | Co-signer | §13 evidence | -| --- | --- | --- | --- | --- | --- | -| `audit-core` | `platform-pg` | P1 | `audit-core` | this repo | probes exist | -| `tenant-engine` | `platform-pg` | P1 | `tenant-engine` | this repo | probes exist | -| `user-engine` | `user-engine-pg` | P2 | `net-kingdom` | this repo | not assessed here | -| `target-revenue` | `target-revenue-pg` | P2 | `target-revenue` | this repo | not assessed here | -| `forgejo` | `forgejo-db` | P2 | this repo | — | single consumer | -| `vergabe` | `apps-pg` | P1 | `vergabe-teilnahme` | this repo | **absent** | -| `coulomb_social` | `apps-pg` | P1 | `coulomb-social` | this repo | **absent** | - -### 3.1 Two of these P1 levels are asserted without evidence - -§13.1 claims a level only with its artifact present. The P1–P4 artifact is -*"provisioning declaration plus the platform's isolation probes"*. - -`platform-pg` has them — `rapp-postgres` runs 15 adversarial probes against the -consumer boundary. **`apps-pg` has none.** `scripts/capture-apps-pg-evidence.py` -captures *capacity* evidence for `resource-control`; it makes no isolation -assertion. So the P1 levels recorded above for `vergabe` and `coulomb_social` -are, as of 2026-08-17, **provisioning declarations without probes** — stated -here rather than quietly carried, because a table of levels that mixes -evidenced and unevidenced entries without saying which is which is the failure -§13.1 exists to prevent. - -Recorded as owed by this repo. Until the probes exist, those two rows should be -read as *intended* placement, not *verified* placement. - -### 3.2 `apps-pg` cannot produce the noisy-neighbour artifact at all - -Worth separating from the gap above, because it is not a matter of writing a -missing test. §13's P1–P2 noisy-neighbour artifact requires a per-consumer -usage baseline, a saturation run, and **evidence that the governance controls -bind — that the greedy consumer is held at its limits.** - -`apps-pg` has no per-consumer governance controls. Its connection pool is not -partitioned, there is no per-consumer connection allowance, no statement -timeout and no resource limit on the pod. There is nothing that could be shown -to bind, so a run against it could only demonstrate that a saturating consumer -degrades its co-residents — which §13.4 explicitly rejects as an artifact that -"can only fail, or that passes by being run gently enough". - -The honest sequence is therefore: **controls first, artifact second.** Writing -the probe before the limits exist would produce an overclaim wearing the -costume of evidence. Tracked as `RPF-WP-0019`. +| Workload | Substrate | Provided level | Placement owner | Co-signer | +| --- | --- | --- | --- | --- | +| `audit-core` | `platform-pg` | P1 | `audit-core` | this repo | +| `tenant-engine` | `platform-pg` | P1 | `tenant-engine` | this repo | +| `user-engine` | `user-engine-pg` | P2 | `net-kingdom` | this repo | +| `target-revenue` | `target-revenue-pg` | P2 | `target-revenue` | this repo | +| `forgejo` | `forgejo-db` | P2 | this repo | — | +| `vergabe` | `apps-pg` | P1 | `vergabe-teilnahme` | this repo | +| `coulomb_social` | `apps-pg` | P1 | `coulomb-social` | this repo | **The consuming repo is the placement owner; this repo co-signs.** The consumer knows its latency requirement, its erasure horizon and its compliance diff --git a/docs/tenancy-posture.md b/docs/tenancy-posture.md index df1db9e..29fad61 100644 --- a/docs/tenancy-posture.md +++ b/docs/tenancy-posture.md @@ -14,19 +14,6 @@ levels below are `0`. None of them is an admission of weakness, and §6 is explicit that a declared low level is conformant while an unevidenced high level is not. -**Every level below carries what §13 requires of it.** §13.1: a level is -claimed only with its evidence artifact present. §13.1a: at or below the -"no control" rung, a declaration requires a **stated reason** instead, because -there is nothing to overclaim at the bottom of a ladder. Floor claims here -carry `reason:`; claims above the floor carry `evidence:`. A claim with -neither would be the overclaim §6 prohibits. - -Where `target` equals `current`, that is a **settled position, not a stalled -trajectory** — §6 permits a low level to be permanent by design, on the -`flex-auth` precedent (`I1` forever, because verifying its own inputs would -make it the identity provider its scope refuses to be). Those lines carry -`permanent: true` so §12's guard does not nag them. - ## Why this is a vector *set* and not a vector §5 asks a **service** for one level per axis. `railiance-platform` is an OAS @@ -97,65 +84,28 @@ tenancy: tenancy: service: openbao role: credential-provider - current: { I: 0, A: 0, E: 0, P: 0, R: 0 } - target: { I: 0, A: 0, E: 0, P: 0, R: 1 } - permanent: [I, A, E] + current: { I: 0, A: 2, E: 0, P: 0, R: 0 } + target: { I: 0, A: 2, E: 0, P: 0, R: 1 } service_class: latency-critical reviewed: "2026-08-17" - reason: - I: "No tenant concept. OpenBao authenticates workloads via Kubernetes - auth; it never receives, parses or validates a NetKingdom tenant - identifier. Permanent by design — a secrets engine that resolved - tenant identity would be duplicating tenant-engine." - A: "No tenant context is carried, so there is nothing to bind. Permanent - by design, on the §6 flex-auth precedent." - E: "No tenant-keyed data. OpenBao holds workload secrets, not tenant - records." - P: "One instance, every consumer shares it. Single-node rail; §17 - scaling demands apply." - R: "No retention position on audit device output or on KV version - history." gap: - R: "R1 (platform default) is reachable cheaply and is not yet declared. - The only line here with a real trajectory." + R: "No retention position on audit device output or on KV version history. + R1 (platform default) is reachable cheaply and is not yet declared." + notes: + - "A2 is claimed on the consumer boundary, not a tenant boundary: policy + per workload path, bound once, centrally, in OpenBao's own policy + engine. It is NOT delegated to flex-auth, so A3 is not claimed and is + not currently a target — a PDP in the credential path would put + flex-auth on OpenBao's availability path and OpenBao on flex-auth's." + - "E is 0 on the TENANT axis and that number is misleading without this + sentence: consumer separation in OpenBao is structural — a workload + token's policy cannot address another workload's path at all, which is + E4-shaped machinery. It scores 0 because the axis measures the tenant + boundary and OpenBao has no tenant dimension. See the provider note." + - "P0 is accurate and deliberate: one instance, every consumer shares it. + Single-node rail; §17 scaling demands apply." ``` -**Correction, 2026-08-17.** This vector first declared `A: 2`. That was -wrong twice over and both errors are worth recording rather than quietly -editing. - -*It was internally incoherent.* `E: 0` was justified on the ground that -OpenBao has no tenant dimension. §4.2's `A2` reads "a single local -authorization boundary; **tenant context** bound once, centrally" — the same -dimension. A declaration cannot invoke the absence of tenant context to claim -the floor on one axis and ignore it to claim a rung on the next. - -*It was unevidenced at the moment of claiming.* §13.1 requires the artifact to -be present when the level is claimed, and none was cited. §13.1a would not -have rescued it: the table defines an artifact from `A2` upward, so `A2` is -precisely the first rung where the exemption stops applying. - -The irony is the point. This repo routed a finding about unevidenced claims -in the same week it made one. - -**What the corrected zeros conceal, and why the provider note below exists.** -`A: 0` now reads as though OpenBao performs no authorization. It performs a -great deal, it is central, and it is mechanically evidenced — -`scripts/openbao-verify-token-grants.py` mints a scoped child token, asserts -it *can* sign with `ssh/sign/agt-role`, and asserts it *cannot* read policy -metadata, then revokes by accessor. That is exactly the shape §13 asks for at -`A2`: choke point identified, unbound request refused. It is pointed at the -**consumer** boundary, and none of the five axes has anywhere to put it. - -That evidence is therefore cited under the provider statement rather than -against a consumer axis. Consumer separation in OpenBao is structural — a -workload token's policy cannot address another workload's path at all, which -is `E4`-shaped machinery — and the framework's honest application scores it -`0`. - -`A3` is not a target: a PDP in the credential path would put `flex-auth` on -OpenBao's availability path and OpenBao on `flex-auth`'s. - ### `platform-pg` — policy relationship only Declared by `rapp-postgres`. This repo does not restate it. What this repo @@ -181,18 +131,6 @@ plus the provider statement is the accurate form. This is routed as a finding. **The five ladders describe a consumer of storage. They do not describe a provider of it.** -**Narrowed on re-reading, 2026-08-17.** An earlier version of this finding -claimed the framework had no way to say "this zero is structural, not weak". -That was wrong: §6 says exactly that, and §13.1a supplies the mechanism — -`target` equal to `current` with a stated reason is a settled position, on the -`flex-auth` `I1`-forever precedent. This declaration now uses it. Half of the -finding is withdrawn. - -What survives is the other half, and it is not expressible: a provider cannot -state **what level it makes reachable for the workloads it holds**, nor record -a control that is real, mechanically evidenced, and simply not on any of the -five consumer axes. - Every service above scores at or near zero on I, A and E, and in each case for the same structural reason rather than for a weakness: a storage or credential platform has no tenant dimension of its own. It is exactly as strong or weak as diff --git a/workplans/RPF-WP-0019-apps-pg-recoverability-and-controls.md b/workplans/RPF-WP-0019-apps-pg-recoverability-and-controls.md deleted file mode 100644 index 9e1fa14..0000000 --- a/workplans/RPF-WP-0019-apps-pg-recoverability-and-controls.md +++ /dev/null @@ -1,130 +0,0 @@ ---- -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.