docs: complete canon alignment housekeeping

This commit is contained in:
codex 2026-08-18 18:12:32 +02:00
parent 490c2d97b1
commit debbc13b40
14 changed files with 397 additions and 81 deletions

View file

@ -0,0 +1,36 @@
---
id: ADHOC-2026-07-30
type: workplan
title: "Ad-hoc Railiance framework maintenance on 2026-07-30"
domain: financials
repo: railiance-master
status: archived
owner: codex
topic_slug: railiance
created: "2026-07-30"
updated: "2026-08-18"
state_hub_workstream_id: "1cc4d75f-9bd8-4843-8b18-5ce9637edd60"
---
# Ad-hoc maintenance - 2026-07-30
## Restore Forgejo repository descriptions
```task
id: ADHOC-2026-07-30-T01
status: done
priority: medium
state_hub_task_id: "9fad2db0-f596-4548-b1b9-f81bd1736014"
```
Compared both public pages of the retired Gitea `coulomb` organization with
both public pages of Forgejo. Gitea retained nonempty descriptions for 79
migrated repositories; Forgejo had lost or replaced all 79. Restored the
normalized Gitea descriptions through the existing activity-core
ExternalSecret-backed Forgejo administrative boundary. Repositories created
only in Forgejo were initially left untouched; the four that remained blank
(`fin-hub`, `forgejo-actions-probe`, `railiance-bootstrap`, and
`railiance-hosts`) then received descriptions derived from their local
scope/intent metadata or explicit probe purpose. All 100 Forgejo repositories
now have nonempty descriptions. No credential value left the cluster or
entered recorded output.

View file

@ -0,0 +1,310 @@
---
id: RMASTER-WP-0017
type: workplan
title: "Rail, Rapp, and Reef Repo Separation"
domain: financials
repo: railiance-master
status: archived
owner: codex
topic_slug: railiance
planning_priority: high
planning_order: 17
created: "2026-07-25"
updated: "2026-08-18"
related_repos:
- railiance-master
- railiance-cluster
- railiance-platform
- railiance-apps
- railiance-forge
- railiance-fabric
- railiance-infra
- railiance-hosts
state_hub_workstream_id: "dd794e38-6da0-4acd-8ad8-e00bd2aa62bc"
---
# RMASTER-WP-0017 - Rail, Rapp, and Reef Repo Separation
## Goal
Turn the newly defined `rail-*`, `rapp-*`, and `reef-*` concepts into an
executable migration plan for the current Railiance repo family, so the
framework can evolve without leaving workload execution semantics, workload
packaging, and substrate realities mixed into the existing ownership repos.
This workplan is explicitly cross-repo. It belongs in `railiance-master`
because no single implementation repo can own the migration end to end.
## Current Starting Point
The framework baseline is now recorded in `railiance-master`:
- `docs/repository-axes.md`
- `docs/reef-substrate-model.md`
- `docs/adr/ADR-0001-repository-prefix-architecture.md`
The repo is also now prepared and registered with State Hub as the architecture
home for future multi-repo Railiance work.
The architectural direction is now further clarified:
- wave 1 is centered on `rail-kubernetes` as the default base rail
- new rails are introduced only for concrete workload needs with a sound
runtime argument
- `rail-knative` is the first expected follow-on rail, driven by the need to
support `qonto-assistent`, but only after the Kubernetes rail boundary is
clean
- `rapp-*` repos are about managed wrapping and scaffolding, not ownership
- `reef-*` repos are about compute resources organized for a defined purpose
- mixed-rail reefs are acceptable early, but production-grade cases should
prefer clearer substrate and primary-rail separation unless deliberately
justified
The remaining problem is implementation reality:
- `railiance-cluster` still owns Kubernetes-specific workload lifecycle and
overlay semantics that should eventually become `rail-kubernetes`
- first-class workload wrappers are still spread across `railiance-apps`,
`railiance-platform`, and `railiance-forge`
- substrate reality is under-described and split between `railiance-infra` and
`railiance-hosts`
- `railiance-fabric` and State Hub do not yet model rails, `rapp`s, or reefs
as first-class entities
## Target Outcome
When this workplan is complete:
1. Railiance has a clear first migration target for `rail-kubernetes`.
2. The first set of `rapp-*` candidates is chosen and ordered.
3. The first `reef-*` rollout rule is chosen for current named substrates.
4. `railiance-fabric` and State Hub have a defined integration path for the new
repo families.
5. The current repo set has an approved separation plan, not only a naming idea.
## Boundaries
This workplan may define, sequence, and coordinate repo splits.
It must not silently move implementation content between repos without explicit
repo-local follow-up workplans or commits in those repos.
## Tasks
## T01 - Record the framework repo-family baseline in railiance-master
```task
id: RMASTER-WP-0017-T01
status: done
priority: high
state_hub_task_id: "7cb6769a-5557-46e5-8e20-c492e5049604"
```
Record the first canonical architecture baseline for:
- `railiance-*` as ownership repos
- `rail-*` as execution-contract repos
- `rapp-*` as managed workload package repos
- `reef-*` as substrate-boundary repos
Acceptance:
- `railiance-master` contains the baseline documents and ADR
- the `reef-*` model is explicitly defined and bounded against repo explosion
## T02 - Prepare railiance-master as the cross-repo workplan home
```task
id: RMASTER-WP-0017-T02
status: done
priority: high
state_hub_task_id: "17be5faa-cba2-4838-97af-dc3b79e0511b"
```
Prepare and register `railiance-master` in State Hub so cross-repo architecture
work can live here as first-class workplans.
Acceptance:
- repo is classification-registered
- repo passes `statehub fix-consistency`
- workplan and architecture files can now be indexed from this repo
## T03 - Define the `rail-kubernetes` extraction boundary
```task
id: RMASTER-WP-0017-T03
status: done
priority: high
state_hub_task_id: "e25ea023-e2fe-44b9-96c1-cca19c65053f"
```
Define what moves from `railiance-cluster` into a future `rail-kubernetes`
repo, and what must remain owned by `railiance-cluster`.
This is the wave-1 architecture task. It must treat `rail-kubernetes` as the
default base rail for the current ecosystem rather than as one option among
many equally urgent rails.
At minimum, decide the boundary for:
- `railiance/app.toml`
- promotion lifecycle semantics
- overlay repo pattern
- stage-1/2/3 tooling
- cluster-runtime prerequisites versus workload-runtime semantics
- the compatibility path future rails such as `rail-knative` will rely on
Acceptance:
- one written boundary contract names what `railiance-cluster` keeps
- one written boundary contract names what `rail-kubernetes` must own
- at least one migration-safe path exists that does not break current users
- the boundary leaves a coherent follow-on path for `rail-knative` without
forcing knative design decisions into wave 1
2026-07-25: Added `docs/rail-kubernetes-boundary.md` and
`docs/adr/ADR-0002-rail-kubernetes-wave-1-boundary.md`. The boundary now names
the retained S2 substrate scope, the extracted Kubernetes rail contract, the
rehome-required workload-specific helpers, and a compatibility-preserving split
sequence.
## T04 - Identify and sequence the first `rapp-*` candidates
```task
id: RMASTER-WP-0017-T04
status: done
priority: high
state_hub_task_id: "674b1a4f-2c5b-499c-b26f-9ed4f9f0861c"
```
Choose the first workload packages that should become first-class `rapp-*`
repos instead of remaining embedded inside ownership repos.
This task must treat `rapp-*` as managed workload packaging for third-party or
self-built workloads in the Railiance and Net Kingdom context. It must not
turn `rapp-*` into an ownership mirror of existing `railiance-*` repos.
Candidate set to assess:
- Forgejo or Gitea from `railiance-forge`
- OpenBao from `railiance-platform`
- one user-facing S5 workload from `railiance-apps`
Acceptance:
- first-wave `rapp-*` candidates are named
- rationale is recorded for each chosen or deferred candidate
- split order is defined so ownership boundaries do not get weaker during migration
- each chosen candidate is justified as a managed wrapper and not as a new
ownership home
2026-07-25: Added `docs/rapp-first-wave-candidates.md` and
`docs/adr/ADR-0003-rapp-first-wave-selection.md`. The first-wave order is now
`rapp-openbao`, then `rapp-vergabe-teilnahme`, with the forge workload
explicitly deferred until the Gitea-versus-Forgejo package target is stable
enough to avoid immediate wrapper churn.
## T05 - Decide the first `reef-*` rollout for current substrates
```task
id: RMASTER-WP-0017-T05
status: done
priority: medium
state_hub_task_id: "53c63266-fbcb-4644-86f4-042e7b80744e"
```
Apply the new reef model to the current Railiance substrate reality and decide
whether the first rollout should be:
- `reef-coulombcore`
- `reef-railiance`
- `reef-workstation`
- or a grouped substrate such as `reef-ops-workstations`
This task must also resolve the conceptual overlap between `railiance-infra`
and `railiance-hosts`.
It should treat reefs as compute resources organized for a defined purpose and
should explicitly decide how much multi-rail mixing is acceptable in early
substrates such as `RAILIANCE01`.
Acceptance:
- first-wave reef repo set is chosen
- the rule for singleton versus grouped reef repos is applied to current hosts
- the `railiance-hosts` versus `railiance-infra` ambiguity has a documented direction
- any provisional terminology for transitional substrates is kept provisional
unless the pattern is stable enough to canonize
2026-07-25: Added `docs/reef-first-wave-rollout.md` and
`docs/adr/ADR-0004-first-wave-reef-rollout.md`. The first rollout is now
`reef-railiance`, `reef-coulombcore`, and `reef-ops-workstations`, with
`railiance-infra` chosen as the canonical S1 ownership repo and transitional
substrate nicknames intentionally left provisional.
## T06 - Define `railiance-fabric` and State Hub adaptation for the new repo families
```task
id: RMASTER-WP-0017-T06
status: done
priority: medium
state_hub_task_id: "4f47d000-6495-4163-8673-f339dd7ef41f"
```
Define the minimum graph and coordination changes needed so rails, `rapp`s, and
reefs are not only naming conventions but visible ecosystem entities.
Acceptance:
- `railiance-fabric` adaptation requirements are listed
- State Hub registration/indexing implications are listed
- the minimum fields or declarations needed in future `rail-*`, `rapp-*`, and
`reef-*` repos are recorded
2026-07-25: Added `docs/fabric-state-hub-adaptation.md`. The minimum
adaptation requirements now cover repo-family metadata, Fabric onboarding
updates, typed service/relation vocabulary, and the smallest repo-local
declaration surface future `rail-*`, `rapp-*`, and `reef-*` repos must expose.
## T07 - Launch the first migration wave into concrete repo-local follow-up work
```task
id: RMASTER-WP-0017-T07
status: done
priority: high
state_hub_task_id: "e762c05a-c6f1-439e-98e2-bf4d7b312a74"
```
Create the concrete follow-up workplans in the affected implementation repos for
the first migration wave.
Acceptance:
- at least one repo-local workplan exists for `rail-kubernetes`
- at least one repo-local workplan exists for a first `rapp-*` extraction
- at least one repo-local workplan exists for a first `reef-*` rollout or substrate cleanup
- this master workplan can then govern sequencing rather than holding all implementation detail itself
2026-07-25: Added the first repo-local follow-up workplans:
- `railiance-cluster/workplans/RAIL-BS-WP-0012-rail-kubernetes-extraction.md`
- `railiance-platform/workplans/RAILIANCE-WP-0012-rapp-openbao-extraction-boundary.md`
- `railiance-infra/workplans/RAIL-HO-WP-0007-first-reef-rollout-and-s1-canonicalization.md`
## Exit Criteria
- [x] The architecture baseline is recorded in `railiance-master`
- [x] `railiance-master` is registered and synced with State Hub
- [x] `rail-kubernetes` boundary is written and approved
- [x] first `rapp-*` candidates and split order are written and approved
- [x] first `reef-*` rollout decision is written and approved
- [x] Fabric and State Hub adaptation requirements are written
- [x] first migration-wave follow-up workplans exist in the affected repos
## Notes
This workplan is intended to stay small at the framework level. Once a split or
migration has a concrete owner, the implementation detail should move into the
relevant repo-local workplan rather than expanding this file into a second
control plane.

View file

@ -0,0 +1,309 @@
---
id: RMASTER-WP-0018
type: workplan
title: "First-Wave Repo Family Materialization"
domain: financials
repo: railiance-master
status: archived
owner: codex
topic_slug: railiance
planning_priority: high
planning_order: 18
created: "2026-07-25"
updated: "2026-08-18"
related_repos:
- railiance-master
- railiance-cluster
- railiance-platform
- railiance-infra
- railiance-fabric
state_hub_workstream_id: "054a494c-e430-4164-b2c3-e32e759cc3e0"
---
# RMASTER-WP-0018 - First-Wave Repo Family Materialization
## Goal
Turn the completed separation architecture into the first concrete
`rail-*`, `rapp-*`, and `reef-*` repo materialization wave.
This workplan governs the first implementation wave that makes the new repo
families real rather than only defined. It stays in `railiance-master` because
the sequencing still spans multiple ownership repos and Fabric integration.
## Why This Exists
`RMASTER-WP-0017` completed the architecture baseline and launched the first
repo-local follow-up workplans.
What is still missing is the shared control plane for:
- creating the first concrete repo-family baselines
- keeping their boundaries consistent across repos
- aligning repo-local declarations with Fabric and State Hub ingestion
- preventing the first repo creations from drifting into ad hoc local patterns
## Current Starting Point
The architecture baseline already exists in `railiance-master`:
- `docs/repository-axes.md`
- `docs/rail-kubernetes-boundary.md`
- `docs/rapp-first-wave-candidates.md`
- `docs/reef-first-wave-rollout.md`
- `docs/fabric-state-hub-adaptation.md`
The first implementation follow-up work also already exists:
- `railiance-cluster/workplans/RAIL-BS-WP-0012-rail-kubernetes-extraction.md`
- `railiance-platform/workplans/RAILIANCE-WP-0012-rapp-openbao-extraction-boundary.md`
- `railiance-infra/workplans/RAIL-HO-WP-0007-first-reef-rollout-and-s1-canonicalization.md`
- `railiance-fabric/workplans/RAIL-FAB-WP-0025-rail-rapp-reef-fabric-adaptation.md`
Fabric now has first repo-family-aware repository metadata, but the ecosystem
still lacks:
- the first actual `rail-*` repo
- the first actual `rapp-*` repo
- the first actual `reef-*` repo
- relation projection for rail/rapp/reef topology
## Target Outcome
When this workplan is complete:
1. Railiance has a repeatable bootstrap contract for first-wave `rail-*`,
`rapp-*`, and `reef-*` repos.
2. The first concrete `rail-kubernetes` repo exists and is registered.
3. The first concrete `rapp-openbao` repo exists and is registered.
4. The first concrete `reef-railiance` repo exists and is registered.
5. Fabric can project the minimum rail/rapp/reef relation topology needed to
answer placement questions.
## Boundaries
This workplan may coordinate repo creation, repo-family bootstrap, and graph
integration across Railiance repos.
It must not weaken the existing ownership boundaries:
- `railiance-cluster` remains the S2 ownership home
- `railiance-platform` remains the S3 ownership home for OpenBao
- `railiance-infra` remains the canonical S1 ownership home for first-wave reefs
- `railiance-fabric` remains the implementation home for graph ingestion and
projection
## Tasks
## T01 - Publish the first-wave repo bootstrap contract
```task
id: RMASTER-WP-0018-T01
status: done
priority: high
state_hub_task_id: "d6ba9286-222d-49d2-83e9-fd251c534dde"
```
Record the minimum bootstrap contract future `rail-*`, `rapp-*`, and
`reef-*` repos should follow so the first materialization wave does not invent
three incompatible patterns.
Acceptance:
- one framework document defines the minimum file baseline
- one framework document defines the first declaration path per repo family
- the metadata projection rule into Fabric and State Hub is named
2026-07-25: Added `docs/repo-family-bootstrap-contract.md`.
## T02 - Launch the first `rail-kubernetes` repo materialization
```task
id: RMASTER-WP-0018-T02
status: done
priority: high
state_hub_task_id: "726a0c5c-2fa5-4bb9-a730-3200abc40ac8"
```
Create the concrete repo bootstrap and extraction path for `rail-kubernetes`
using the boundary already defined in `railiance-master` and the repo-local
follow-up work already opened in `railiance-cluster`.
Acceptance:
- the new repo has the required baseline files
- the repo declares the Kubernetes rail contract in source-controlled form
- the compatibility path from `railiance-cluster` is preserved
- the repo is ready for Fabric and State Hub registration
2026-07-25: Bootstrapped `/home/worsch/rail-kubernetes` with the baseline
repo files, `declarations/rail.yaml`, repo-local docs, and
`RAIL-K8S-WP-0001`. The repo is now registered in State Hub and seeded into
`railiance-fabric/registry/railiance-repos.yaml`; live Fabric registry sync and
had initially not yet been pushed to a remote.
2026-07-25: Reconciled `rail-kubernetes` with the server-side bootstrap commit
on `forgejo-remote:coulomb/rail-kubernetes.git` and pushed `main`. The
remaining registration gap is now only live Fabric registry sync.
2026-07-25: Attempted targeted live Fabric registry sync for `rail-kubernetes`,
but the registry endpoint at `http://127.0.0.1:8765` refused the connection.
The repo is now remote-backed and source-registered, while live registry
ingestion remains blocked on the local Fabric registry service being up.
2026-07-25: Started the local Fabric registry service and completed the
targeted live sync for `rail-kubernetes`. The repo is now State Hub
registered, source-registered in the Fabric onboarding manifest, remote-backed
in Forgejo, and live-ingested into the Fabric registry. Remaining wave-1
`rail-kubernetes` work is now about helper scripts and command
implementations rather than registration.
2026-07-25: Imported the first core generic contract artifacts into
`rail-kubernetes`: the canonical deployment lifecycle doc, the canonical
`railiance/app.toml` contract doc, the machine-readable schema, and the example
`railiance/app.toml`. Remaining helper-command and supporting-doc imports stay
repo-local follow-up work.
2026-07-25: Imported the remaining reviewed generic supporting docs for the
Kubernetes rail, including the overlay pattern, canary chart pattern, Stage 2
deploy/observe contract, promotion/rollback guide, and Stage 1 run-command
contract. Remaining wave-1 import debt is now mainly helper scripts and command
implementations rather than core contract documentation.
2026-07-25: Closed T02 after confirming the first-class repo baseline, source
declaration, compatibility path, remote registration, State Hub registration,
and live Fabric ingestion are all in place for `rail-kubernetes`.
## T03 - Launch the first `rapp-openbao` repo materialization
```task
id: RMASTER-WP-0018-T03
status: done
priority: high
state_hub_task_id: "3e46e1bd-c730-4e41-a00a-6aa7a4645023"
```
Create the concrete repo bootstrap and workload-package declaration for
`rapp-openbao` without letting the package repo absorb platform ownership.
Acceptance:
- the new repo has the required baseline files
- the repo declares OpenBao as a managed workload package
- the repo declares supported rails and rollout/smoke/rollback contract
- the remaining ownership boundary with `railiance-platform` stays explicit
2026-07-25: Confirmed that `/home/worsch/rapp-openbao` does not yet exist.
Bootstrap will start from the boundary and first move set already recorded in
`railiance-platform/docs/rapp-openbao-boundary.md`.
2026-07-25: Bootstrapped `/home/worsch/rapp-openbao` with the first-wave repo
baseline, `declarations/rapp.yaml`, a package-local Makefile, repo-local docs,
the first imported OpenBao package assets from `railiance-platform`, and
`RAPP-OPENBAO-WP-0001`. The repo is now registered in State Hub; Fabric
registry onboarding remains open as repo-local follow-up work.
## T04 - Launch the first `reef-railiance` repo materialization
```task
id: RMASTER-WP-0018-T04
status: done
priority: medium
state_hub_task_id: "f6f2ea73-0ff3-4cfa-b7a7-7b71db3afb4a"
```
Create the concrete repo bootstrap and substrate declaration for
`reef-railiance` as the first grouped home reef repo.
Acceptance:
- the new repo has the required baseline files
- the repo declares substrate kind, lifecycle, criticality, hosted rails, and
primary rail
- the repo stays compatible with the canonical S1 ownership role of
`railiance-infra`
2026-07-26: Corrected the first-wave home reef target from singleton
`reef-railiance01` to grouped `reef-railiance`, updated the framework and S1
source docs accordingly, and bootstrapped `/home/worsch/reef-railiance` with
the baseline repo files, grouped reef declaration, substrate/topology files,
repo-local docs, and `REEF-RAILIANCE-WP-0001`. The repo is now remote-backed in
Forgejo, registered in State Hub, added to
`railiance-fabric/registry/railiance-repos.yaml`, and live-ingested into the
Fabric registry as `repo_family: reef` with `primary_rail: rail-kubernetes`
and `substrate_kind: server-group`.
## T05 - Add relation projection for rails, `rapp`s, and reefs in Fabric
```task
id: RMASTER-WP-0018-T05
status: done
priority: medium
state_hub_task_id: "ef999db5-e75e-467a-a72a-d45fda2b6fa7"
```
Extend Fabric beyond repo-family-aware metadata so it can project the minimum
rail/rapp/reef relation topology.
Acceptance:
- Fabric can project `supports_rail`
- Fabric can project `hosts_rail`
- Fabric can project `binds_rapp`
- Fabric can project `governed_by`
- the projection path is grounded in repo-local declarations rather than
registry-only guesses
2026-07-26: Completed the Fabric relation-projection slice in
`railiance-fabric`. Repository records now retain local checkout paths, the
registry combined graph reopens repo-local `rail`, `rapp`, and `reef` files to
project `governed_by`, `supports_rail`, `hosts_rail`, and `binds_rapp`, and
the graph explorer preserves the declaration evidence on projected repository
nodes. Focused Fabric tests passed after the projection update.
## T06 - Verify first-wave registration and coordination end to end
```task
id: RMASTER-WP-0018-T06
status: done
priority: medium
state_hub_task_id: "5834f122-b9e9-41c1-93e9-d5525cd720bf"
```
Verify that the first concrete repos participate cleanly in:
- State Hub registration and indexing
- Fabric registration and graph projection
- workplan tracking across ownership repo and repo-family repo boundaries
Acceptance:
- the first concrete repos are visible to State Hub
- the first concrete repos are visible to Fabric
- cross-repo ownership and runtime relations are queryable
- the wave can continue to second candidates without redefining the bootstrap model
2026-07-26: Verified the first-wave coordination path end to end. The live
Fabric registry at `http://127.0.0.1:8765` now stores the local paths and
repo-family metadata for `rail-kubernetes`, `rapp-openbao`, and
`reef-railiance`, and its graph-explorer export emits live `governed_by`,
`supports_rail`, and `hosts_rail` edges from repo-local declarations. State
Hub consistency sync remains clean for the framework and repo-local workplans,
so the first wave can continue without reopening the bootstrap model.
## Exit Criteria
- [x] The first-wave bootstrap contract is published
- [x] `rail-kubernetes` exists as a first-class repo baseline
- [x] `rapp-openbao` exists as a first-class repo baseline
- [x] `reef-railiance` exists as a first-class repo baseline
- [x] Fabric projects the minimum rail/rapp/reef relation topology
- [x] State Hub and Fabric can coordinate the first concrete repo-family wave end to end
## Notes
This workplan should stay focused on the first concrete repo-family wave.
Second-wave candidates such as `rapp-vergabe-teilnahme`, `reef-coulombcore`,
or a future `rail-knative` should be launched only after the first wave proves
the bootstrap contract, registration path, and relation model.

View file

@ -0,0 +1,472 @@
---
id: RMASTER-WP-0019
type: workplan
title: "Knative Qonto Runtime on reef-railiance"
domain: financials
repo: railiance-master
status: archived
owner: codex
topic_slug: railiance
planning_priority: high
planning_order: 19
created: "2026-07-26"
updated: "2026-08-18"
related_repos:
- railiance-master
- qonto-assistant
- key-cape
- rail-kubernetes
- reef-railiance
- railiance-platform
- railiance-fabric
state_hub_workstream_id: "d2b082db-ecc3-4322-8be3-22aa00a2af5c"
---
# RMASTER-WP-0019 - Knative Qonto Runtime on reef-railiance
## Goal
Turn the Qonto internet-runtime direction into an explicit second-wave
Railiance repo-family plan: `reef-railiance` hosts a new `rail-knative`, and
that rail dynamically runs a new `rapp-qonto` package extracted from the
current `qonto-assistant` deployment path.
This workplan exists to replace the remaining "custom activator or maybe
Knative" ambiguity with a concrete cross-repo architecture path before new
runtime implementation starts.
The second-wave design also establishes a general rule for reliable and
cost-efficient operation: deterministic work should progress from attended
human operation, to agent-executable interaction, to idempotent functional
automation as its contract stabilizes. Human interaction remains appropriate
for irreducible policy acceptance and privileged authority, not for repeated
evidence collection or routine reconciliation.
## Why This Exists
`RMASTER-WP-0018` finished the first wave:
- `rail-kubernetes` exists as the default base rail
- `reef-railiance` exists as the grouped home reef for the Railiance servers
- `rapp-openbao` proved the first managed package split
- Fabric can now project `governed_by`, `supports_rail`, `hosts_rail`, and
`binds_rapp`
The next concrete runtime pressure comes from `QONTO-WP-0004`. That workload
needs internet reachability, bounded cold-start behavior, strong identity, and
strict secret custody. Those are exactly the conditions that justify a second
rail instead of extending the direct `rail-kubernetes` path indefinitely.
At the same time, `KEY-WP-0004` is provisioning the first tenant and workload
identity lane for Binky Hedgehog GmbH. That lane should target the future
runtime package and not harden a permanent direct deployment shape that the
framework now intends to replace.
## Current Starting Point
The current ecosystem state is:
- `reef-railiance` is the grouped reef for the Railiance server fleet
- `rail-kubernetes` owns the current baseline deployment and lifecycle contract
- `qonto-assistant` already contains the Qonto domain code, hardening work, and
an initial direct Kubernetes placement request
- `key-cape` has `KEY-WP-0004` open for the Binky tenant and qonto workload
identity lane
- no `rail-knative` repo exists yet
- no `rapp-qonto` repo exists yet
## Target Outcome
When this workplan is complete:
1. The boundary between `rail-kubernetes` and a new derived `rail-knative` is
written and approved.
2. The boundary between `qonto-assistant` and a future `rapp-qonto` is written
and approved.
3. `reef-railiance` is confirmed as the first host reef for this runtime path,
including the rail/reef binding assumptions.
4. Identity, secret, and routing lanes are aligned to the future
`rapp-qonto` runtime instead of a permanent direct `qonto-assistant`
deployment.
5. Repo-local follow-up workplans can launch `rail-knative` and `rapp-qonto`
without reopening the architecture question.
6. Reef admission, contract compatibility, credential routing, and operational
evidence have automation-ready contracts before production implementation.
## Boundaries
This workplan may define architecture, sequencing, and repo-boundary contracts
across Railiance, `qonto-assistant`, and `key-cape`.
It must not:
- bootstrap `rail-knative` yet
- bootstrap `rapp-qonto` yet
- move code or manifests between repos
- implement runtime changes in `reef-railiance`, `rail-kubernetes`,
`qonto-assistant`, or `key-cape`
Implementation starts only after the follow-up workplans opened from this plan
are reviewed and accepted.
## Tasks
## T01 - Define the `rail-knative` boundary against `rail-kubernetes`
```task
id: RMASTER-WP-0019-T01
status: done
priority: high
state_hub_task_id: "336cb298-9107-4519-ad64-5b23853c84fa"
```
Write the contract for what `rail-knative` must own versus what remains owned
by `rail-kubernetes`.
At minimum this boundary must decide ownership for:
- scale-to-zero and cold-start semantics
- service activation / ingress entry behavior
- Knative Serving primitives versus generic Railiance lifecycle semantics
- compatibility expectations for `reef-railiance`
- what stays reusable from the base Kubernetes rail instead of being forked
Acceptance:
- one written boundary contract exists
- the contract names explicit non-goals for `rail-knative`
- the split does not duplicate the generic lifecycle contract already owned by
`rail-kubernetes`
2026-07-26: Added `docs/rail-composition-contract.md` and
`docs/adr/ADR-0005-derived-rail-composition.md`. `rail-knative` is now a
derived rail with a versioned `rail-kubernetes` base contract. The contract
defines inheritance, overrides, non-goals, readiness states, and a rail-neutral
workload/binding model.
## T02 - Define the `rapp-qonto` extraction boundary from `qonto-assistant`
```task
id: RMASTER-WP-0019-T02
status: done
priority: high
state_hub_task_id: "54fcea7d-3c4e-444f-b184-f18f3c4b3a7c"
```
Write the contract for what moves into `rapp-qonto` and what stays in
`qonto-assistant`.
This task must keep `qonto-assistant` as the ownership home for the Qonto
domain logic while using `rapp-qonto` as the managed runtime/package boundary.
At minimum the contract must decide ownership for:
- deployment packaging and runtime manifests
- runtime configuration and secret references
- smoke, rollout, rollback, and promotion expectations
- how the current direct Kubernetes material becomes migration input rather
than a permanent target shape
Acceptance:
- one written boundary contract exists
- `qonto-assistant` remains the ownership repo in the contract
- `rapp-qonto` is justified as a managed package repo and not as a new
ownership mirror
2026-07-26: Added the ownership and request-flow boundary in
`docs/qonto-knative-runtime-contract.md`. `qonto-assistant` retains domain
logic, financial policy, authorization integration, and application audit
events. `rapp-qonto` owns packaging, rail bindings, runtime configuration,
secret references, and workload-specific rollout evidence.
## T03 - Define the `reef-railiance` hosting model for the qonto runtime
```task
id: RMASTER-WP-0019-T03
status: done
priority: high
state_hub_task_id: "b0233f06-3dee-4cac-bae4-d8add5ac29c8"
```
Confirm how `reef-railiance` hosts the first `rail-knative` runtime slice and
what that means for ingress, isolation, and criticality.
This task must decide:
- whether the qonto path lives entirely on `reef-railiance` in wave 2
- how `rail-knative` is declared as a hosted rail there
- whether a primary-rail versus mixed-rail statement needs revision
- what existing S1/S2 approvals or substrate notes must be updated before
runtime work starts
Acceptance:
- the hosting assumption is written
- the reef/rail relation update path is named
- unresolved substrate risks are listed instead of buried in repo-local work
2026-07-26: Added `docs/reef-production-readiness-contract.md` and
`docs/adr/ADR-0006-reef-production-admission.md`. `reef-railiance` may host
`rail-knative` during wave 2 while keeping `rail-kubernetes` primary, but
topology no longer implies readiness. Critical Qonto admission requires
capacity, ingress, identity, isolation, observability, recovery, and explicit
single-server/shared-control-plane risk evidence.
## T04 - Align identity and secret custody to the future runtime path
```task
id: RMASTER-WP-0019-T04
status: done
priority: high
state_hub_task_id: "0086eb68-a852-4fe7-834c-4a19dd763b77"
```
Align `KEY-WP-0004`, the OpenBao lane, and the ops-warden routing lane so the
credential path terminates at the future `rapp-qonto` runtime on
`rail-knative`, while still allowing a clearly temporary migration bridge from
the current `qonto-assistant` repo.
Acceptance:
- the future runtime principal and lane names are defined
- the transitional use of any current `qonto-assistant` runtime role is
explicitly marked temporary
- no workplan still assumes a permanent direct deployment target for the lane
2026-07-26: `docs/qonto-knative-runtime-contract.md` defines steady identifiers
for `rapp-qonto`, its service account, OpenBao role, and caller client while
making any `qonto-assistant` role a time-bounded bridge. Credential values must
use sanctioned custody and execution transports; only route and conformance
metadata may enter workplans, Git, chat, or State Hub.
## T05 - Define second-wave bootstrap and Fabric registration sequence
```task
id: RMASTER-WP-0019-T05
status: done
priority: medium
state_hub_task_id: "8905d447-dee9-460d-bbfe-3f0c8e42c7c6"
```
Define the repo-creation and registration sequence for `rail-knative` and
`rapp-qonto`, using the first-wave bootstrap contract already proven by
`RMASTER-WP-0018`.
Acceptance:
- the bootstrap order is named
- the minimum declaration files are named for both future repos
- the expected Fabric relations (`supports_rail`, `hosts_rail`, `binds_rapp`,
`governed_by`) are named before repo creation starts
Bootstrap order:
1. publish the common contract/schema additions in `rail-kubernetes`
2. add readiness and compatibility support in Fabric
3. open the `reef-railiance` admission work and collect baseline evidence
4. bootstrap `rail-knative` with a derived-rail declaration
5. bootstrap `rapp-qonto` with common and Knative binding declarations
6. register `governed_by`, `base_rail`, `supports_rail`, `hosts_rail`, and
`binds_rapp` relations
7. promote relations through `declared`, `installed`, `verified`, and
`production-approved` only as evidence lands
The minimum declaration files remain `declarations/rail.yaml`,
`declarations/rapp.yaml`, and `declarations/reef.yaml`. Rail-neutral workload
and rail-binding schemas must be source-controlled before repo creation is
treated as implementation completion.
## T06 - Open the repo-local follow-up workplans for implementation
```task
id: RMASTER-WP-0019-T06
status: done
priority: medium
state_hub_task_id: "18fe6f04-f8c5-451b-8e3f-38c19f05f682"
```
Once the boundary and sequencing documents exist, open or update the concrete
repo-local implementation workplans that will execute them.
At minimum, this should cover:
- `qonto-assistant`
- `key-cape`
- `reef-railiance`
- the future `rail-knative` repo at bootstrap time
- the future `rapp-qonto` repo at bootstrap time
Acceptance:
- each implementation-owning repo has a concrete follow-up workplan
- no implementation starts from an unwritten architecture assumption
- this framework workplan can later close with repo-local execution delegated
2026-07-26: Architecture is ready for repo-local workplan creation. Workplans
must separate source-repo, package, derived-rail, reef admission, Fabric
compatibility, identity/custody, and S2 runtime installation responsibilities.
2026-07-26: Opened and State Hub registered repo-local execution work in
`rail-kubernetes`, `rail-knative`, `rapp-qonto`, `reef-railiance`,
`railiance-fabric`, `qonto-assistant`, and `key-cape`. The two new repos were
reconciled with their existing Forgejo bootstrap commits and pushed.
## T07 - Version the rail-neutral workload and compatibility contract
```task
id: RMASTER-WP-0019-T07
status: done
priority: high
state_hub_task_id: "5b5f2d41-2860-44ee-a8b6-8b9e1d32854f"
```
Turn the composition decision into source-controlled schemas and conformance
checks.
Acceptance:
- workload declarations separate common facts from rail bindings
- base and derived rails declare contract versions and compatibility
- validation rejects missing owners, incompatible base rails, and unsupported
substrate capabilities
- Kubernetes remains the default binding for platform workloads
2026-07-26: `rail-kubernetes` now publishes base contract `1.0.0`, common
workload, rail-binding, and derived-rail schemas, a Kubernetes-default example,
and an offline validator. `rail-knative` and `rapp-qonto` pass that validator.
## T08 - Establish reef admission evidence and mixed-rail split triggers
```task
id: RMASTER-WP-0019-T08
status: done
priority: high
state_hub_task_id: "663a319e-2a05-4a35-90a2-4f6b7b5afa22"
```
Implement the production-readiness states and evidence model in
`reef-railiance` and Fabric.
Acceptance:
- topology is distinct from installed, verified, and production-approved state
- capacity, ingress, network, identity, observability, recovery, and failure
domain evidence have machine-readable locations
- Qonto's single-server/shared-control-plane residual risk is explicit
- mixed-rail split triggers are represented
2026-07-26: Added a reusable, secret-free substrate preflight in
`railiance-cluster` and recorded the failed API reachability result as reef
evidence with `block_installation`. Declared topology was not promoted.
2026-07-26: After the server upgrade, the server-side preflight and complete
Knative lifecycle verification passed. Reef evidence now distinguishes the
verified rail from blocked production approval and retains capacity and
single-node split triggers.
## T09 - Establish Qonto SLO, threat, rollback, and fallback evidence
```task
id: RMASTER-WP-0019-T09
status: done
priority: high
state_hub_task_id: "6cc2d3a7-33b6-47a7-b59d-04be9318c861"
```
Turn `docs/qonto-knative-runtime-contract.md` into executable package and
source-repo verification.
Acceptance:
- cold start, request timeout, buffering, concurrency, and scale-down values
are measured before production approval
- retry and idempotency behavior is explicit for financial operations
- public ingress and raw service exposure boundaries are testable
- egress is narrower than unrestricted HTTPS or has an approved exception
- rollback prefers a verified Knative revision; direct Kubernetes fallback is
time-bounded and exceptional
2026-07-26: `rapp-qonto` now has offline-validated, cluster-local,
digest-pinned, bounded Knative packaging with scale-to-zero, default-deny
networking, ExternalSecret references, and an explicit unverified FQDN egress
gate. Live SLO, identity, failure, and rollback evidence remains outstanding.
2026-07-27: Deployed digest-addressed revision `rapp-qonto-00004` on
rail-knative. The fail-closed proxy gate and cluster-local health smoke pass.
Full cold-start timing, audit/idempotency, revocation, dependency-failure, and
previous-revision rollback evidence remains before T09 completion.
2026-07-29: `rapp-qonto/tools/verify_live.sh` now records reversible,
machine-readable live evidence. The successful railiance01 run measured a
7.384443-second cold activation from zero and proved bounded repeat requests,
metadata-only secret delivery, fail-closed proxy loss, recovery,
missing-secret denial and restoration, previous-revision rollback, and return
to latest traffic. The owner repository's 80-test suite supplies the
domain-level audit, idempotency, authorization, and redaction evidence.
## T10 - Automate routing, conformance, reconciliation, and evidence
```task
id: RMASTER-WP-0019-T10
status: done
priority: high
state_hub_task_id: "1b75308f-f968-4443-aefe-af37550545a1"
```
Move repeatable operation toward reliable functional automation while
preserving least privilege and explicit risk authority.
Acceptance:
- `warden plan` and route lookup cover implementation credential needs before
any founder interaction is proposed
- agents use sanctioned `--exec`, `--out`, or wrapped transports rather than
reading secret values
- conformance checks are idempotent and suitable for CI or scheduled execution
- Fabric/State Hub reconciliation consumes source declarations and evidence
- human steps are limited to named authority or residual-risk decisions and
have an automation follow-up path
2026-07-26: Generic Knative and Qonto conformance is executable offline. The
cluster preflight is idempotent and read-only. Credential routing was attempted
first; its unrelated Forgejo match is tracked as a catalog-quality gap rather
than used for Kubernetes access.
2026-07-26: The cluster now has checksum-locked, idempotent Serving/Kourier
installation and live verification automation. The configured SSH lane
provides agent execution while direct public API access remains unnecessary.
2026-07-27: KeyCape client credentials are live, their OpenBao custody and
rotation route is published as `rapp-qonto-keycape-client`, and the M3/prod
posture manifest passes. Public `kc.coulomb.social` DNS still targets the
older CoulombCore endpoint; railiance01 verification currently uses direct
TLS-preserving resolution pending routing convergence.
2026-07-29: Credential needs are routed before access, installation and
verification use sanctioned SSH execution without secret output, and the live
gate is idempotent, reversible, and emits secret-free JSON suitable for CI or
scheduled reconciliation. Source declarations and reef bindings now consume
the verified evidence. The only remaining human authority is the explicitly
separate production acceptance or mitigation of single-node failure-domain
risk.
## Exit Criteria
- [x] `rail-knative` has a written boundary against `rail-kubernetes`
- [x] `rapp-qonto` has a written boundary against `qonto-assistant`
- [x] `reef-railiance` is explicitly named as the first host reef
- [x] identity and secret custody point at the future runtime path
- [x] derived-rail composition and non-duplication are decided
- [x] reef readiness and mixed-rail split rules are decided
- [x] Qonto end-to-end failure, rollback, and security boundaries are written
- [x] common and rail-binding schemas are implemented
- [x] repo-local implementation workplans are registered and active
- [x] conformance and credential-routing paths are agent-executable
- [x] second-wave repo bootstrap order is defined
- [x] repo-local implementation workplans exist before implementation begins
## Notes
This is a second-wave framework plan. It should stay at the planning and
coordination layer until the relevant repo-local workplans are in place.

View file

@ -0,0 +1,494 @@
---
id: RMASTER-WP-0021
type: workplan
title: "Rapp Declaration Schema and Family Declaration Validator"
domain: financials
repo: railiance-master
status: archived
owner: codex
topic_slug: railiance
planning_priority: high
planning_order: 21
created: "2026-08-11"
updated: "2026-08-18"
related_repos:
- railiance-master
- railiance-platform
- rapp-openbao
- rapp-postgres
- rapp-qonto
- rail-kubernetes
- rail-knative
- reef-railiance
- the-custodian
state_hub_workstream_id: "37667c23-1fa9-430e-8d80-d67d98f90e38"
---
# RMASTER-WP-0021 - Rapp Declaration Schema and Family Declaration Validator
## Goal
Give the `rapp-*` family a machine-checkable declaration contract, and stand up
a validator over all three family declaration files (`rail.yaml`, `rapp.yaml`,
`reef.yaml`) so that architectural drift fails loudly instead of accumulating
quietly.
The deliverable is not more prose. `docs/repo-family-bootstrap-contract.md`
already names the required `rapp.yaml` fields; naming them in prose is exactly
what failed. The deliverable is a schema plus a check that runs.
## Why This Exists
`RMASTER-WP-0017` through `RMASTER-WP-0019` established the four-axis model
and materialized the first concrete family repos. The model held up. Its
enforcement did not.
`railiance-platform` surveyed all six family repos and the live Railiance01
cluster on 2026-08-11 under its `RAILIANCE-WP-0015` and routed the findings
here, on the grounds that `railiance-master` owns the model and an
implementation repo should not author it. State Hub messages
`04c776c4-52fa-494a-9761-942123f28a6e` (proposal) and
`f88f938d-1ebc-4ba0-91ee-9395e44a7cb3` (amendment) carry the detail.
Reported drift across the three live rapps:
- rollout, smoke, and rollback contracts are mutually unreadable —
`rapp-openbao` and `rapp-postgres` use `supported_commands` lists,
`rapp-qonto` uses `default_mode` / `production_gate`, a `required:` smoke
list and an `order:` rollback list
- `contract_version`, `readiness_state`, `data_classification` and
`criticality` appear only in `rapp-qonto`, though both rails carry
`contract_version` / `readiness_state` consistently
- `consumer_contract` / `consumers` exist only in `rapp-postgres`
- `workload_identity.name` is the workload name in two rapps and the repo name
in the third
- `ownership_repo` points at a layer repo twice and at an app repo once
- `reef-railiance` `bound_rapps` lists `rapp-qonto` only, while `rapp-openbao`
and `rapp-postgres` are both live on Railiance01 — a hand-maintained registry
with nothing checking it
- roughly 17 live non-system workloads in-cluster against 3 rapps
The `rail-*` family received a tighter contract in wave 1 and has stayed clean.
That contrast is the argument for schema rather than for more documentation.
## Current Starting Point
- no `schemas/` directory exists in this repo
- `docs/repo-family-bootstrap-contract.md` lists eight required `rapp.yaml`
fields with no shapes, no types, and no cardinality rules
- `docs/rapp-first-wave-candidates.md` still reads as though the planned first
wave (`rapp-openbao`, `rapp-vergabe-teilnahme`, `rapp-forgejo`) happened; the
actual population is `rapp-openbao`, `rapp-postgres`, `rapp-qonto`, the
latter two created ad hoc from immediate need
- `rapp-vergabe-teilnahme` was never built, so the user-facing wrapper pattern
it existed to prove remains unproven while the platform-service pattern has
been proven twice
- canon does not yet admit the family prefixes, so `fix-consistency` cannot
call a family validator until `the-custodian` lands its side
- `railiance-platform` is holding `RAILIANCE-WP-0015-T02` (bringing
`rapp-openbao` and `rapp-postgres` onto one shape) until this schema exists
## Target Outcome
When this workplan is complete:
1. `schemas/rapp.schema.json` defines every required and optional `rapp.yaml`
field with one normative shape per contract.
2. A `composition:` block expresses what a grouped rapp actually is: a stated
purpose, first-party member repos, and pinned third-party components.
3. The two cardinalities are written down and enforced separately — repos are
many-to-many with rapps, deployables are one-to-one.
4. A validator runs over `rail.yaml`, `rapp.yaml`, and `reef.yaml` and fails on
drift.
5. Reef `bound_rapps` is derived from rapp declarations rather than
hand-listed.
6. `docs/rapp-first-wave-candidates.md` records what was actually built versus
planned, and carries the grouped wave-2 inventory.
7. Downstream repos have a single shape to converge on, and know it.
## Boundaries
This workplan owns the schema, the validator, the cardinality rules, and the
framework documents that describe them.
It does not migrate any rapp repo onto the new shape — that stays with each
owning repo, coordinated through `railiance-platform` `RAILIANCE-WP-0015-T02`.
It does not touch canon. Admitting the family prefixes into
`repo-classification-standard_v1.0` is `the-custodian`'s call, routed there
separately as `cc64764a`.
It does not address the Forgejo organization split. All repos currently sit
under a single `coulomb/` org and a split into stewardship orgs is a
URL-breaking change across every remote, CI config and ArgoCD source. It is an
independent dimension from rapp grouping and needs its own work record.
## Tasks
## T01 - Accept the routed proposal and record the ownership decision
```task
id: RMASTER-WP-0021-T01
status: done
priority: high
state_hub_task_id: "af45d82d-c1e3-47b3-81ae-2974bc65cc5d"
```
Read and acknowledge State Hub messages `04c776c4` and `f88f938d`, mark them
read, and reply to `railiance-platform` confirming that `railiance-master`
accepts ownership of the schema and that this workplan is the vehicle.
Record the framework decision that rapp bounded context is a grouping dimension
in its own right, derived neither from Forgejo organizations nor from State Hub
domains, with the cardinality argument as rationale: a repo lives in exactly
one Forgejo org, so `org:repo` is one-to-many, while a repo may legitimately
contribute to more than one rapp, so `rapp:repo` is many-to-many — and a
many-to-many grouping cannot be derived from a one-to-many one. Domains fail in
both directions. The three dimensions also change at different speeds.
Reference OAS P1 in `canon/standards/orthogonal-architecture_v1.0.md`: separate
independent perspectives into orthogonal dimensions.
**Done 2026-08-11.** Both messages marked read; reply `a9a8109f` sent to
`railiance-platform` accepting ownership and confirming this workplan as the
vehicle. No decision record was created here: `d07ee5f9` already carries the
dimension decision from the `railiance-platform` side, and duplicating it would
create two records of one decision. The rationale lands instead in `ADR-0007`
under T07. One question was returned to `railiance-platform` — whether they can
emit the live deployable inventory that T06 needs, so this repo does not
acquire a cluster dependency.
## T02 - Author schemas/rapp.schema.json
```task
id: RMASTER-WP-0021-T02
status: done
priority: high
state_hub_task_id: "87ae4551-1fac-4613-b258-28c8b22bbb82"
```
Create `schemas/` and write `rapp.schema.json` covering:
- the eight fields already required by the bootstrap contract: `rapp_id`,
`workload_identity`, `ownership_repo`, `supported_rails`,
`runtime_dependencies`, `rollout_contract`, `smoke_contract`,
`rollback_contract`
- `contract_version` and `readiness_state`, matching the shape the rail family
already carries
- `data_classification` and `criticality`, promoted from `rapp-qonto`-only to
required
- `consumer_contract`, promoted from `rapp-postgres`-only to optional but
schema-defined
Settle the two definitional questions the survey exposed. `workload_identity.name`
is the workload, never the repo. `ownership_repo` names the repo that owns the
workload's lifecycle; state explicitly whether a layer repo is a legal value,
since current declarations disagree.
Pick one normative shape for `rollout_contract`, `smoke_contract` and
`rollback_contract` rather than admitting all three observed variants. Note in
the schema which existing declaration the chosen shape most resembles, so
migration cost is visible.
**Done 2026-08-11.** `schemas/rapp.schema.json` (draft 2020-12) and
`schemas/README.md` written from the three live declarations rather than from
the survey summary, which turned out to understate the drift — see the extra
finding below.
Normative choices, with who pays:
- `default_mode` kept; all three declarations already carried it. No cost.
- `supported_commands``commands` in all three contracts. Mechanical.
- `smoke_contract.required` is required, `commands` optional — taken from
`rapp-qonto`. This is the one migration with real cost: the platform rapps
declare only commands and their owners must now name the outcomes those
commands establish. A command list records what was run; a required list
records what must be true, and only the second is checkable by anyone other
than the person who ran it.
- `rollback_contract.order` required, also from `rapp-qonto`, recoverable from
the command sequences already documented. `direct_kubernetes_fallback`
generalizes to `fallback`.
- `workload_identity.name` is the workload, never the repo; the schema forbids
the `rapp-` prefix outright.
- `contract_version`, `readiness_state`, `data_classification`, `criticality`
all promoted to required.
- `consumer_contract` schema-defined but optional.
`ownership_repo` is left permissive — exactly one repo, not the rapp repo
itself, layer and application repos both legal — so the schema does not
silently invalidate two of three live declarations on a question the survey
cannot settle. The tighter alternative and its consequence are written up in
`schemas/README.md` for the architecture owner. Treat the permissive rule as
provisional.
Validated against all three live declarations with `jsonschema` 4.10.3. The
schema is well-formed and reproduces precisely the drift the survey reported,
which is the intended result — it fails on real drift and not on anything else:
| Declaration | Errors | Character |
|---|---|---|
| `rapp-openbao` | 10 | four missing metadata fields, no composition, three contracts on the old shape |
| `rapp-postgres` | 10 | identical set — the two platform rapps drifted together |
| `rapp-qonto` | 4 | composition, `direct_kubernetes_fallback`, missing `package_type`, and `workload_identity.name` = `rapp-qonto` |
**Extra finding, not in the routed survey.** There are six `rapp-*` repos, not
three: `rapp-secrets-engine`, `rapp-tenant-engine` and `rapp-user-engine` exist
and carry no `declarations/` directory at all. The survey counted only declared
rapps, so the real gap is wider than the roughly 17-workloads-versus-3-rapps
figure suggests — three further repos claim the family prefix while declaring
nothing. Raised to `railiance-platform`; whether these are stubs, abandoned, or
undeclared live workloads should be settled before T06 draws its coverage
baseline, since undeclared repos are invisible to a coverage check that reads
declarations.
## T03 - Define the composition block and its cardinality rules
```task
id: RMASTER-WP-0021-T03
status: done
priority: high
state_hub_task_id: "8b130ff8-0601-40bb-8539-d6f5847c2f41"
```
Supersede the flat `members:` list from the original proposal with the
composition block from the amendment:
```yaml
composition:
purpose: <what this coherent workload does>
member_repos: # first-party, each sharing rollout and rollback fate
- repo: <slug>
role: <what it contributes>
deployables: [<names>]
upstream_components: # third-party, pinned
- name: <chart|image|operator>
source: <repo/chart or registry ref>
version: <pin>
```
Encode both cardinalities. A repo may appear in the `member_repos` of more than
one rapp. A deployable must belong to exactly one rapp — each running
deployable has exactly one rapp owning its rollout. Keeping these distinct is
what makes the coverage check in T06 well-defined.
Carry the operator decision of 2026-08-11 into the schema: rapp granularity is
grouped-by-bounded-context, one rapp per cohesive group that deploys, versions
and rolls back together, not one per deployable. Grouping is legitimate only
where members share rollout and rollback fate; make that testable rather than
advisory where possible.
**Done 2026-08-11.** `composition` is required on every rapp, in
`schemas/rapp.schema.json` under `$defs/composition`. A single-repo rapp
declares exactly one member, so there is no second shape for the ungrouped
case — the composition block is the only way to state membership.
`purpose` carries a `minLength` and is documented as the bounded-context
statement: if it cannot be written without "and also", the grouping is probably
wrong. That is the closest this schema gets to testing shared rollout fate;
the substantive check is structural, in that `deployables` is required on every
member, which is what T06 consumes.
Both cardinalities are encoded where they bite. `member_repos[].repo` is
unconstrained across rapps, so a repo may appear in several — many-to-many.
`member_repos[].deployables` is documented as one-to-one and enforced by the
validator's coverage check rather than by the schema, since no single-file
schema can see across declarations. `upstream_components` requires an exact
`version` pin, with the schema stating that ranges and floating tags are not
pins.
`workload_identity.package_type` gains a `grouped-composition` value for rapps
whose members carry their own package types.
## T04 - Schematize rail.yaml and reef.yaml, and derive bound_rapps
```task
id: RMASTER-WP-0021-T04
status: done
priority: medium
state_hub_task_id: "50ffbff9-f8f0-4085-975a-6c5123d9c2f0"
```
Write `schemas/rail.schema.json` and `schemas/reef.schema.json` from the
minimum field sets in `docs/repo-family-bootstrap-contract.md`. The rail family
has stayed clean, so this is largely codifying what already works; do not
tighten it beyond current practice without cause.
Change `reef.yaml` `bound_rapps` from a hand-maintained list to a derived
projection of the rapp declarations that name the reef, and record the
migration path for `reef-railiance`, whose current list is already known stale.
**Done 2026-08-13.** `schemas/rail.schema.json` and `schemas/reef.schema.json`
written from the live declarations and the composition/substrate contracts,
not from the bootstrap field lists (those lists used an older
`substrate_prerequisites` name the live rails do not carry).
Both live rails conform. The live reef conforms as a document.
`bound_rapps` is optional and derived from a new required `rapp.bound_reefs`
field (empty list means declared but not placed). The live
`reef-railiance` hand-list `[rapp-qonto]` fails the derivation check, which
is the intended result. Migration for that repo: delete the field now, or
replace it with the derived set once the three live rapps name the reef.
Adjacent ownership, recorded so it is not mistaken for a schema hole: the
postgres backup object store is a consumed capability (`s3-backup-target`),
not a rapp-owned resource. Bucket = `resource-control` / `RESOURCE-WP-0002`.
Credential = `railiance-platform` OpenBao `Secret platform-pg-backup-s3`.
Purchase = decision `9c21c0e0`. `make postgres-backup-deploy` stays
fail-closed until that handoff lands.
## T05 - Build the family declaration validator
```task
id: RMASTER-WP-0021-T05
status: done
priority: high
state_hub_task_id: "0a212d33-2169-4472-80d0-69ec4edba5cb"
```
Add a validator under `tools/` that walks the family repos, locates each
`declarations/*.yaml`, and validates it against the matching schema.
Checks in priority order, per the routed proposal:
1. every declaration conforms to its schema
2. every declared member repo and upstream pin resolves
3. reef `bound_rapps` is derivable from rapp declarations rather than
hand-listed
4. deployable coverage — deferred to T06, which needs cluster state
Exit non-zero on any failure and name the offending file and field. Make it
runnable standalone now, and callable from `fix-consistency` later; do not
block on canon admitting the prefixes, since that is `the-custodian`'s
sequencing, not ours.
**Done 2026-08-13.** `tools/validate-family-declarations.py` walks
`rail-*` / `rapp-*` / `reef-*` siblings of `--root` (default: parent of
this repo). `--self-test` passes against `tools/testdata/family-declarations`.
First live scan of `/home/worsch` (no inventory): 6 declarations, 31
errors. The two rails are clean. The three declared rapps fail on the
already-known shape drift plus the new `bound_reefs` field. The reef fails
only on stale `bound_rapps`. The three undeclared engine repos fail as
prefix-without-declaration. That is exactly the drift the schema exists to
make loud.
## T06 - Add the deployable coverage check
```task
id: RMASTER-WP-0021-T06
status: done
priority: medium
state_hub_task_id: "519c2838-c4af-46e9-81fa-612fb09e91e3"
```
Extend the validator with the standing control-loop question: does every live
deployable belong to exactly one rapp?
This is the check that turns the schema from a one-shot lint into an ongoing
control loop. Per the amendment it catches all three drift findings at once —
stale reef bindings, unpackaged live workloads, and the roughly
17-workloads-versus-3-rapps gap.
Decide how live cluster state reaches the check without `railiance-master`
acquiring a cluster dependency it should not have. Consuming an inventory
produced by an implementation repo is preferable to querying Railiance01 from
here.
Expect the first run to report a large uncovered set. That output is the wave-2
worklist, not a failure to suppress.
**Done 2026-08-13.** The validator takes `--inventory PATH`. This repo does
not query a cluster. The file is a JSON mapping with `source`,
`captured_at`, optional `reef`, and `deployables` as a list of `{name,
namespace?, kind?}` (bare name strings also accepted). Live names that no
rapp claims are errors labelled as wave-2 worklist items. Declared names
missing from the inventory are warnings.
The first live run is not in this repo: it needs `railiance-platform` to
emit the inventory T01 asked for. The check itself is implemented and
covered by `--self-test`.
## T07 - Update the bootstrap contract to point at the schemas
```task
id: RMASTER-WP-0021-T07
status: done
priority: medium
state_hub_task_id: "aa71bc2f-7d9e-4351-8130-8b6e23b8ee36"
```
Revise `docs/repo-family-bootstrap-contract.md` so the minimum-field lists cite
the schema files as normative rather than restating fields in prose. Prose that
duplicates a schema will drift from it.
Record an ADR (next free number, `ADR-0007`) for the framework decisions this
workplan settles: bounded rapp context as an independent dimension, the two
cardinalities, and grouped-by-bounded-context granularity.
**Done 2026-08-13.** Bootstrap contract now points at the three schema files
and refuses to restate their fields. `docs/adr/ADR-0007-rapp-declaration-contract.md`
records the dimension, the two cardinalities, grouped-by-bounded-context
granularity, and that the schemas are normative. Decision `d07ee5f9` remains
the platform-side record; this ADR is the framework record, not a second
decision.
## T08 - Refresh rapp-first-wave-candidates.md
```task
id: RMASTER-WP-0021-T08
status: done
priority: medium
state_hub_task_id: "28e4ef82-62ea-41d2-a059-05c047292240"
```
Correct the document to record what was actually built against what was
planned: `rapp-openbao` as planned, then `rapp-postgres` and `rapp-qonto` ad
hoc from need, and `rapp-vergabe-teilnahme` never built.
State the consequence plainly — the platform-service pattern is proven twice
and the user-facing wrapper pattern is unproven — and carry the operator
decision to build `rapp-vergabe-teilnahme` to prove the user-facing shape
before mass extraction.
Add the grouped wave-2 inventory in the new composition shape. Indicative
candidates: `rapp-netkingdom-identity` (sso, mfa, user-engine, tenant-engine,
flex-auth), `rapp-forgejo` (forgejo, runner, registry), `rapp-coulomb-social`,
`rapp-helix-forge`.
**Done 2026-08-13.** The document now leads with the plan-versus-outcome
table, states that the user-facing wrapper is unproven, keeps the 2026-07-25
assessment as history, and carries the wave-2 inventory in composition
shape. The three undeclared engine repos are named so they are not invented
again as separate rapps. `rapp-forgejo` placement cites `d151d817`.
## T09 - Release the schema to downstream repos
```task
id: RMASTER-WP-0021-T09
status: done
priority: high
state_hub_task_id: "b8dc2452-8ec0-4301-98b7-4ec96411f428"
```
Waiting on T02, T03 and T05.
Notify `railiance-platform` that the shape is settled and
`RAILIANCE-WP-0015-T02` is unblocked, and notify `rapp-openbao`,
`rapp-postgres`, `rapp-qonto` and `reef-railiance` of the migration they each
own. Include the normative shape chosen for the rollout, smoke and rollback
contracts and what each repo must change.
Migration itself belongs to the owning repos. This task ends when they have
what they need, not when they are done.
**Done 2026-08-13.** Release messages sent. Downstream now has the shape and
the per-repo change list; migration itself is not this task.
| To | Message | What they received |
|---|---|---|
| `railiance-platform` | `072c5820` | WP-0015-T02 unblocked; inventory shape for T06; S3 split restated |
| `rapp-openbao` | `093f2f73` | 11-error migration (metadata, composition, `bound_reefs`, contract rename) |
| `rapp-postgres` | `7b1cb9b7` | same 11-error set; S3 is a consumed destination, not a hole |
| `rapp-qonto` | `16742192` | 5-error migration (`composition`, `bound_reefs`, `package_type`, name, `fallback`) |
| `reef-railiance` | `6b395e26` | delete or replace stale `bound_rapps` |
| `resource-control` | `aa8393e3` | coordination only: bucket is theirs (`RESOURCE-WP-0002`) |

View file

@ -0,0 +1,136 @@
---
id: RMASTER-WP-0022
type: workplan
title: "Establish reef-storage for Scaleway object storage"
domain: financials
repo: railiance-master
status: archived
owner: grok
topic_slug: railiance
created: "2026-08-14"
updated: "2026-08-18"
related:
- RMASTER-WP-0017
- RMASTER-WP-0021
- RESOURCE-WP-0002
- RESOURCE-WP-0006
state_hub_workstream_id: "b63b9d6b-deb5-4f13-b6db-12da6f7bd644"
---
# RMASTER-WP-0022 — establish reef-storage
## Goal
Create `reef-storage` as the reef that **operates** S3-compatible object
storage (first instance: Scaleway, for postgres backup). This is a
different substrate from `reef-railiance`. Backup will not be provided on
top of the home compute reef.
`resource-control` procures and costs the bucket. `reef-storage` owns the
committed non-secret operating attributes (endpoint, bucket, region,
prefix, lifecycle). `railiance-platform` holds the secret.
`rapp-postgres` consumes a reviewed destination.
Join convention: `resource-control/docs/operational-reference-convention.md`
(`reef:storage/…#key`).
## Why a new reef
`docs/reef-substrate-model.md`: a reef is one durable operational
boundary. Scaleway object storage has its own identity, access path,
lifecycle, and failure domain (`provider:scaleway` / `region:nl-ams`).
Putting a bucket “on” `reef-railiance` would mix a cloud object store into
a Host Europe compute substrate and hide the independent failure domain
the backup workplan requires.
## Schema tension
The current `reef.schema.json` requires `primary_rail` and
`hosted_rails`. A storage reef does not host `rail-kubernetes`. This
workplan must either:
- allow a storage substrate with no rail (preferred), or
- introduce a storage execution contract later, not as a fake rail.
Do not list `rail-kubernetes` as hosted just to satisfy the schema.
## Intended declaration (planned)
```yaml
kind: substrate-reef
reef_id: reef-storage
repo: reef-storage
ownership_repo: railiance-platform
substrate_kind: object-storage
lifecycle_state: planned
criticality: high
# primary_rail / hosted_rails: omitted or schema-extended — see T01
current_members: [] # fill with Scaleway project/bucket names after purchase
```
Non-secret attributes live under e.g.
`substrate/object-stores/<resource-slug>.yaml` so resource-control can cite
`reef:storage/substrate/object-stores/platform-audit-storage.yaml#endpoint`.
## T01 — Extend the reef schema for storage substrates
```task
id: RMASTER-WP-0022-T01
status: done
priority: high
state_hub_task_id: "c222e439-795d-45bb-ba81-b94b2a571a1c"
```
Amend `schemas/reef.schema.json` and
`docs/reef-substrate-model.md` so a reef may be an object-storage (or
other provider-managed) substrate without hosting a compute rail.
Document `substrate_kind: object-storage`. Keep `reef-railiance` valid.
Done when a planned `reef-storage` declaration validates and
`reef-railiance` still validates.
Completed 2026-08-14: `primary_rail` / `hosted_rails` optional; empty
`current_members` allowed; substrate model documents provider-delegated
reefs with no rail. Confirmed: no rail — Scaleway operates S3.
## T02 — Bootstrap the reef-storage repo
```task
id: RMASTER-WP-0022-T02
status: done
priority: high
state_hub_task_id: "61617b38-27b5-4c44-be8a-244a2476f1eb"
```
Materialize `reef-storage` from the first-wave bootstrap contract:
classification, INTENT, SCOPE, AGENTS, `declarations/reef.yaml`, empty
attribute file layout, workplans. Register the remote. Do not create a
Scaleway account in this task.
Done when the repo exists, declares `lifecycle_state: planned`, and
resource-control can name `reef:storage/…` paths that will be filled after
purchase.
Completed 2026-08-14: operator provided the repo; declaration, INTENT,
SCOPE, and planned attribute path committed.
## T03 — Attribute file contract for object stores
```task
id: RMASTER-WP-0022-T03
status: done
priority: medium
state_hub_task_id: "6ea318c4-33e5-4177-9f87-433670636304"
```
Define the committed YAML shape for one object-store: endpoint, region,
bucket, prefix, versioning/lifecycle flags, provider project ref. No
keys. Hand resource-control and `rapp-postgres` the path, not a chat
paste.
Done when a schema or documented example exists and WP-0002 T03 can cite
it as the destination of non-secret attributes after buy.
Completed 2026-08-14:
`reef-storage/schemas/object-store-attributes.schema.json` and planned
`substrate/object-stores/platform-audit-storage.yaml`.

View file

@ -0,0 +1,353 @@
---
id: RMASTER-WP-0023
type: workplan
title: "Private-by-default exposure until production admission"
domain: financials
repo: railiance-master
status: archived
owner: grok
topic_slug: railiance
planning_priority: high
planning_order: 23
created: "2026-08-15"
updated: "2026-08-18"
related:
- ADR-0006
- ADR-0008
- RMASTER-WP-0017
- RMASTER-WP-0019
- RMASTER-WP-0021
- RMASTER-WP-0022
- RAIL-HO-WP-0009
related_repos:
- railiance-master
- rail-kubernetes
- railiance-cluster
- reef-railiance
- reef-storage
- railiance-infra
- railiance-enablement
- ops-bridge
state_hub_workstream_id: "50303bdf-aada-4c38-8782-d4e63b603e65"
---
# RMASTER-WP-0023 — private-by-default exposure until production admission
## Goal
Make every new reef, rail, and rapp **unreachable from the public internet
until it is production-safe**, by one family rule that the implementation
repos can enforce.
"Production-safe" already has a name: ADR-0006 admission
(`declared``installed``verified``production-approved`). What is
missing is the **default-private exposure rule** and a field the schemas can
check. Topology and a working deploy must not imply a public listener.
## Why this is here
`railiance-master` owns framework contracts that span `reef-*`, `rail-*`,
and `rapp-*`. Shielding "whatever we spin up" is that kind of rule.
It does **not** belong as the system owner in:
- `railiance-infra` — host UFW. Necessary for a new reef with no 80/443;
useless for a rapp that shares `railiance01` ingress. RAIL-HO-WP-0009
closed the hand-opened k3s API; do not make S1 the admission desk for
rapps. Do not use this workplan to re-public `6443`.
- `railiance-cluster` / `rail-kubernetes` — Ingress, NetworkPolicy, Service
types. This is where most packets will actually be blocked. They cannot
define what "production-safe" means for a rapp or for a non-k8s reef
(`reef-storage`).
- each `rapp-*` — must declare intended exposure; must not punch its own
public hole.
- `railiance-enablement` — should ship private-by-default templates; does
not own the rule.
- `reef-railiance` alone — too local; the next reef must inherit the same
rule.
- `ops-bridge` — the paved path to a shielded thing; not the policy owner.
## What already exists
- ADR-0006 and `docs/reef-production-readiness-contract.md` — admission
states; network isolation and extra gates for internet-facing or
high-value workloads. Admission answers "may this binding run in
production?" Exposure answers "who may reach the listener?" Keep them
beside each other.
- Family schemas (`schemas/{reef,rail,rapp}.schema.json`) — no
`exposure` / `network_posture` field. Rapp already uses `public` on
`data_classification`; the new field must not reuse that name as a
top-level enum collision.
- Readiness vocabularies are **deliberately not unified**
(`schemas/README.md`):
- rail: `declared | installed | verified | production-approved | deprecated`
- rapp: `draft | declared | deployed | verified | retired`
- reef: `lifecycle_state` `planned | active | retired`
- bindings: ADR-0006 states on the reef
- RAIL-HO-WP-0009 — operator access to a shielded k3s API is ops-bridge
(`k3s-api-railiance01` on `:16444`). That is the intended pattern for
anything not yet public.
- Live public exceptions already exist on `reef-railiance` (Forgejo,
Coulomb Social, Host Europe Nydus on 2224). They must be named as
grants, not silently grandfathered.
- Provider-delegated reefs (`reef-storage`, Scaleway S3) already expose a
provider API on the internet. That is a different category from a
listener we open.
## Decision (T01, accepted 2026-08-15)
Ratified as written. Recorded in
[`docs/adr/ADR-0008-private-by-default-exposure.md`](../../docs/adr/ADR-0008-private-by-default-exposure.md).
| Posture | Meaning | Who may reach it |
| --- | --- | --- |
| `private` | No extra listener we control | in-cluster DNS, same-reef services |
| `operator` | Same listener as `private`, plus a named ops-bridge / SSH tunnel | operator and approved agents |
| `public` | Internet listener we publish | anyone the Ingress / DNS / UFW surface allows |
`operator` is an **access annotation**, not a different packet posture.
It does not open a host port or a public Ingress. A ClusterIP service
plus a named tunnel is still `private` at the packet layer.
Rules:
1. New reefs, rails, and rapps default to `private`. Use `operator` only
when a named admin or API surface must be reachable before admission
(k3s API, OpenBao UI). Do not prefer `operator` as the debug default
for every new rapp — that grows an ops-bridge catalog by accident.
2. `public` requires **both** an ADR-0006 binding of
`production-approved` **and** an explicit exposure grant. Do not key
`public` off `rapp.yaml` `readiness_state` (it has no
`production-approved`) and do not add that value to the rapp enum in
this workplan.
3. A reef has no `production-approved` lifecycle. A public reef surface
(host port, public DNS we publish) is a **substrate grant**, not a
binding admission. T01 must name that grant shape separately.
4. A working deploy, a `hosts_rail` / `binds_rapp` line, or an Ingress
object is not a grant.
5. A grant is accepted residual risk (reason, approved_on, residual-risk
owner, hostname or port). It is not a self-serve wish in the
declaration.
6. Ops-bridge is the normal path to a shielded thing. That is the same
trade as ADR-005 in `railiance-infra`: convenience depends on the
tunnel; the Internet does not see the service.
7. `6443` / the k3s API is not a grantable public surface. RAIL-HO-WP-0009
already closed it; this contract must not become a way to reopen it.
Do not unify the three readiness enums in this workplan. Exposure sits
beside them. Record the rule as **ADR-0008** (sibling), not as an edit
that mixes "is this binding production-safe?" with "who may reach it?"
The shared vocabulary is one enum. It is **not** one schema shape and
not one admission check:
| Family | What the field means | What makes `public` legal |
| --- | --- | --- |
| `rapp-*` | intended consumer-facing listener | binding `production-approved` + grant |
| `rail-*` | which listener classes the rail may emit | rail may emit public Ingress only when a bound rapp/rail grant exists |
| `reef-*` | host ports and public DNS we publish | substrate grant; not reef `lifecycle_state` |
A rapp cannot be `public` on a reef that has not granted a public
surface. Conflicting declarations fail closed.
## Boundaries
This workplan may:
- add ADR-0008 for exposure posture and point ADR-0006 at it
- extend family schemas and `tools/validate-family-declarations.py`
- write the exposure contract and a dated exception snapshot
- **route** implementation work to owning repos (intake notes or State
Hub messages). The owning-repo agent files the workplan. This repo
does not implement there and does not silently create four sibling
workplans as a side effect of registration.
It must not:
- implement NetworkPolicy, Ingress classes, UFW rules, or ops-bridge
tunnels here
- take down live public services as a side effect of the contract
- put booked cost, credentials, or customer numbers in declarations
- inventory CoulombCore host ports (that stays with RAIL-HO-WP-0009 /
`railiance-infra` until CoulombCore is a declared reef)
- absorb Q7 / Goss declared-vs-live *reaction* (page a human, open a
task, block a rollout). Inbox `ae73593a` handed that to
`railiance-master`; it is a separate workplan
- treat provider-native internet APIs (Scaleway S3, other
provider-delegated endpoints) as the same grant as a listener we open.
This contract covers listeners we control: host ports, Ingress,
Service types, and public DNS we publish
- migrate live family declarations onto the new field (owning repos, via
T05)
- become a living port inventory. After T04, updates live in the owning
declarations
## Tasks
### T01 — Ratify the exposure rule
```task
id: RMASTER-WP-0023-T01
status: done
priority: high
state_hub_task_id: "40d50848-d59e-4f7c-98df-a442dce90439"
```
Human review. Accept or amend the recommended table above. Record ADR-0008.
Name the default posture for a new reef, a new rail, and a new rapp
(recommended: `private` for all three). Confirm that `public` keys off
the **binding** admission state and that a reef public surface uses a
substrate grant. Confirm `6443` is not grantable as `public`.
**Done when:** ADR-0008 is accepted, the three readiness enums are left
distinct on purpose, and the per-family meaning of `public` is written
down.
Completed 2026-08-15: operator accepted the recommended table as
written. ADR-0008 records the postures, the per-family meaning of
`public`, the grant shape, and the 6443 exclusion. Readiness enums stay
distinct.
### T02 — Write the exposure contract
```task
id: RMASTER-WP-0023-T02
status: done
priority: high
state_hub_task_id: "96db2a2d-670c-4b98-b6f3-860f9b3009b3"
```
Depends on T01.
One contract, `docs/exposure-posture-contract.md`, that says what each
posture means **per family**:
| Family | `private` | `operator` | `public` |
| --- | --- | --- | --- |
| `reef-*` compute | no extra host ports; no public DNS for new members | ops-bridge / SSH only | declared host/DNS surface plus substrate grant |
| `reef-*` provider-delegated | we publish no extra name | n/a | out of this enum; provider API is not a listener we open |
| `rail-*` | no public Ingress class; default-deny NetworkPolicy | tunnel to the rail's API or admin path | rail may emit public Ingress for a granted binding |
| `rapp-*` | ClusterIP / in-cluster DNS only | named tunnel (local port) on the same ClusterIP | named public hostname plus binding grant |
Include the fail-closed rule: missing field means `private`.
Include the conflict rule: rapp `public` + reef without a public
substrate grant is invalid.
**Done when:** implementation repos can implement without inventing
vocabulary.
Completed 2026-08-15: `docs/exposure-posture-contract.md` names the
per-family reading, grant shape, fail-closed default, conflict rule,
6443 exclusion, and provider-delegated carve-out.
### T03 — Schema and validator
```task
id: RMASTER-WP-0023-T03
status: done
priority: high
state_hub_task_id: "03070be7-925e-4aee-9938-85b03b8cbe37"
```
Depends on T01.
Add `exposure` (or the T01 name) to the family schemas. Share the
vocabulary; do not pretend one object shape fits all three. Validator
must fail when:
- a rapp or rail posture is `public` and the relevant **binding** is not
`production-approved`
- posture is `public` and no grant object is present (hostname / port /
reason / approved_on / residual-risk owner)
- a rapp is `public` and its reef has not granted a public surface
Keep the field additive so existing declarations do not break the day
the schema lands. Requiring the field on live declarations is a T05
migration in the owning repos, not a T03 done-when.
Name the field so it cannot be confused with rapp
`data_classification: public`.
**Done when:** `tools/validate-family-declarations.py` rejects a public
rapp that is not production-approved, and the testdata tree has good and
bad fixtures. Live sibling repos are unchanged.
Completed 2026-08-15: additive `exposure` on all three family schemas.
Validator rejects unapproved public rapps, missing grants, public rapps
on a private reef, port 6443, and public provider-delegated reefs.
Fixtures: `good-exposure` and `bad-exposure`. Live declarations omitted
the field and still validate.
### T04 — Exception snapshot for already-public surfaces
```task
id: RMASTER-WP-0023-T04
status: done
priority: medium
state_hub_task_id: "6576cc7c-6c85-4d3b-b0cc-dfba4e9b4774"
```
Dated snapshot in this repo so the contract does not pretend
`reef-railiance` is already private. Seed from what is already named:
Forgejo, Coulomb Social, Host Europe Nydus 2224, then fill anything else
found on that reef.
Each entry: hostname or port, owning rapp or provider, why it is public,
residual-risk owner. Same shape T01 names for a grant.
This snapshot is not the living source of truth. T05 files the grant
objects into the owning declarations. Later changes happen there.
Out of scope: CoulombCore, shutdown plans, provider-native APIs.
**Done when:** every known public surface on `reef-railiance` is either a
snapshot grant or a routed task to close it.
Completed 2026-08-15:
`docs/evidence/reef-railiance-exposure-snapshot-2026-08-15.md`.
Grants: Forgejo, Coulomb Social, reuse-surface, Nydus 2224. Routed:
`bao.coulomb.social` verify/close; CoulombCore names stay out.
### T05 — Route enforcement work
```task
id: RMASTER-WP-0023-T05
status: done
priority: high
state_hub_task_id: "d780df59-c67d-486e-8e8a-15e5043cd513"
```
Depends on T01 and T02.
Write intake notes here or send State Hub messages to the owning-repo
agents. Those agents file the workplan in their repo. Do not implement
there from this repo.
| Owner | What to enforce |
| --- | --- |
| `rail-kubernetes` (or `railiance-cluster` until the rail owns networking) | default-deny NetworkPolicy; no public Ingress unless the grant exists; ClusterIP as the paved Service |
| `reef-railiance` | first live admission: new binds stay `private` (or `operator` only for a named admin path); existing public binds receive the T04 grants |
| `railiance-infra` | new reefs do not get 80/443 until a reef substrate grant exists; 6443 stays operator-only; this is not the rapp gate |
| `railiance-enablement` | templates emit private Services + tunnel docs, never a public Ingress by default |
**Done when:** each owning repo has a workplan or an accepted intake,
with a back-link to this ID.
Completed 2026-08-15: intakes in
`docs/exposure-enforcement-intakes.md`. Child workplans filed:
`RAIL-K8S-WP-0003`, `REEF-RAILIANCE-WP-0004`, `RAIL-HO-WP-0010`,
`RAIL-EN-WP-0001`.
## Suggested review order
1. T01 (human) — accept or change the recommended table.
2. T04 can start now as a census; its grant fields lock after T01.
3. T02 after T01.
4. T03 after the field names are stable.
5. T05 last; it is the only task that creates work in other repos.
## Outcome
Finished 2026-08-15. ADR-0008, the exposure contract, additive schemas,
validator fixtures, the reef-railiance snapshot, and routed intakes are
in this repo. Packet-level enforcement stays with the owning repos.