# 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. ```yaml kind: managed-workload-package repo_family: rapp rapp_id: rapp- repo: rapp- ownership_repo: railiance-platform # the S3 home retaining governance contract_version: 1.0.0 # † readiness_state: verified # † data_classification: # † criticality: # † workload_identity: name: # the workload, never the repo name package_type: helm-managed-platform-service chart: chart_version: app_version: 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.