railiance-master/docs/rail-kubernetes-boundary.md
2026-07-25 10:54:37 +02:00

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.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 rapps
  • 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 <app>-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.