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>
177 lines
8.1 KiB
Markdown
177 lines
8.1 KiB
Markdown
# S3 tenancy posture declaration
|
||
|
||
Work record: `RPF-WP-0018` T01
|
||
Declared: 2026-08-17
|
||
Framework: `net-kingdom/canon/standards/tenancy-posture_v0.1.md` (draft-5, proposed)
|
||
Contract version of this document: `1.0.0`
|
||
|
||
This is `railiance-platform`'s response to the Tenancy Posture ratification
|
||
test (§20.2): *"Each publishes its own posture vector as part of review. The
|
||
framework is validated by whether it can describe them accurately."*
|
||
|
||
It declares under §6 — **conformance is accuracy, not altitude**. Several
|
||
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.
|
||
|
||
## Why this is a vector *set* and not a vector
|
||
|
||
§5 asks a **service** for one level per axis. `railiance-platform` is an OAS
|
||
**layer** holding custody of several services with materially different
|
||
postures. A single vector for the repo would be an average of things that are
|
||
not alike, which is the inaccuracy §6 prohibits.
|
||
|
||
So: one vector per service, plus an explicit statement of what this repo owns
|
||
versus what it merely holds policy for. The distinction matters because
|
||
`RAILIANCE-WP-0012` and `RAILIANCE-WP-0015` deliberately moved the deployable
|
||
surface to the `rapp-*` repos while custody and policy stayed here.
|
||
|
||
| Service | This repo's relationship | Deployable owned by |
|
||
| --- | --- | --- |
|
||
| `apps-pg` | **owns** — cluster CR is `helm/apps-pg-cluster.yaml` | this repo |
|
||
| `forgejo-db` | **owns** — cluster CR is `helm/forgejo-db-cluster.yaml` | this repo |
|
||
| `openbao` | **custody and policy**; package extracted | `rapp-openbao` |
|
||
| `platform-pg` | **policy only**; consumer contract and grants here | `rapp-postgres` |
|
||
| Valkey, MinIO | declared capability, no live instance | — |
|
||
|
||
## The vectors
|
||
|
||
### `apps-pg` — shared cluster for S5 application databases
|
||
|
||
```yaml
|
||
tenancy:
|
||
service: apps-pg
|
||
role: storage-provider
|
||
current: { I: 0, A: 0, E: 0, P: null, R: 0 }
|
||
target: { I: 0, A: 0, E: 0, P: null, R: 2 }
|
||
service_class: interactive
|
||
reviewed: "2026-08-17"
|
||
gap:
|
||
R: "No backup is configured on this cluster at all — no barmanObjectStore,
|
||
no retentionPolicy, no deletion path. R0 is the accurate reading and
|
||
it is worse than it looks: R0 here means no recovery, not merely no
|
||
erasure policy. Closing this is the highest-value item this
|
||
declaration surfaced. Target R2 requires a backup target first."
|
||
notes:
|
||
- "I/A/E are 0 by construction, not by neglect. apps-pg holds no tenant
|
||
concept, carries no tenant claim, and applies no tenant predicate. The
|
||
tenant boundary for data on this cluster sits entirely in the consuming
|
||
S5 application. See the provider note below."
|
||
- "P is null rather than a number. See §P-axis note."
|
||
```
|
||
|
||
### `forgejo-db` — dedicated cluster for the forge
|
||
|
||
```yaml
|
||
tenancy:
|
||
service: forgejo-db
|
||
role: storage-provider
|
||
current: { I: 0, A: 0, E: 0, P: null, R: 0 }
|
||
target: { I: 0, A: 0, E: 0, P: null, R: 2 }
|
||
service_class: interactive
|
||
reviewed: "2026-08-17"
|
||
gap:
|
||
R: "Backup exists as a documented procedure (docs/forgejo-backup.md), not
|
||
as a declared retention with a published erasure horizon. R2 needs the
|
||
horizon published to the consumer, which is the point of the level."
|
||
notes:
|
||
- "Single-consumer cluster. Provides P2 to forgejo; is not itself at P2."
|
||
```
|
||
|
||
### `openbao` — platform secrets service
|
||
|
||
```yaml
|
||
tenancy:
|
||
service: openbao
|
||
role: credential-provider
|
||
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"
|
||
gap:
|
||
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."
|
||
```
|
||
|
||
### `platform-pg` — policy relationship only
|
||
|
||
Declared by `rapp-postgres`. This repo does not restate it. What this repo
|
||
declares about it is in `docs/placement-policy.md`: the rule, the placement
|
||
owner per consumer, and the triggers. The numbers — `instances: 1`,
|
||
`max_connections: 100`, `1Gi`, `retentionPolicy: 30d` — are `rapp-postgres`'s
|
||
cluster CR and its declaration to make.
|
||
|
||
## The P-axis note — why two entries are `null`
|
||
|
||
The P ladder describes **which substrate holds a tenant's data**, from the
|
||
point of view of the workload whose data it is. `apps-pg` and `forgejo-db` are
|
||
the substrate. Asking a cluster what its own placement level is has no
|
||
well-formed answer: `apps-pg` *provides* P1 to each of its consumers, and
|
||
`forgejo-db` *provides* P2 to forgejo, but neither *is* at a level.
|
||
|
||
Declaring `P: 1` for `apps-pg` would read as though the cluster had a
|
||
placement, and a later reader would use it to argue about isolation. `null`
|
||
plus the provider statement is the accurate form. This is routed as a finding.
|
||
|
||
## Finding routed to `net-kingdom`
|
||
|
||
**The five ladders describe a consumer of storage. They do not describe a
|
||
provider of it.**
|
||
|
||
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
|
||
its consumers make it. The framework anticipates this in §3.3 and in the
|
||
"newly absorbed repo" example, but the vector shape gives a provider no way to
|
||
say the useful thing, which is not *what level am I at* but **what level do I
|
||
make reachable for the workloads I hold**.
|
||
|
||
The concrete case is the `openbao` E line. `E: 0` is literally correct and
|
||
actively misleading — the mechanism in place is credential-scoped structural
|
||
separation, the same machinery E4 describes, pointed at a consumer boundary
|
||
rather than a tenant one. A reader scanning a table of E values would rank
|
||
OpenBao below a service doing per-query filtering in application code, which
|
||
inverts the actual security position.
|
||
|
||
**Proposed amendment, offered as the first correction from review rather than
|
||
from research (§Status observes that every correction so far came from research
|
||
or relocation):** add a **provider declaration** beside the posture vector — a
|
||
platform service states, per axis, the maximum level it makes reachable and
|
||
what the consumer must do to reach it. For `apps-pg` that is: E4 unreachable
|
||
(shared credential per consumer, no per-tenant credential), E3 reachable once
|
||
the GUC contract exists, R2 blocked on a backup target. That is the sentence a
|
||
consumer actually needs, and no arrangement of the five consumer ladders
|
||
produces it.
|
||
|
||
This is offered as an addition, not a replacement. The consumer vector is
|
||
right for consumers, and this repo is not asking for the ladders to be
|
||
renumbered.
|
||
|
||
## What this repo accepts
|
||
|
||
- The P0–P4 ladder as written, by reference. It describes our estate correctly.
|
||
- §8.1's monitoring obligation, with the deferrals stated in
|
||
`docs/placement-policy.md` rather than silently.
|
||
- §9.1 — no static long-lived database credentials above E0. Already how the
|
||
credential broker works; no change needed.
|
||
- §10.2 — quota disclosure. Landed in `docs/s3-consumer-interfaces.md` 1.1.0.
|
||
- §6 — accuracy over altitude, which is why this document declares four zeros.
|
||
|
||
## Review interval
|
||
|
||
Six months, or on any of: a service moving placement level, a backup target
|
||
becoming available, or the framework reaching `accepted`. Next review due
|
||
**2027-02-17**.
|