diff --git a/workplans/RPF-WP-0018-policy-surface-alignment.md b/workplans/RPF-WP-0018-policy-surface-alignment.md new file mode 100644 index 0000000..ed8deca --- /dev/null +++ b/workplans/RPF-WP-0018-policy-surface-alignment.md @@ -0,0 +1,259 @@ +--- +id: RPF-WP-0018 +type: workplan +title: "Align S3 with the estate policy surface (Tenancy Posture + policy-nexus)" +domain: financials +repo: railiance-platform +status: proposed +owner: codex +topic_slug: railiance +created: "2026-08-17" +updated: "2026-08-17" +related: + - POLICY-NEXUS-WP-0001 + - TEN-WP-0009 + - RAPP-POSTGRES-WP-0002 +origin: routed +origin_ref: "net-kingdom/canon/standards/tenancy-posture_v0.1.md §19.2, §20" +--- + +# RPF-WP-0018 — policy surface alignment + +## Goal + +Answer the two policy asks that landed on this repo, accept what fits, +push back on what does not, and leave behind artifacts that are checkable +rather than a promise to be good. + +Done means: this repo has published a posture declaration, has accepted or +declined placement ownership in writing, has told its consumers their +quotas, and has answered `policy-nexus` on the one assignment in +`POLICY-NEXUS-WP-0001` that contradicts the OAS stack. + +## Where the policy comes from + +Two documents, from two owners, arriving in the same week. + +1. **`net-kingdom/canon/standards/tenancy-posture_v0.1.md`** (draft-5, + proposed, reviewed by nobody). Routed by `rapp-postgres` on 2026-08-17 + (message `d9da7d11`), foreshadowed by `tenant-engine` on 2026-08-16 + (message `31aed179`). It asks this repo, **co-signed with + `adaptive-pricing`**, to own database placement policy — §8.2, §19.2 — + and to publish its own posture vector as the ratification test (§20.2). +2. **`policy-nexus/workplans/POLICY-NEXUS-WP-0001`** (proposed). It + assigns this repo the *substrate* for `policy.coulomb.social` — DNS, + TLS, ingress, hosting — and includes this repo in the ~68-ADR corpus it + intends to publish from `docs/adr/*.md`. + +Both are drafts. Neither is ratified. Accepting them now is cheaper than +accepting them later, because both are still willing to change. + +## What actually applies to us, and what does not + +**Applies, and fits.** The placement ladder (P0–P4) describes our estate +accurately: `apps-pg` is a P1 shared cluster, `user-engine-pg` and +`target-revenue-pg` are P2. The §10.2 quota-disclosure obligation lands on +a document we already maintain, `docs/s3-consumer-interfaces.md`. The §9.1 +ban on static long-lived database credentials is already how the credential +broker works. Nothing in the ladder requires us to move a single workload. + +**Applies, but we cannot satisfy it yet.** §8.1 says placement triggers +MUST be *monitored*, not merely recorded. This repo has no monitoring +plane. `SCOPE.md` states we emit to `railiance-telemetry` "once the +evidence plane exists — seeded 2026-08-11, not yet implemented". Claiming +monitoring we do not have is the exact overclaim §6 prohibits, so T03 +records each trigger with a named monitor or an explicit `unmonitored` +marker, and the marker is the honest answer until telemetry lands. + +**Does not fit, and the policy should change.** Three findings, T07. + +## Findings against the policy (recommendations, not yet routed) + +**F1 — `POLICY-NEXUS-WP-0001` assigns S3 a concern S3 does not own.** +It states "`railiance-platform` owns the substrate — DNS, TLS, ingress, +hosting". Per `SCOPE.md`, DNS and OS-level concerns are S1 +`railiance-infra`, and Kubernetes ingress is S2 `railiance-cluster`; +S3 owns shared *platform services*. Accepting the assignment as written +would re-import the boundary violation that `RAIL-PL-WP-0001` and ADR-003 +exist to prevent. + +*Recommend:* split T04 of that workplan. Ingress and TLS to S2, DNS to S1 +or the reef boundary, and the genuinely S3 parts — an object-storage bucket +for the static output if it wants one, plus any credential lane — stay +here. This repo can co-sign the deployment without owning the substrate. +Adjacent and unresolved: `railiance-master` asked us on 2026-08-15 +(`ba477968`) to confirm `bao.coulomb.social` does not publish on +reef-railiance. A second public name on the same rail should go through +whatever answers that question, not around it. + +**F2 — this repo's decisions are structurally unpublishable.** +`policy-nexus` publishes canon and ADRs only, globbing `docs/adr/*.md`. +This repo has **no `docs/adr/` directory and no ADRs**. Its load-bearing +decisions live either in `docs/` as prose without frontmatter (24 files, +no status, revision, review date or owner — the fields T02/T05 of the +publication workplan require) or in the State Hub via `record_decision()`, +which is a read model and is not a publication source. The estate's +standing rule is that local files are the source of truth; for decisions, +this repo does not follow it. + +*Recommend:* not a mass conversion. T05 here promotes the small set that +is genuinely decisional — the boundary rules, the credential-lane model, +the consumption-mode gate — into ADR form with the frontmatter +`policy-nexus` needs. The runbooks stay prose and stay unpublished, which +is what that workplan's scope rule already wants. + +**F3 — §19.8 asks this repo for a number that lives in another repo.** +Cell sizing wants `platform-pg`'s declared maximum size. That spec — +`instances: 1`, `max_connections: 100`, `1Gi` — is `rapp-postgres`'s +cluster CR, not ours; `RAILIANCE-WP-0012` and `RAILIANCE-WP-0015` +deliberately moved the deployable surface to the rapp repos while this repo +kept custody and policy. The split is right and the question is misrouted +by one hop. + +*Recommend:* accept §8.2 with the scope stated — **this repo owns the rule, +`rapp-postgres` owns the number**. We declare *that* a ceiling must be +published and what happens at it; they declare what it is. Same shape for +§19.9's retention floor and ceiling. `rapp-postgres`'s own analysis already +concedes the point: memory probably binds before connections do, at roughly +10MB per backend, and that is an observation only the package owner can +make. + +**F4 — the posture vector is service-shaped; this repo is a layer.** +§5 asks a *service* to state one level per axis. `railiance-platform` is an +OAS layer that holds custody of several services with different postures. +One vector for the repo would be an average, which is exactly the +inaccuracy §6 prohibits. §20.2 invites this: "if a repo cannot express +itself in these five ladders, the ladders are wrong and this document +changes, not the repo." + +*Recommend:* a layer repo publishes a **vector set** — one per service it +has custody of — plus a statement of which it owns versus operates. T01 +does that and routes the finding back to `net-kingdom` as the framework's +first real correction from review rather than from research. + +## Boundaries + +- No workload moves placement level in this workplan. Declaring where + things are is separate from changing where they are. +- No monitoring is built here. T03 records the gap; closing it belongs + with `railiance-telemetry`. +- Co-signature with `adaptive-pricing` is *requested*, not assumed. If they + decline, T02 records single ownership and says so. +- This repo does not amend NetKingdom canon. Findings are routed; the + standard's owner decides. + +## Tasks + +```task +id: RPF-WP-0018-T01 +status: todo +priority: high +``` +**Publish the S3 posture vector set.** Write `docs/tenancy-posture.md`: +one vector per service under S3 custody (`openbao`, `apps-pg`, and the +`rapp-postgres`-operated clusters we hold policy for), each with `current`, +`target`, `reviewed`, and a `gap` note per axis below target. State plainly +which services this repo *owns* and which it only holds *policy* for. +Route F4 to `net-kingdom` with the vector set as the evidence. + +```task +id: RPF-WP-0018-T02 +status: todo +priority: high +``` +**Accept placement ownership, scoped.** Write `docs/placement-policy.md`: +adopt the P0–P4 ladder by reference (do not restate it — the canon copy is +the source), record the default (P1 for platform services), and record a +named **placement owner per workload** for every consumer on an S3-custody +cluster. State the F3 split explicitly: rule here, number in +`rapp-postgres`. Request `adaptive-pricing` co-signature; record the answer +either way. + +```task +id: RPF-WP-0018-T03 +status: todo +priority: medium +``` +**Make triggers monitorable or honestly unmonitored.** For each of the five +§8 triggers, record in `docs/placement-policy.md` what signal would fire it, +who reads that signal, and at what cadence — or the literal marker +`unmonitored: pending railiance-telemetry`. Include the §8.3.3 obligation: +report which service classes are co-resident. Today that is +`latency-critical` (`tenant-engine`) beside `batch` (`audit-core`) on +`platform-pg`, which becomes a stated risk rather than an invisible one. + +```task +id: RPF-WP-0018-T04 +status: todo +priority: medium +``` +**Disclose quotas to consumers (§10.2).** Extend +`docs/s3-consumer-interfaces.md` to `1.1.0` — additive under its own +compatibility rule — adding per-interface connection limits, statement and +idle-transaction timeouts, and backup retention to `apps-pg.v1` and +`rapp-postgres.v1`. Add the standing obligation that a change to any of +these is announced to bound consumers, not discovered by them. + +```task +id: RPF-WP-0018-T05 +status: todo +priority: medium +``` +**Create the ADR surface (F2).** Create `docs/adr/` with an ADR template +carrying the frontmatter `policy-nexus` T02/T05 consume: title, status, +revision, owner, last-reviewed, review interval. Promote the decisions that +are already load-bearing and already written down elsewhere — the S3 +boundary rule, the credential-lane model, the consumption-mode gate, +and the outcome of T02 — into ADRs. Runbooks stay prose. State in each ADR +which hub `record_decision()` id it supersedes, so the hub stops being the +only home for a decision. + +```task +id: RPF-WP-0018-T06 +status: todo +priority: low +``` +**Answer §19.9 — retention floor and ceiling.** Decide whether +`backupRetentionDays` gets a platform minimum (so a consumer asking for one +day gets a validation error rather than a quiet disappointment) and a +maximum (so nobody exhausts the volume). Record the rule here; the +enforcing validator is `rapp-postgres`'s. Carry `rapp-postgres` ADR-0002's +consequence forward: on a shared cluster a consumer's erasure horizon is the +maximum declared across co-residents, so a shorter horizon is a P2 trigger. + +```task +id: RPF-WP-0018-T07 +status: todo +priority: high +``` +**Route the findings.** Reply to `rapp-postgres` and `tenant-engine` with +T01/T02 outcomes and F3/F4. Reply to `policy-nexus` with F1 (substrate +misassignment, with the S1/S2 split recommended) and F2 (ADR surface now +being built, plus the hub-decisions gap). Reply to `railiance-master` on +`ba477968`, since a second public name on reef-railiance depends on the +same answer. Mark the routed messages read. + +## Sequencing + +T01 and T02 are the ratification inputs and go first; T07 cannot be +answered without them. T03 hangs off T02's document. T04 and T05 are +independent and can run any time. T06 is last and cheap. + +Deliberately not blocking on ratification: this repo publishes its +declaration whether or not the standard is ratified, because §6 makes +accuracy the conformance test and an accurate declaration is useful even if +the ladders are later renumbered. + +## Risks + +**Accepting ownership of a policy we cannot enforce.** §8.1 monitoring is +the specific case. Mitigation is T03's explicit `unmonitored` marker — an +owner who says what is not covered still owns it usefully; one who implies +coverage does not. + +**Scope creep into S1/S2.** F1 is the live instance. Mitigation is that +this workplan routes the finding and does not build the ingress. + +**A declaration that goes stale the day it is written.** Mitigation is the +`reviewed:` date on the vector set and the review interval in the ADR +template — the same currency mechanism `policy-nexus` T05 will read.