--- id: RPF-WP-0009 type: workplan title: "Platform rapp consistency and deployment-management contract" domain: financials repo: railiance-platform status: finished owner: codex topic_slug: railiance created: "2026-08-11" updated: "2026-08-13" related_repos: - railiance-master - the-custodian - reef-railiance - rapp-openbao - rapp-postgres state_hub_workstream_id: "cd85bf28-7050-59a7-9af2-a7455b3df0fb" --- # RPF-WP-0009 - Platform rapp consistency and deployment-management contract ## Goal Bring the platform-owned managed workload packages (`rapp-openbao`, `rapp-postgres`) onto one consistent, verifiable declaration and operations contract, and establish the S3 pattern that later platform-service rapps are cut from. This workplan does **not** own the four-axis repo family model. That model lives in `railiance-master/docs/repository-axes.md` and is being promoted to custodian canon separately. S3's job here is to be the first family member that actually conforms, and to supply the platform-service reference shape. ## Findings that motivate this workplan Surveyed 2026-08-11 against the live cluster and all six family repos. The concepts are in better shape than their enforcement. The four families are cleanly defined and `RMASTER-WP-0018`/`RMASTER-WP-0019` in `railiance-master` are both `finished`. Nothing, however, validates a declaration file, so the model has been drifting quietly since the first wave. 1. **The rapp population diverged from the plan of record.** Planned wave 1 was `rapp-openbao` → `rapp-vergabe-teilnahme` → `rapp-forgejo`. Actual is `rapp-openbao`, plus `rapp-postgres` and `rapp-qonto` created ad hoc from immediate need. `rapp-vergabe-teilnahme` was never built, so the *user-facing* wrapper pattern it existed to prove is still unproven while the platform-service pattern has been proven twice. 2. **`rapp.yaml` has no schema and has drifted three ways.** The bootstrap contract names eight required fields but no shapes. Result: the rollout/smoke/rollback contracts are mutually unreadable (`supported_commands` lists in openbao and postgres versus `default_mode`/`production_gate`, `required:` and `order:` in qonto); only qonto carries `contract_version`, `readiness_state`, `data_classification` and `criticality`; only postgres has `consumer_contract`; `workload_identity.name` is the workload name twice and the repo name once. `rail.yaml` by contrast is consistent — the rail family got a tighter contract than the rapp family. 3. **The reef binding registry is stale.** `reef-railiance` declares `bound_rapps: [rapp-qonto]` while `rapp-openbao` and `rapp-postgres` both run on Railiance01. The reef's stated purpose is recording what is bound where, and it missed two of three. 4. **The model is not canon.** Custodian canon has no reef/rail/rapp standard, and `repo-classification-standard_v1.0` does not admit the prefixes — the same root cause as the open C-31 failures on `RAPP-*-WP-` work-record ids. 5. **Scale gap.** ~17 live non-system workloads, 3 rapps. ## Decisions taken (2026-08-11, operator-approved) - **Granularity: grouped by bounded context.** One rapp per cohesive service group that deploys, versions and rolls back together, not one per deployable. A grouped rapp must declare its member services explicitly — grouping is only legitimate where the members share a rollout and rollback fate. - **Scope split.** S3 owns its own rapps and the platform-service pattern. Schema, validator and family-model changes are routed to `railiance-master`; canon promotion is routed to `the-custodian`. S3 does not author them. - **Canon: promote now**, so `fix-consistency` can validate declarations. - **Wave-1 gap: build `rapp-vergabe-teilnahme`** to prove the user-facing shape before further extraction. That build is `railiance-apps`/master territory, not S3's; this workplan only supplies the platform-service half of the pattern. ## Boundaries This workplan may: - change declarations and operations contracts of platform-owned rapps (`rapp-openbao`, `rapp-postgres` — both declare `ownership_repo: railiance-platform`) - define the S3 platform-service rapp reference pattern - define how credential lanes and grant catalog entries bind to a rapp - send proposals to the repos that own the model, schema and canon It must not: - author the four-axis model, the `rapp.yaml` schema, or canon standards here - edit `reef-railiance`, `rail-*`, or `railiance-master` declarations directly - absorb platform-wide policy into a rapp (the "shadow S3 repo" failure mode named in `rapp-first-wave-candidates.md`) - change any credential gate, grant delivery, or revocation semantic ## Tasks ```task id: RPF-WP-0009-T01 status: done priority: high state_hub_task_id: "512adfef-a561-5b60-b4f3-f60e67696ef9" ``` Publish the S3 platform-service rapp pattern as `docs/rapp-platform-service-pattern.md`: the reference `rapp.yaml` shape for a platform service, the ownership split between rapp and S3, the grouped-rapp member declaration rule, and the credential-lane binding. Generalize from the existing `docs/rapp-openbao-boundary.md` and `docs/rapp-postgres-boundary.md` rather than inventing a third boundary vocabulary. ```task id: RPF-WP-0009-T02 status: done priority: high state_hub_task_id: "29b55ae5-74e2-5110-892c-ebb2b0cbf550" ``` Bring `rapp-openbao/declarations/rapp.yaml` and `rapp-postgres/declarations/rapp.yaml` into a single consistent shape: add `contract_version`, `readiness_state`, `data_classification` and `criticality` (present in `rail.yaml` and in `rapp-qonto`, absent in both platform rapps), fix `workload_identity.name` to be the workload rather than the repo where it drifts, and align the rollout/smoke/rollback contract shape. Coordinate the target shape with the `railiance-master` schema proposal (T04) before editing, so the two do not converge on different answers. ```task id: RPF-WP-0009-T03 status: done priority: medium state_hub_task_id: "53cb32e3-5cdc-5664-b7be-c46828849c56" ``` Correct the reef binding registry: propose to `reef-railiance` that `bound_rapps` include `rapp-openbao` and `rapp-postgres`, with the evidence that both are live on Railiance01. Proposal only — `reef-railiance` owns its declaration. Ask that the binding be derived or checked rather than hand-listed, since a hand-listed registry is what went stale. ```task id: RPF-WP-0009-T04 status: done priority: high state_hub_task_id: "b157de50-ff34-57aa-800e-d2d79e65a731" ``` Route the schema and validator proposal to `railiance-master`: a `rapp.schema.json` covering the eight required fields plus the four consistency fields, a validator that runs over every family declaration, the grouped-rapp member field, and a refresh of `rapp-first-wave-candidates.md` to reflect what was actually built and the grouped-context wave-2 inventory. ```task id: RPF-WP-0009-T05 status: done priority: medium state_hub_task_id: "57ed22c9-94b9-526d-beb0-0a090eeaac3a" ``` Route the canon promotion proposal to `the-custodian`: a `railiance-repo-families` standard, declaration schemas under `canon/standards/schemas/`, and admission of the `rail-`/`rapp-`/`reef-` prefixes into `repo-classification-standard` and `work-record-types.yaml` — the latter closing the open C-31 failures on multi-segment `RAPP-*-WP-` ids. ```task id: RPF-WP-0009-T06 status: done priority: medium state_hub_task_id: "4423fa37-7e0d-5e2d-ac39-f6b505c60243" ``` Define how a rapp binds to its credential lane: the relationship between `credential-grants/catalog.yaml` entries, CCR lanes, and a rapp's `runtime_dependencies`/`secret_references`, so that a new rapp has one documented way to acquire runtime secrets instead of re-deriving it per package. Must not alter existing gate, delivery or revocation semantics — this documents and binds the existing lane, it does not build a parallel one. ## Outcome Session of 2026-08-11: T01 published the S3 platform-service pattern; T03, T04 and T05 routed their proposals to the owning repos (reef-railiance f0f44ce0, railiance-master 04c776c4, the-custodian cc64764a). T02 was held until railiance-master settled the schema. Session of 2026-08-13: T02 done. `rapp-openbao` and `rapp-postgres` `declarations/rapp.yaml` now pass `schemas/rapp.schema.json` (contract metadata, `composition`, `bound_reefs: [reef-railiance]`, `commands`, `smoke_contract.required`, `rollback_contract.order`). Live inventory for the T06 coverage check is `docs/evidence/reef-railiance-deployables.json`, refreshable with `scripts/capture-reef-deployables.py`. Family-validator remainder is outside this task: `rapp-qonto` still on the pre-schema shape, three undeclared `rapp-*` engine repos, and `reef-railiance` `bound_rapps` still hand-listed. Session of 2026-08-13 (later): T06 done. Binding recipe is `docs/rapp-credential-lane-binding.md`. Standing secrets bind through a CCR `target.rapp` + `secret_references`; short-lived leases bind through grant `rapp_id` (required when the grant id is `rapp-*/…`). Stamped `rapp-postgres` catalog grants and `CCR-2026-0009`. Gate, delivery, and revocation semantics are unchanged. Workplan finished.