8.7 KiB
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.mddocs/app-toml-contract.mddocs/overlay-repo-pattern.mdschemas/railiance-app.schema.jsonexamples/railiance/app.tomlbin/railiancetools/cmd/railiance-runtools/cmd/railiance-stage2tools/cmd/railiance-stage3tools/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-kubernetesis the default base rail for wave 1.railiance-clusterremains the S2 owner of the Kubernetes substrate.rail-kubernetesowns the generic workload-on-Kubernetes execution contract.- Workload-specific helpers currently living in
railiance-clusterare 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.tomlcontract 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
rapps - the generic repo scaffold for Kubernetes-targeting wrappers
- compatibility guidance for how a
rappdeclares Kubernetes namespaces, releases, probes, ingress, routing, and rollback - the documented compatibility path from today's overlay pattern into future
rapp-*repos
In short:
railiance-clusteranswers "is the Kubernetes substrate present and healthy?"rail-kubernetesanswers "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-robustnessadmin-sync-smoke- workload-specific runtime reconcile flows tied to
activity-coreor 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
isolatedand optionalweighted - 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.
- Create
rail-kuberneteswith the copied contract docs, schema, examples, and generic Stage 1-3 tooling now living inrailiance-cluster. - Leave compatibility shims in
railiance-clusterfor one migration window. The cluster repo may keep a thinbin/railiancewrapper that delegates the generic lifecycle commands torail-kubernetes. - Update the moved docs in
railiance-clusterto become short boundary notes that point torail-kubernetesas the owning source. - Keep accepting the current
<app>-railiance-overlaypattern during the migration window, even though the long-term direction is towardrapp-*. - Rehome or delete workload-specific helpers that do not belong in either the substrate repo or the generic Kubernetes rail.
- Only after
rail-kubernetesis stable should Railiance addrail-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-kubernetesboundary
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-clusterkeeps generic workload lifecycle ownership,rail-kuberneteswill be nominal only. - If
rail-kubernetesabsorbs 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.