T01: docs/rapp-platform-service-pattern.md generalizes the ownership split already drawn in the rapp-openbao and rapp-postgres boundary docs into a reusable four-question test, a reference rapp.yaml for platform services, the grouped-rapp member rule, and the credential-lane position. It deliberately does not restate the four-axis model, which railiance-master owns. T03/T04/T05: proposals routed to the repos that own the model rather than authored here - reef-railiance (bound_rapps lists 1 of 3 live rapps, and should be derived rather than hand-listed), railiance-master (rapp.schema.json plus a family declaration validator, grouped-rapp members field, wave-2 candidate refresh), the-custodian (canon promotion of the four-axis model, which also closes the open C-31 multi-segment prefix failures). T02 is held until the schema settles so the platform rapps and the schema do not converge on different answers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6.6 KiB
The S3 Platform-Service Rapp Pattern
Date: 2026-08-11
Owner: railiance-platform (S3)
Work record: RAILIANCE-WP-0015-T01
Purpose
This is the reference shape for packaging a platform service as a rapp-*
managed workload package. It generalizes the two boundaries S3 has already
drawn — docs/rapp-openbao-boundary.md and docs/rapp-postgres-boundary.md —
so the third and fourth platform rapps do not each re-derive the split from
scratch.
It is deliberately not a general rapp standard. The four-axis repo family
model belongs to railiance-master/docs/repository-axes.md, and the
rapp.yaml schema and canon promotion are in flight there and in
the-custodian. This document covers only the half S3 owns: what a
platform-service rapp looks like, and where the line falls between the package
and the platform.
The rule
A rapp owns how its workload is packaged, deployed, verified, rolled back and recovered. S3 retains everything that is true across workloads: policy, credential custody, lane approval, and the shared substrate the package assumes.
Both existing platform rapps already state this in their own words. From
rapp-openbao-boundary.md: the package may own how OpenBao is packaged,
deployed, verified and skinned, and must not become the home for platform-admin
policy, workload KV lane policy, delegated metadata authority, or credential
grants. From rapp-postgres-boundary.md: the package owns manifests, consumer
declarations, provisioning surface, isolation tests and recovery, while S3
retains the operator, storage policy, backup target and the credential-broker
grant catalog.
The ownership test
When an asset's home is unclear, ask in this order. The first answer that applies decides it.
- Would this asset survive replacing the workload with a different product? If yes, it is S3's. Credential lane approval survives swapping OpenBao for another secrets engine; an OpenBao Helm values file does not.
- Does any other workload depend on this asset? If yes, it is S3's. A
workload-kv-read-*policy is consumed by the workload it names but governed by a lane model shared by all of them. - Does it encode who may approve, not how to apply? Approval authority is
S3's; application mechanics are the package's. This is the line that keeps a
rapp from becoming a shadow S3 repo — the failure mode named explicitly in
railiance-master/docs/rapp-first-wave-candidates.md. - Otherwise it is the package's. Charts, values, overlays, deploy and verify scripts, workload-specific smoke and recovery procedure, and the Makefile targets that drive them.
Applied
| Asset class | Home | Test |
|---|---|---|
| Helm values, chart pins, overlays, ingress/middleware manifests | rapp | 4 |
| Deploy / dry-run / status / verify / rollback commands | rapp | 4 |
| Workload-specific smoke and recovery procedure | rapp | 4 |
| Consumer declarations and provisioning surface | rapp | 4 |
| Operator and read-only policy surface | S3 | 1 |
| Credential lane approval, CCRs, grant catalog | S3 | 3 |
| Cross-workload secret delivery (ESO, KV lanes) | S3 | 2 |
| Backup target procurement and its credentials | S3 | 1 |
| Storage class, monitoring substrate, cluster access | S3 | 1 |
Reference rapp.yaml for a platform service
Pending the normative schema from railiance-master, a platform-service rapp
should carry at least the following. Fields marked † are the consistency fields
currently present in rail.yaml and rapp-qonto but missing from both platform
rapps; RAILIANCE-WP-0015-T02 adds them once the schema settles.
kind: managed-workload-package
repo_family: rapp
rapp_id: rapp-<workload>
repo: rapp-<workload>
ownership_repo: railiance-platform # the S3 home retaining governance
contract_version: 1.0.0 # †
readiness_state: verified # †
data_classification: <internal|restricted> # †
criticality: <high|critical> # †
workload_identity:
name: <workload> # the workload, never the repo name
package_type: helm-managed-platform-service
chart: <repo/chart>
chart_version: <pinned>
app_version: <pinned>
primary_rail: rail-kubernetes
supported_rails: [rail-kubernetes]
runtime_dependencies: [...] # what must exist for this to run
rollout_contract: {...}
smoke_contract: {...}
rollback_contract: {...}
source_documents: [...]
Two conventions worth stating because they have already drifted:
workload_identity.nameis the workload, not the repo.openbao, notrapp-openbao.ownership_repois the repo that retains governance after extraction — for platform services that israiliance-platform, and it is not the same thing as the repo the package was carved out of.
Grouped rapps
Operator decision of 2026-08-11: rapp granularity is grouped by bounded context — one rapp per cohesive group of services that deploys, versions and rolls back together, rather than one rapp per deployable.
For a grouped rapp:
- members must be declared explicitly; an undeclared bundle is not a rapp, it is a drawer
- grouping is legitimate only where members share rollout and rollback fate. If one member can be rolled back without the others, it is a separate rapp
- the group's smoke contract must cover the group, not just its largest member
rapp-postgres is already a grouped package in this sense — it owns the CNPG
cluster manifests plus per-consumer declarations, and declares its consumers
explicitly in consumers:. That is the shape to copy.
Credential lanes
A platform-service rapp never owns credential custody. It declares what it
needs; S3 vends it through the existing broker. The binding between a rapp's
runtime_dependencies / secret_references and the S3 grant catalog and CCR
lanes is specified in RAILIANCE-WP-0015-T06 — until that lands, follow
docs/credential-broker.md and docs/credential-change-approval.md directly
and do not create a package-local lane.
The existing rule holds without exception: the package never commits credentials, and a workload receives a short-lived lease through the platform broker rather than a package-managed secret.
When a platform service earns a rapp
Not every S3 service needs one. A platform service is ready for extraction when
it has a stable workload identity, a package surface already visible in Git, an
explicit dependency and secret story, and described deploy/verify/recover
behavior — the criteria from rapp-first-wave-candidates.md. A service that
fails these is not blocked from being operated; it simply stays owned by S3
until its packaging identity stops moving.