Unverified Commit 096d5971 authored by Minseong Choi's avatar Minseong Choi 💬
Browse files

docs(changes): backfill detail docs for pre-ledger functional commits

Retroactively author 15 grouped detail docs covering the backend
functional (feat/fix) commits made before the change ledger was
established (fad48ff2), closing the ledger's detail-doc axis for the
pre-convention history. Each doc groups a feature's constituent commits,
lists their SHAs with subjects, and carries a backfill note stating it
was reconstructed from git history on 2026-07-07 and not independently
re-verified (current tree green at 9911b8cd).

Add a Detail docs section to INDEX.md linking every detail doc (the 6
existing + 15 backfill) to the commit(s) it covers, so a doc is findable
from the index without a column on the auto-generated ledger table. Catch
the table up with the missing 9911b8cd row.

Scope: backend (Go/Java/K8s) only, per the ledger's stated convention
that frontend/panel commits are the collaborator's UI work; non-functional
commits (docs/style/chore/refactor) keep their table row without a
dedicated detail doc.
parent 9911b8cd
Loading
Loading
Loading
Loading
+41 −0
Changes for docs/changes/2026-06-26-foundational-subsystems.md: 41 added lines, 0 removed lines.
Original line number Diff line number Diff line
# Foundational subsystems: the initial Felis import (ledger backfill)

- **Type:** feature (initial import) — retroactive ledger entry
- **Date:** 2026-06-26
- **Area:** `apis/`, `internal/` (naming, rcon, store, config, build, backup, operator,
  submit, api, platform), `cmd/felis`, `plugins/`
- **Commits:**
  - `7fbebfe` feat(apis): MinecraftServer CRD types (v1alpha1) — the lifecycle source of truth (§1)
  - `708cdfc` feat(core): naming, RCON, store (Postgres + embedded migrations), config, image-build libraries
  - `43ab921` feat(backup): archive-based world backup/restore + the retention/idle reaper
  - `78b8cf6` feat(operator): MinecraftServer controller and reconcilers
  - `d39605e` feat(submit): user modpack build + admin-approval pipeline (see [modpack-submission-lane](2026-06-26-modpack-submission-lane.md))
  - `b508fcc` feat(api): dual-faced felis-api — permissions/LuckPerms, modpack lane, admin fleet read
  - `47fcd90` feat(platform): node orchestration + the `cmd/felis` single-binary entrypoint
  - `93f143f` feat(plugins): Velocity proxy + Fabric/Forge/NeoForge/Paper integration mods
- **Tasks:** #23 (permissions), #24 (modpack lane), #25 (fleet read)

## What it did

Stood up the whole backend spine in one build-order sweep: the Kubernetes CRD that is
the lifecycle source of truth, the core libraries (deterministic resource naming, the
RCON client, the Postgres store with embedded SQL migrations, config loading, container
image-build helpers), the backup/restore/reaper subsystems, the operator controller
that drives `MinecraftServer` resources, the user-modpack submit+approval pipeline, the
dual-faced (internal/external) felis-api behind a Zero-Trust guard, the platform
orchestrator that wires it all together under `cmd/felis`, and the server-side
integration plugins.

## Why

This is the project's first functional import — the substrate every later change edits.
It predates the change-ledger convention (established `fad48ff`, 2026-07-06), so it never
got a contemporaneous detail doc; this entry backfills one.

> **Backfill note.** Reconstructed 2026-07-07 from the commit history to close the
> change-ledger's detail-doc axis (§ Convention). This entry deliberately describes only
> what these eight commits **introduced** on 2026-06-26 — the named subsystems have been
> extended and reworked many times since (auth, passkey, metrics, quotas, updates), and
> that later work lives in its own dated detail docs, not here. Not independently
> re-verified for this doc; each subsystem was verified at its original commit and the
> current tree builds green at `9911b8c` (WSL oracle, go1.26.4).
+30 −0
Changes for docs/changes/2026-06-26-modpack-submission-lane.md: 30 added lines, 0 removed lines.
Original line number Diff line number Diff line
# Modpack submission lane: build/approval pipeline + storage backends (ledger backfill)

- **Type:** feature — retroactive ledger entry
- **Date:** 2026-06-26 – 2026-07-02
- **Area:** `internal/submit` (build/approval pipeline, storage backends), `internal/api` (submission endpoints)
- **Commits:**
  - `d39605e` feat(submit): user modpack build + approval pipeline — an uploaded modpack stays `pending_review` and is never built until an admin approves; approval is a single-winner compare-and-swap handing off to the image-build Job, keeping the mandatory vulnerability scan in front of any push
  - `598f3d3` feat(submit): local + S3 backends for modpack upload contexts, installer-selectable
- **Tasks:** #24 (§8 user-submitted modpack approval lane)

## What it did

Built the user-directed extension over the image-build subsystem: a player uploads a
modpack context, it sits in `pending_review`, and an admin's approval is the single-winner
gate that hands off to the build Job — with the vulnerability scan always ahead of any
registry push. `598f3d3` makes the upload-context store pluggable (local filesystem or S3),
selectable at install time.

## Why

Untrusted user content must never build or push unreviewed, and the compare-and-swap
approval guarantees exactly one build per submission even under a double-click or retry.
The storage-backend choice lets a single-node demo use local disk while a real deployment
uses S3, without a code change.

> **Backfill note.** Reconstructed 2026-07-07 from the commit history. The approval
> compare-and-swap and endpoints were unit-tested at their commits; the S3 path is
> integration-configurable. The panel-side submission/approval UI is the collaborator's
> frontend work and is tracked only by its INDEX rows. Not independently re-verified for
> this doc; current tree green at `9911b8c`.
+37 −0
Changes for docs/changes/2026-06-27-cloudflare-tunnel-access-edge.md: 37 added lines, 0 removed lines.
Original line number Diff line number Diff line
# Cloudflare Tunnel + Access edge (cfsetup) + NodePort fencing (ledger backfill)

- **Type:** feature + fix — retroactive ledger entry
- **Date:** 2026-06-27 – 2026-07-01
- **Area:** `internal/cfsetup` (pure core + integration runner), `cmd/felis` (TUI edge flow), edge nftables fence
- **Commits:**
  - `53a7664` feat(cfsetup): recommended Cloudflare Tunnel + Access edge (§14) — domain- and IdP-agnostic; the load-bearing `validateFailClosed` allowlist refuses any policy that could be public; fail-shut 404 catch-all; the raw game host is never proxied
  - `ba13839` feat(breakglass): optional Tunnel + Access setup in the sudo TUI, an independent peer of Owner provisioning
  - `a531f5e` fix(cfsetup): keep the connector install in the host apply layer only (drop the duplicate `StartConnector`)
  - `2810fe8` fix(cfsetup): repoint a stale DNS record when routing a tunnel hostname
  - `7d3be64` feat(cfsetup): start the tunnel connector as a setup step
  - `e058a64` feat(edge): close the panel NodePort to the public after the tunnel is up — nftables at prerouting `raw` (-300), before kube-proxy's NodePort DNAT, gated on the connector actually serving; loopback accepted first so the connector origin hop is untouched
- **Tasks:** #37 (fence panel NodePort to public after tunnel)

## What it did

Stood up the optional one-click Zero-Trust edge: a Cloudflare Tunnel routing only the web
hostnames plus a fail-closed Access application, provisioned from the sudo TUI against the
operator's own Cloudflare account. `e058a64` then closes the Access-bypass hole where a
direct `https://<node-ip>:<nodeport>/` with the right Host header reached the origin
behind Access, by fencing the NodePort at the nftables raw hook so the packet is caught on
its original destination port — but only once the connector is confirmed serving, so
fencing never severs the only web path to a live origin.

## Why

Access is only a security boundary if the origin cannot be reached around it. The
fail-closed policy guard (`validateFailClosed`) and the NodePort fence are the two
load-bearing safety properties: a policy that could be public aborts the run with nothing
created, and a routable-but-unfenced NodePort would defeat the whole edge.

> **Backfill note.** Reconstructed 2026-07-07 from the commit history. The policy guard,
> ingress generation, request bodies, gating, and the nftables ruleset shape / conn-count
> gate are unit-tested; the live cloudflared/Cloudflare-API and `nft` calls are
> INTEGRATION-ONLY (need a real account). KNOWN-LIMITATION: the fence targets nftables;
> firewalld-native coordination is deferred. Not independently re-verified for this doc;
> current tree green at `9911b8c`.
+32 −0
Changes for docs/changes/2026-06-27-console-auth-passwordless.md: 32 added lines, 0 removed lines.
Original line number Diff line number Diff line
# Console auth: local-password login → passwordless migration (ledger backfill)

- **Type:** feature + refactor — retroactive ledger entry
- **Date:** 2026-06-27 – 2026-07-04
- **Area:** `internal/api` (auth handlers, sessions), `internal/store` (users schema)
- **Commits:**
  - `af14f02` feat(api): local-password authentication backend — login/logout/change-password on `op.console`; HttpOnly+Secure+SameSite=Lax host-only server-side sessions (SHA-256, 12h TTL); anti-enumeration uniform bcrypt; JSON-only credential writes (415 otherwise); fails closed unless `local_auth_enabled`
  - `0c1cc59` feat(auth): migrate console login to passwordless
  - `3b43f05` refactor(api): drop the dead login concurrency limiter and reconcile passwordless comments
  - `c20b12c` refactor(api): drop the dead password-era `ResetMailer`, reconcile passkey-unbind docs
- **Tasks:** #27 (B1 thin thread), #79/#80/#81 (residue sweep + primitive adjudication)

## What it did

Shipped the staff local-password door (`af14f02`) as the primary web login when
Zero Trust is not in front of the API, then migrated the console to passwordless
(`0c1cc59`) once email-OTP + passkey were the intended factors. The two refactors
(`3b43f05`, `c20b12c`) then swept the password-era residue — the now-dead login
concurrency limiter and the `ResetMailer` — so no unused password machinery lingered in
the compile path, and reconciled the stale comments that referenced it.

## Why

`op.console` needs a real login even in deployments without a Cloudflare-Access edge; the
password backend was that. Once the passwordless factors landed, keeping the old password
scaffolding around was a bug farm — the sweep is the closeout evidence that the migration
was complete, not half-done.

> **Backfill note.** Reconstructed 2026-07-07 from the commit history. `af14f02` was
> covered by Go unit tests (content-type guard, anti-enumeration, forced-change lockdown)
> at its commit. Not independently re-verified for this doc; current tree green at
> `9911b8c` (WSL oracle, go1.26.4).
+38 −0
Changes for docs/changes/2026-06-27-deploy-bootstrap-installer.md: 38 added lines, 0 removed lines.
Original line number Diff line number Diff line
# Deploy: one-line bootstrap installer + demo bring-up (ledger backfill)

- **Type:** feature + fix — retroactive ledger entry
- **Date:** 2026-06-27 – 2026-07-03
- **Area:** `deploy/` (bootstrap.sh, Dockerfiles, demo-up.sh), image build context
- **Commits:**
  - `58fa4b0` feat(deploy): one-line bootstrap installer + distroless felis image (auto-detects apt/dnf, installs Docker/k3s/PostgreSQL, opens pg_hba to the pod CIDR, runs migrations, applies the control-plane bundle, leaves Web disabled pending `felis setup`)
  - `94a3b7b` fix(deploy): harden bootstrap for RHEL-family Linux
  - `deaa2f8` feat(deploy): zypper support (openSUSE/SLES)
  - `318a724` feat(deploy): pacman support (Arch)
  - `e5f1682` refactor(deploy)!: TUI (breaking walkthrough restructure)
  - `28c3eee` refactor(deploy): improved TUI walkthrough
  - `c14ed17` fix(docker): keep embedded `panel/` and `deploy/` in the image build context
  - `d9e866f` fix(deploy): make the lobby image actually build (re-include `plugins/paper`, build on `gradle:8.14-jdk21`)
  - `b84debf` feat(deploy): one-shot `demo-up.sh` — bootstrap → build/import limbo+lobby images → wire `[velocity]` image refs → `felis setup`, ending in the interactive Owner TUI
- **Tasks:** #26 (Phase A bootstrap verified end-to-end on the Demo VM)

## What it did

Made a bare Linux box a running Felis with one command. `bootstrap.sh` auto-detects the
host package manager across the four major families (apt/dnf/zypper/pacman), installs
whatever is missing (Docker, k3s, PostgreSQL, cloudflared), builds+imports the distroless
felis image, opens `pg_hba` to the pod CIDR, runs migrations, and applies the rendered
control-plane bundle. `demo-up.sh` wraps that plus the login-limbo/lobby image build and
`felis setup` into a single command, stopping only at the Owner-creation TUI it cannot
automate.

## Why

The spec calls for a self-hostable single-node deployment a SysAdmin can stand up without
a Kubernetes background. The package-manager fan-out and the demo wrapper are what make
"one line" true across real distros rather than only on the author's box.

> **Backfill note.** Reconstructed 2026-07-07 from the commit history to close the
> change-ledger's detail-doc axis. `deploy/` is shell + Dockerfiles (not Go-oracle
> verifiable); `d9e866f` records a real build+boot check (limbo `/healthz` 200, lobby
> reaches "Done"). Not independently re-verified for this doc; current tree green at
> `9911b8c`.
Loading