178 lines
8.1 KiB
Markdown
178 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**.
|