diff --git a/CHANGELOG.md b/CHANGELOG.md
index 90da56b..e1c72d3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,34 @@ This project uses calendar versioning: `YY.N-stage` (e.g. `26.0-alpha` = 2026, r
## [Unreleased]
+## [26.14-alpha] - 2026-04-21
+
+### Fixed
+
+- **Landing page and `/settings/` were silently bypassing the auth
+ guard.** Since 26.11 shipped login, the Caddyfile only
+ reverse-proxied `/api/*`, `/apps*`, `/login*`, and `/logout*` to
+ Python. Everything else — including `/` and `/settings/` — fell
+ through to Caddy's catch-all `file_server` and was served straight
+ from `assets/www/` without ever hitting the session check. The
+ effect: a LAN visitor saw the box's hostname, IP, Furtka version,
+ and the buttons for Update-now / Reboot / HTTPS-toggle. The API
+ calls those buttons fired were all 401-auth-gated so actions didn't
+ land, but the information leak and the "looks open" UX was a real
+ bug. Caught in the 26.13 SSH test session when the user noticed
+ Logout only showed up on `/apps`. Now Caddy routes `/` and
+ `/settings*` through Python; a new `_serve_static_www` handler
+ checks the session cookie, redirects to `/login` if unauthed, and
+ reads the HTML from `assets/www/` otherwise. Catch-all still
+ serves `/style.css`, `/rootCA.crt`, and the runtime JSON files
+ publicly — those don't need auth.
+- **Logout link now shows on every authed page, not just `/apps`.**
+ The static HTML for `/` and `/settings/` maintained their own nav
+ separate from `_HTML` in `api.py`, so they never got the Logout
+ entry when it was added in 26.11. Both nav bars now include it
+ plus an inline `doLogout()` that POSTs `/logout` and bounces to
+ `/login`, matching the pattern in `_HTML`.
+
## [26.13-alpha] - 2026-04-21
### Fixed
@@ -279,7 +307,8 @@ First tagged snapshot. Pre-alpha — the installer does not yet boot, but the de
- **Containers:** Docker + Compose
- **License:** AGPL-3.0
-[Unreleased]: https://forgejo.sourcegate.online/daniel/furtka/compare/26.13-alpha...HEAD
+[Unreleased]: https://forgejo.sourcegate.online/daniel/furtka/compare/26.14-alpha...HEAD
+[26.14-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.14-alpha
[26.13-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.13-alpha
[26.12-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.12-alpha
[26.11-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.11-alpha
diff --git a/assets/Caddyfile b/assets/Caddyfile
index 36cc8ea..b9437af 100644
--- a/assets/Caddyfile
+++ b/assets/Caddyfile
@@ -41,6 +41,20 @@
handle /logout* {
reverse_proxy localhost:7000
}
+ # /settings and / — these previously served as static HTML straight
+ # from the catch-all file_server, which meant the auth-guard was
+ # bypassed: a LAN visitor could see the box's version, IP, and
+ # reach the Update-now / Reboot buttons (the API calls behind them
+ # are auth-gated, but the page itself rendered without a redirect
+ # to /login). Route them through the Python handler which checks
+ # the session cookie and either serves the static HTML from
+ # assets/www/ or redirects to /login.
+ handle /settings* {
+ reverse_proxy localhost:7000
+ }
+ handle / {
+ reverse_proxy localhost:7000
+ }
# Runtime JSON lives under /var/lib/furtka/ so it survives self-updates
# (which only swap /opt/furtka/current).
handle /status.json {
diff --git a/assets/www/index.html b/assets/www/index.html
index 6bf3476..66e5e2c 100644
--- a/assets/www/index.html
+++ b/assets/www/index.html
@@ -14,6 +14,7 @@
Home
Apps
Settings
+ Logout