Files
Felis/internal/platform/doc.go
T
flyemoji 47fcd90f75 feat(platform): add node orchestration and the felis entrypoint
The platform package that places servers across nodes and wires the operator, build, restore, and reaper subsystems, plus cmd/felis, the single binary that runs them.
2026-06-26 23:32:38 +09:00

59 lines
3.5 KiB
Go

// Package platform renders the cluster-install objects that bound what every
// Felis identity may do (spec §21, §22): the RBAC for the control-plane
// identities (felis-api and felis-operator always; felis-reaper only when the
// retention reaper is enabled, gated with its CronJob), the weak service
// accounts for the build/restore Jobs, and the minecraft-namespace
// NetworkPolicies that fence server pods. Like internal/build and
// internal/restore, the objects are pure Go-typed values so the security-critical
// shape is unit-tested here, because no cluster runs in this environment.
//
// # Why the RBAC is derived from code, not from the spec prose
//
// The allowlist is built from the actual K8s API call sites in the control
// plane, not transcribed from §21's wording, because the prose is incomplete and
// in places wrong. Concretely, the rendered Roles encode these realities the
// prose misses:
//
// - felis-api creates the restore Job in the minecraft namespace (see
// internal/restore) and the build Job in the build namespace (see
// internal/build), so it needs batch/jobs verbs in BOTH namespaces — not
// just minecraftservers + secrets.
// - The RCON port (25575) is reached by BOTH felis-api (console writes, see
// internal/api.console) AND felis-operator (the readiness prober, see
// internal/operator.prober). The RCON NetworkPolicy peer is therefore
// {api, operator}, not api alone.
// - felis-api ALSO reads pod logs for the read-side console (spec §8 读=pods/log
// follow, see internal/api.logstream): it gets pods:list (to find a server's
// running pod by label) + pods/log:get (to follow it), and deliberately NOT
// pods:get — the streamer never reads a pod's full object. This is the only
// pods grant in the bundle, and it belongs to the api, not the operator.
// - felis-operator uses the manager's CACHED client (informers), so even a
// single Get on a type requires list+watch on it; felis-api and felis-reaper
// use direct clients, so they need only the verbs they literally call.
// - The reaper is a distinct, destructive identity: it deletes
// PersistentVolumeClaims and patches minecraftservers, powers that belong to
// neither the api nor the operator. It gets its own SA and Role.
// - The operator never emits Events, sets finalizers, deletes workloads, or
// touches pods/PVCs, so none of those appear in its Role — and
// minecraftservers/status carries only `update` (it calls Status().Update),
// never get/patch.
//
// # Honesty
//
// The typed render and the §22 invariant tests are Oracle-verifiable here. What
// is NOT verifiable in this toolchain, and is therefore code-complete-but-
// unverified (the same bucket as the K8s E2E):
//
// - that the RBAC actually binds and authorizes on a live apiserver;
// - that the NetworkPolicies are enforced by the cluster CNI (a CNI without
// NetworkPolicy support silently ignores them);
// - the egress_mode=loadbalancer source-IP caveat (spec §20): with MetalLB L2,
// a per-server Service must set externalTrafficPolicy=Local or the client IP
// is SNAT'd and the 25565 ipBlock allowlist will not match the Velocity host.
//
// A Helm-chart wrapper around these objects is intentionally NOT produced: it
// could not be validated here (no helm/kubeconform), and the typed generator
// (RenderYAML, surfaced by `felis manifests`) is the verifiable source of truth a
// chart would only re-encode.
package platform