diff --git a/cmd/felis/update.go b/cmd/felis/update.go index e7405c6..d913ece 100644 --- a/cmd/felis/update.go +++ b/cmd/felis/update.go @@ -260,10 +260,17 @@ func renderApplyGuidance(res updater.Result, selected map[string]bool, force boo // on every run. But setup hands deploy/bootstrap.sh the binary it is itself running // (FELIS_BOOTSTRAP_BINARY), and that arm skips the release lookup entirely, so it // rebuilds the image and rolls the deployment from the SAME binary -- a run that looks - // like a successful update and leaves the version unchanged. Only the installer, which - // resolves and downloads a release, actually moves felis-api. + // like a successful update and leaves the version unchanged. + // + // The installer is the only thing that moves felis-api, but it is NOT an updater and + // must not be recommended as one without this warning. detect_node_ip re-derives + // FELIS_ROOT_DOMAIN on every run and defaults it to .nip.io -- nothing reads + // the domain back out of the felis.toml a previous run wrote. A bare re-run therefore + // rewrites root-domain/panel-hostname/admin-hostname to nip.io names while + // ensure_panel_tls_cert, which is write-once, keeps serving the old ones: the console + // stops matching its own certificate. There is no re-domain flow to recover with. if offeredFelisAPI { - b.WriteString("\nfelis-api (panel, plugins) is the exception: setup re-images it from the felis binary\nalready on this host, so it cannot install a NEWER felis-api. To move to a newer\nrelease, re-run the bootstrap installer (see README).\n") + b.WriteString("\nfelis-api (panel, plugins) is the exception: setup re-images it from the felis binary\nalready on this host, so it cannot install a NEWER felis-api. Only re-running the\nbootstrap installer does that, and it is a full install run, not an update: give it the\nSAME environment as the original install, FELIS_ROOT_DOMAIN above all. It defaults to\n.nip.io, and a bare re-run re-domains this install while the write-once panel\ncertificate keeps the old hostnames.\n") } return b.String() } diff --git a/cmd/felis/update_test.go b/cmd/felis/update_test.go index 01ee167..b12ccd7 100644 --- a/cmd/felis/update_test.go +++ b/cmd/felis/update_test.go @@ -148,6 +148,14 @@ func TestApplyGuidanceScopesTheFelisAPICaveat(t *testing.T) { if !strings.Contains(api, caveat) { t.Fatalf("--panel resolves to felis-api and must carry the caveat:\n%s", api) } + // Naming the installer without naming this is worse than saying nothing: the README + // invocation carries no environment, detect_node_ip then defaults FELIS_ROOT_DOMAIN + // to .nip.io, and the operator re-domains a live install by following our + // own advice. Nothing reads the previous domain back, and the write-once panel cert + // keeps the old hostnames, so there is no recovery path either. + if !strings.Contains(api, "FELIS_ROOT_DOMAIN") { + t.Fatalf("pointing at the installer without the re-domain warning is a footgun:\n%s", api) + } vel := renderApplyGuidance( planResult([]updates.Action{{Component: "velocity", Kind: updates.ActionNotify, LatestKnown: true}}),