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

200 lines
8.7 KiB
Markdown

# 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 `<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.