# Wave 1 `rail-kubernetes` Boundary Date: 2026-07-25 ## Purpose Define the wave-1 extraction boundary between `railiance-cluster` and a future `rail-kubernetes` repo. This document exists to make `rail-kubernetes` real rather than rhetorical. Today, `railiance-cluster` already contains the practical contract for running managed workloads on Kubernetes: - `docs/deployment-lifecycle.md` - `docs/app-toml-contract.md` - `docs/overlay-repo-pattern.md` - `schemas/railiance-app.schema.json` - `examples/railiance/app.toml` - `bin/railiance` - `tools/cmd/railiance-run` - `tools/cmd/railiance-stage2` - `tools/cmd/railiance-stage3` - `tools/create_railiance_overlay_repo.sh` That is already a rail in substance. The problem is that it currently lives inside the S2 cluster-runtime repo. ## Decision Summary - `rail-kubernetes` is the default base rail for wave 1. - `railiance-cluster` remains the S2 owner of the Kubernetes substrate. - `rail-kubernetes` owns the generic workload-on-Kubernetes execution contract. - Workload-specific helpers currently living in `railiance-cluster` are not part of the long-term boundary for either repo. They are migration debt that must be rehomed. ## What `railiance-cluster` Keeps `railiance-cluster` remains responsible for the Kubernetes substrate itself. It keeps ownership of: - k3s installation, upgrade, and baseline configuration - cluster networking, ingress controllers, DNS, and certificate plumbing - cluster operators and addons such as cert-manager, CloudNative PG operator, ArgoCD, admission controllers, and similar cluster-scoped services - kubeconfig management, runtime access patterns, and cluster backup/restore posture - cluster smoke tests that prove substrate readiness - cluster-level runbooks for substrate recovery, upgrade, access, and failure handling - cluster capability declarations that tell higher layers what the substrate actually supports `railiance-cluster` may describe the prerequisites a rail needs from the substrate, but it should not own the workload lifecycle semantics that run on top of those prerequisites. ## What `rail-kubernetes` Must Own `rail-kubernetes` owns the generic contract for Railiance-managed workloads that run on Kubernetes. It should own: - the three-stage lifecycle semantics for Kubernetes-backed workloads - the canonical `railiance/app.toml` contract for the Kubernetes rail - the machine-readable schema and reference example for that contract - the generic Stage 1, Stage 2, and Stage 3 CLI/tooling - Kubernetes-specific check types, rollout modes, and rollback expectations - the generic packaging pattern for Kubernetes-managed `rapp`s - the generic repo scaffold for Kubernetes-targeting wrappers - compatibility guidance for how a `rapp` declares Kubernetes namespaces, releases, probes, ingress, routing, and rollback - the documented compatibility path from today's overlay pattern into future `rapp-*` repos In short: - `railiance-cluster` answers "is the Kubernetes substrate present and healthy?" - `rail-kubernetes` answers "how does a Railiance-managed workload run on that substrate?" ## What Should Not Stay In Either Boundary Some items currently inside `railiance-cluster` are not good long-term residents of either `railiance-cluster` or the future `rail-kubernetes`. Examples include workload-specific helpers such as: - `deploy-triage-robustness` - `admin-sync-smoke` - workload-specific runtime reconcile flows tied to `activity-core` or similar single-workload concerns These belong with the owning workload or ownership repo unless they are refactored into clearly generic rail behavior. `rail-kubernetes` must not become a second junk drawer after the split. ## File And Artifact Migration Map | Current location in `railiance-cluster` | Target owner | Notes | | --- | --- | --- | | `docs/deployment-lifecycle.md` | `rail-kubernetes` | Generic workload lifecycle, no longer S2-owned | | `docs/app-toml-contract.md` | `rail-kubernetes` | Base rail contract for Kubernetes-managed workloads | | `schemas/railiance-app.schema.json` | `rail-kubernetes` | Versioned workload declaration schema | | `examples/railiance/app.toml` | `rail-kubernetes` | Reference contract example | | `docs/overlay-repo-pattern.md` | `rail-kubernetes` | Keep as compatibility doc, evolve toward `rapp-*` language | | `tools/create_railiance_overlay_repo.sh` | `rail-kubernetes` | Keep behavior initially, then retarget toward `rapp-*` scaffolding | | `docs/railiance-run-command.md` | `rail-kubernetes` | Stage 1 command reference | | `docs/stage2-deploy-observe.md` | `rail-kubernetes` | Stage 2 command reference | | `tools/cmd/railiance-run` | `rail-kubernetes` | Generic Stage 1 tooling | | `tools/cmd/railiance-stage2` | `rail-kubernetes` | Generic Stage 2 tooling | | `tools/cmd/railiance-stage3` | `rail-kubernetes` | Generic Stage 3 tooling | | `bin/railiance` generic lifecycle commands | `rail-kubernetes` | `run`, `deploy`, `observe`, `promote`, `rollback`, `create-overlay` | | `bin/railiance` substrate/bootstrap commands | `railiance-cluster` | `doctor`, `plan-host`, `cloudinit`, `backup`, `preflight`, similar substrate helpers | | `tests/smoke_kube.sh`, cluster bootstrap tests | `railiance-cluster` | Substrate health proof | | workload-specific reconcile helpers | rehome later | Move to owner repo or rewrite as generic rail helpers | ## The Interface Between The Two Repos The clean split depends on a narrow interface. `railiance-cluster` should publish a Kubernetes substrate profile that `rail-kubernetes` can consume. At minimum, that profile should declare: - cluster distro and supported Kubernetes version range - ingress controller and ingress classes - supported canary modes, such as `isolated` and optional `weighted` - certificate management path - default storage classes and stateful-workload constraints - required or optional operators available to workloads - namespace and RBAC expectations for managed workloads - observability surfaces available for health, logs, and metrics - approved secret-delivery patterns available on this substrate `rail-kubernetes` should not assume a capability the substrate profile does not declare. This keeps the substrate owner and the rail owner separate while still letting them compose. ## Migration-Safe Path The split should happen in six steps. 1. Create `rail-kubernetes` with the copied contract docs, schema, examples, and generic Stage 1-3 tooling now living in `railiance-cluster`. 2. Leave compatibility shims in `railiance-cluster` for one migration window. The cluster repo may keep a thin `bin/railiance` wrapper that delegates the generic lifecycle commands to `rail-kubernetes`. 3. Update the moved docs in `railiance-cluster` to become short boundary notes that point to `rail-kubernetes` as the owning source. 4. Keep accepting the current `-railiance-overlay` pattern during the migration window, even though the long-term direction is toward `rapp-*`. 5. Rehome or delete workload-specific helpers that do not belong in either the substrate repo or the generic Kubernetes rail. 6. Only after `rail-kubernetes` is stable should Railiance add `rail-knative`. This path avoids breaking current users while making the new boundary real. ## Follow-On Path For `rail-knative` `rail-knative` is the first expected follow-on rail, motivated by the need to run `qonto-assistent`. That follow-on rail should: - depend on the Kubernetes substrate being described cleanly first - reuse the top-level Railiance workload and promotion model where that remains sensible - own Knative-specific runtime semantics such as revisions, scale-to-zero, traffic splitting, activator behavior, and event-driven behavior - avoid forcing Knative-specific design choices back into the wave-1 `rail-kubernetes` boundary The test for wave 1 is not "can Railiance describe all future rails now?" The test is "can Railiance separate the Kubernetes base rail cleanly enough that `rail-knative` can be added later without another taxonomy rewrite?" ## Risks To Watch - If `railiance-cluster` keeps generic workload lifecycle ownership, `rail-kubernetes` will be nominal only. - If `rail-kubernetes` absorbs substrate bootstrap and operator ownership, it stops being a rail and becomes a second cluster repo. - If workload-specific helpers are moved unchanged into `rail-kubernetes`, the split will preserve the current ambiguity instead of reducing it. - If `rapp-*` migration is delayed too long, the old overlay naming and the new workload-package model will drift apart. ## Outcome Wave 1 should produce a base Kubernetes rail with a clean contract and a backward-compatible migration path. That is enough to start practical repo separation without pretending the rest of the rail family already exists.