railiance-platform/docs/rapp-platform-service-pattern.md
codex b17a9f8bff
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Has been cancelled
Publish S3 platform-service rapp pattern; route family proposals
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>
2026-08-11 11:11:57 +02:00

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.

  1. 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.
  2. 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.
  3. 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.
  4. 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.name is the workload, not the repo. openbao, not rapp-openbao.
  • ownership_repo is the repo that retains governance after extraction — for platform services that is railiance-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.