-
26.15-alpha
Pre-releaseAll checks were successfulBuild ISO / build-iso (push) Successful in 17m23sCI / lint (push) Successful in 27sCI / test (push) Successful in 1m2sCI / validate-json (push) Successful in 24sCI / markdown-links (push) Successful in 15sRelease / release (push) Successful in 11m34sreleased this
2026-04-21 19:30:04 +02:00 | 5 commits to main since this releaseFixed
- HTTPS is now opt-in; fresh installs no longer hit unbypassable
SEC_ERROR_BAD_SIGNATURE. Every version since 26.5 shipped a
Caddyfile with a__FURTKA_HOSTNAME__.local { tls internal }site
block, so Caddy auto-generated a self-signed root CA + intermediate- leaf on first boot. That worked for first-time-ever users, but
every reinstall (or second Furtka box on the same LAN) produced a
new CA with the same intermediate CN (Caddy Local Authority - ECC Intermediate— Caddy hardcodes it). Any browser that had ever
trusted an earlier Furtka CA got a cached intermediate with
mismatched keys, then Firefox's cert lookup substituted the cached
intermediate when validating the new box's leaf → the signature
check failed →SEC_ERROR_BAD_SIGNATURE, which Firefox has no
"Advanced → Accept Risk" bypass for.
- Removed the hostname site block from the default Caddyfile.
Fresh installs serve:80only; visitinghttps://furtka.local
now yields a clean connection-refused instead of the crypto
fault. - Added top-level
import /etc/caddy/furtka-https.d/*.caddyfile.
The/settingsHTTPS toggle (viafurtka.https.set_force_https)
now writes TWO snippets atomically — the top-level hostname +
tls internalblock (enables:443) and the:80-scoped
redirect (forces HTTP → HTTPS) — and removes both on disable.
Caddy reloads after the pair-swap; failure rolls both back. - Webinstaller creates
/etc/caddy/furtka-https.d/during
post-install alongside the existingfurtka.d/. updater._refresh_caddyfileruns a 26.14 → 26.15 migration: if
the box already had the redirect snippet on disk (user had
explicitly enabled "Force HTTPS" under the old regime), the
migration also writes the new listener snippet so HTTPS keeps
working across the upgrade.
- leaf on first boot. That worked for first-time-ever users, but
status.force_httpsnow reads the listener snippet, not the
redirect snippet. A lone redirect without a:443listener
wouldn't actually serve HTTPS, so the listener file is the
authoritative "HTTPS is on" signal. The UI on/settingssees the
correct state as a result.
Known remaining UX wart: a browser that trusted a previous Furtka box
still seesBAD_SIGNATUREwhen visiting this box'shttps://after
enabling HTTPS here — the fixed intermediate CN is a Caddy-side
limitation we can't fix from Furtka. Fresh installs on a browser that
never visited another Furtka box work correctly. Workaround:
about:networking#sts→ Forget → clearcert9.db.Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
-
furtka-26.15-alpha.iso
3 downloads · 1.4 GiB
-
furtka-26.15-alpha.tar.gz
5 downloads · 60 KiB
-
furtka-26.15-alpha.tar.gz.sha256
3 downloads · 92 B
-
release.json
2 downloads · 173 B
- HTTPS is now opt-in; fresh installs no longer hit unbypassable
-
26.14-alpha
Pre-releaseAll checks were successfulBuild ISO / build-iso (push) Successful in 17m14sCI / lint (push) Successful in 26sCI / test (push) Successful in 1m2sCI / validate-json (push) Successful in 24sCI / markdown-links (push) Successful in 15sRelease / release (push) Successful in 11m26sreleased this
2026-04-21 18:16:42 +02:00 | 6 commits to main since this releaseFixed
- 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-allfile_serverand was served straight
fromassets/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_wwwhandler
checks the session cookie, redirects to/loginif unauthed, and
reads the HTML fromassets/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_HTMLinapi.py, so they never got the Logout
entry when it was added in 26.11. Both nav bars now include it
plus an inlinedoLogout()that POSTs/logoutand bounces to
/login, matching the pattern in_HTML.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
- Landing page and
-
26.13-alpha
Pre-releaseAll checks were successfulBuild ISO / build-iso (push) Successful in 17m28sCI / lint (push) Successful in 27sCI / test (push) Successful in 59sCI / validate-json (push) Successful in 23sCI / markdown-links (push) Successful in 15sRelease / release (push) Successful in 11m38sreleased this
2026-04-21 17:03:28 +02:00 | 7 commits to main since this releaseFixed
- Upgrade path from pre-auth releases actually works. 26.11-alpha
introducedfrom werkzeug.security import ...infurtka/auth.py,
but werkzeug isn't installed on the target system — core runs as
system Python with stdlib only, andflask>=3.0inpyproject.toml
is never pip-installed on the box. Fresh boxes from the 26.11/26.12
ISO without a manually-installed werkzeug crashed on import; boxes
upgrading from pre-26.11 got double-broken by that plus the health
check below. Replaced the werkzeug dependency with a stdlib-only
furtka/passwd.pythat useshashlib.pbkdf2_hmacfor new hashes
and parses werkzeug'sscrypt:N:r:p$salt$hexformat for backward
compatibility — existingusers.jsonfiles created on the rare
boxes that did have werkzeug keep working after this upgrade, no
re-setup needed.from werkzeug.security import ...is gone from
the import chain entirely;pyproject.toml's flask dep stays only
for the live-ISO webinstaller. - Self-update no longer auto-rolls-back when crossing the auth
boundary.updater._health_checkpinged/api/appsand demanded
a 200, which meant every 26.10 → 26.11+ upgrade hit the post-restart
check, got a 401 (auth guard), and treated that as "server dead"
→ rollback. Now any 2xx–4xx response counts as "server alive"; only
connection-level failures or 5xx fail the check. 5xx still fails
rollback because that means the new process is up but broken. - Install lock closes its race window.
POST /api/apps/install
used to release the fcntl lock immediately after the sync
pre-validation so the systemd-run child could re-acquire it —
leaving a tiny gap where a second POST could slip in, pass the lock
check, and return 202. Both child processes would start, one would
win the in-child lock, the other would die silently. Now the API
also readsinstall-state.jsonand refuses with 409 if the stage
is non-terminal (pulling_image,creating_volumes,
starting_container). The fcntl lock stays as belt-and-suspenders.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
2 downloads
- Upgrade path from pre-auth releases actually works. 26.11-alpha
-
26.12-alpha
Pre-releaseAll checks were successfulBuild ISO / build-iso (push) Successful in 17m24sCI / lint (push) Successful in 26sCI / test (push) Successful in 43sCI / validate-json (push) Successful in 24sCI / markdown-links (push) Successful in 16sRelease / release (push) Successful in 11m34sreleased this
2026-04-21 15:50:49 +02:00 | 8 commits to main since this releaseChanged
- App-Install geht async mit Live-Progress.
POST /api/apps/install
returnt jetzt202 Acceptednach der synchronen Pre-Validation
(Source auflösen, Files kopieren,.envschreiben, Placeholder- und
Path-Checks). Den eigentlichen Docker-Teil (compose pull→ volumes
→compose up) dispatched der Handler alssystemd-run --unit=furtka-install-<app>Hintergrund-Job, der seine Phase in
/var/lib/furtka/install-state.jsonschreibt. Neues
GET /api/apps/install/statusfür UI-Polling. Das Install-Modal
zeigt jetzt live "Image wird heruntergeladen…" →
"Speicherbereiche werden erstellt…" → "Container wird gestartet…"
statt ~30 Sekunden totem "Installing…". Muster 1:1 parallel zu
/api/catalog/sync/applyund/api/furtka/update/apply. Neue CLI-
Subcommandfurtka app install-bg <name>(intern, von der API
aufgerufen);furtka app installfür Terminal-User bleibt synchron.
Die Reinstall-Taste in der App-Liste pollt ebenfalls den
Install-Status und spiegelt die Phase im Button-Text.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
- App-Install geht async mit Live-Progress.
-
26.11-alpha
Pre-releaseAll checks were successfulBuild ISO / build-iso (push) Successful in 17m30sCI / lint (push) Successful in 27sCI / test (push) Successful in 43sCI / validate-json (push) Successful in 31sCI / markdown-links (push) Successful in 15sRelease / release (push) Successful in 11m38sreleased this
2026-04-21 13:01:17 +02:00 | 9 commits to main since this releaseAdded
- Login-auth for the Furtka web UI. Every
/apps,/api/*,/,
and/settings/route now requires a signed-in session. New
/loginpage serves a username/password form;POST /login
validates against/var/lib/furtka/users.json(werkzeug PBKDF2-
hashed), sets afurtka_sessioncookie (HttpOnly,SameSite= Strict, 7-day TTL), and redirects to/apps.POST /logout
revokes the server-side session and clears the cookie.
Unauthenticated HTML requests get a 302 to/login; unauthenticated
API requests get 401 JSON. The old "No authentication on this UI
yet" banner is gone; the/appsheader picks up aLogoutlink
instead. - First-run setup fallback for upgrade-path boxes. Boxes
upgrading from 26.10-alpha have nousers.jsonyet — on the first
visit/loginrenders a setup form (username + password +
password-confirm) that creates the admin record on submit. Fresh
installs skip this: the webinstaller writesusers.jsonduring
the chroot post-install step using the step-1 password, so the
first browser visit after boot goes straight to the login form. - Caddy proxy routes
/loginand/logout.assets/Caddyfile
gets two newhandleblocks in the shared(furtka_routes)
snippet so both the:80block and thehostname.local, hostname
HTTPS block forward the auth endpoints to the stdlib server on
127.0.0.1:7000. Without this Caddy would serve a 404 from the
static file server.
Fixed
tests/test_installer.pyruff-format nit — the 26.10-alpha
release commit had a misformatted list literal that failed
ruff format --check. Caught when the Release page on Forgejo
showed a red CI badge for the tag.pyproject.tomlversion string bumped from the stale 26.8-alpha
to 26.11-alpha. Release pipeline usesGITHUB_REF_NAMEas source
of truth for the artefact name, but having the two agree matters
for local dev runs that readpyproject.toml.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
- Login-auth for the Furtka web UI. Every
-
26.10-alpha
Pre-releasereleased this
2026-04-21 11:48:07 +02:00 | 11 commits to main since this releaseAdded
- Remove-USB-stick hint on the installer's post-install screen.
webinstaller/templates/install/rebooting.htmlnow shows a bold
"Remove the USB stick now" line before the reboot, plus a muted
fallback explaining the BIOS boot-menu keys (F11/F12/Esc) if the
machine boots back into the installer anyway. Caught on the first
bare-metal test (Medion i5-4gen, 2026-04-21) where the box didn't
boot the installed system without manual BIOS-order changes. - New
pathsetting type for app manifests. Apps can now declare a
setting with"type": "path"whose value is an absolute filesystem
path on the host; docker-compose bind-mounts it via the usual.env
substitution (${MEDIA_PATH}:/media). Unlocks media/data-heavy apps
(Jellyfin, later Paperless/Nextcloud/Immich) where the user points at
an existing folder instead of copying everything into a Docker
volume. The install form renders path settings as a plain text input
with a/mnt/…placeholder hint. - Server-side path validation. Both
install_from()and
update_env()refuse values that aren't absolute, don't exist,
aren't directories, or resolve (afterPath.resolve()) into a
system-path deny-list (/,/etc,/root,/boot,/proc,
/sys,/dev,/bin,/sbin,/usr/bin,/usr/sbin,
/var/lib/furtka). Catches/mnt/../etc-style traversal too. Error
messages surface in the existing install/edit modal error line.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
- Remove-USB-stick hint on the installer's post-install screen.
-
26.9-alpha
Pre-releaseAll checks were successfulBuild ISO / build-iso (push) Successful in 20m49sCI / lint (push) Successful in 1m13sCI / test (push) Successful in 48sCI / validate-json (push) Successful in 44sCI / markdown-links (push) Successful in 16sRelease / release (push) Successful in 13m31sreleased this
2026-04-20 18:51:30 +02:00 | 15 commits to main since this releaseFixed
- Landing-page app tiles with an
open_urlnow open in a new tab
(target="_blank" rel="noopener"), matching the Open button
behaviour on/apps. Without this, clicking "Uptime Kuma" on the
home screen replaced Furtka itself with the Kuma admin page.
Internal links (theManage →fallback for apps without an
open_url) still open in the same tab. scripts/publish-release.shno longer fails the whole release when
the ISO upload hits a Forgejo proxy 504. The core tarball + sha256 +
release.json (which running boxes need for self-update) are uploaded
first and the ISO is attempted last as a best-effort; a 504 now logs
a warning and exits 0 so the release page still publishes. Surfaced
by the 26.8-alpha cut: the tarball landed but the ~1 GB ISO upload
timed out at the Forgejo reverse proxy.
Changed
furtka app list --jsonnow mirrors/api/appsfield-for-field —
previously the CLI emitted a slim projection missing
description_long,open_url, andsettings. Anyone piping the
CLI output into jq for automation was seeing an incomplete view.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
- Landing-page app tiles with an
-
26.8-alpha
Pre-releaseSome checks failedBuild ISO / build-iso (push) Successful in 26m56sDeploy site / deploy (push) Successful in 23sCI / lint (push) Successful in 34sCI / test (push) Successful in 1m4sCI / validate-json (push) Successful in 51sCI / markdown-links (push) Successful in 28sRelease / release (push) Failing after 7m38sreleased this
2026-04-20 16:00:52 +02:00 | 16 commits to main since this releaseAdded
- Live-installer ISO attached to the Forgejo release page.
.forgejo/workflows/release.ymlmoves to the self-hosted runner, builds both the self-update tarball and the ISO, andscripts/publish-release.shuploads the ISO as a fourth release asset (furtka-<version>.iso) alongside the existing tarball + sha256 + release.json. Fresh-install users can now grab the ISO from the release page instead of hunting throughbuild-iso.ymlartifact retention windows. ISO build step iscontinue-on-errorso an ISO flake doesn't hold back the core tarball that running boxes need for self-update. - Reboot + Shut down buttons on
/settings. Replaces the two "Coming next" placeholders with real actions backed byPOST /api/furtka/power({"action": "reboot" | "poweroff"}). Handler kicks a delayedsystemd-run --on-active=3s systemctl {reboot|poweroff}so the HTTP response reaches the browser before the kernel loses network. Each button opens a native confirm dialog first (reboot: "back in ~30 s", shut down: "need to press the physical power button"), then the UI swaps to a status line and — after a reboot — polls/furtka.jsonuntil the box is back, reloading the page automatically. No auth (same posture as install/remove). - Manifest
open_urlfield + Open button in/appsand on the landing page. Apps declare a URL template (e.g.smb://{host}/filesfor fileshare,http://{host}:3001/for Uptime Kuma); the UI substitutes{host}with the current browser's hostname at render time so the link follows however the user reached Furtka (furtka.local, raw IP, a future reverse-proxy hostname). The landing page's hardcodedif app.name === 'fileshare'special-case is gone — any app with anopen_urlin its manifest now gets a proper "Open" link. The core seedapps/fileshare/manifest.jsonbumps to v0.1.2 to carry it.
Changed
.btnCSS class introduced so an<a>rendered-as-button lines up with its<button>siblings in.buttons. Needed because "Open" is a real link (middle-click, copy URL, screen readers) and HTML doesn't let<button>carryhref.
Notes
26.7-alphawas tagged but never published — the tag push didn't triggerrelease.yml(Forgejo race with the concurrent main push).26.8-alphasupersedes it and carries the same content plus power actions.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
- Live-installer ISO attached to the Forgejo release page.
-
26.6-alpha
Pre-releaseAll checks were successfulBuild ISO / build-iso (push) Successful in 21m23sCI / lint (push) Successful in 1m31sCI / test (push) Successful in 1m20sCI / validate-json (push) Successful in 48sCI / markdown-links (push) Successful in 27sDeploy site / deploy (push) Successful in 8sRelease / release (push) Successful in 24sreleased this
2026-04-20 14:49:31 +02:00 | 18 commits to main since this releaseAdded
- Apps catalog synced independently of core. A new
daniel/furtka-appsForgejo repo carries the bundled app catalog; running boxes pull the latest release viafurtka-catalog-sync.timer(10 min post-boot + daily, ±6 h jitter) and extract atomically into/var/lib/furtka/catalog/. The resolver now prefers catalog apps over the seed/opt/furtka/current/apps/tree that ships inside the core release tarball, so apps can update without cutting a Furtka core release. Manual trigger: "Sync apps catalog" button on/apps, orsudo furtka catalog syncat the console. Fresh boxes with no network fall back to the seed, so offline first-boot still shows installable apps. Installed apps are never auto-swapped — users click Reinstall in/appsto move an existing install onto a newer catalog version (settings merge-preserved via the existinginstaller.install_frompath). - Catalog CLI:
furtka catalog sync [--check] [--json]+furtka catalog status [--json]. Same shape as the corefurtka updatecommands. - Catalog API endpoints:
POST /api/catalog/sync/check,POST /api/catalog/sync/apply(detached viasystemd-runfor symmetry with/api/furtka/update/apply),GET /api/catalog/status. The existing/api/bundledendpoint keeps working as a backwards-compat alias for/api/apps/available, which now returns the union of catalog + seed apps with a new"source"field on each entry ("catalog"|"bundled").
Changed
furtka._release_commonextracted fromfurtka.updater. Bothupdaterand the newcatalogmodule now share one implementation of the Forgejo-releases-API call, SHA256 verification, path-traversal-guarded tarball extraction, and CalVer comparison. Public updater surface unchanged._link_new_unitsnow auto-enables newly-linked.timerunits. On self-update, a fresh timer file (e.g.furtka-catalog-sync.timeradded in this release) needssystemctl enableto actually start firing — linking alone isn't enough. Fresh installs get their enable via the webinstaller's_FURTKA_UNITSlist as before.
Fixed
- SHA-256 CA fingerprint no longer overflows the
/settingsLocal HTTPS card on narrow viewports..kv ddgrid items now setmin-width: 0+overflow-wrap: anywhereso the colon-separated hex string breaks within the card's right edge instead of pushing past it.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
- Apps catalog synced independently of core. A new
-
26.5-alpha
Pre-releaseSome checks failedBuild ISO / build-iso (push) Successful in 20m10sDeploy site / deploy (push) Successful in 13sCI / lint (push) Failing after 26sCI / test (push) Successful in 33sCI / validate-json (push) Successful in 24sCI / markdown-links (push) Successful in 14sRelease / release (push) Successful in 6sreleased this
2026-04-20 11:52:36 +02:00 | 22 commits to main since this releaseFixed
- HTTPS handshake regression on the installed box (#10). Phase 1 shipped two linked bugs: the
:443 { tls internal }site block had no hostname, so Caddy never issued a leaf cert and every SNI handshake died withSSL_ERROR_INTERNAL_ERROR_ALERT; and bothfurtka.httpsand the Caddyfile's/rootCA.crthandler referenced/var/lib/caddy/.local/share/caddy/pki/…, a path that doesn't exist because our systemd unit setsXDG_DATA_HOME=/var/lib. Force-HTTPS toggle made the brokenness user-visible by redirecting working HTTP to dead HTTPS. Fixed: the Caddyfile now ships a__FURTKA_HOSTNAME__.local, __FURTKA_HOSTNAME__ { tls internal }block with the placeholder substituted at install time (webinstaller/app.py) and on every self-update (furtka.updater._refresh_caddyfilereads/etc/hostname).auto_https disable_redirectskeeps Caddy's built-in redirect out of the way of the/settingstoggle. PKI paths corrected in bothfurtka/https.pyandassets/Caddyfile. Verified end-to-end on the 192.168.178.110 test VM: TLS 1.3 handshake completes, leaf cert issued,/rootCA.crtreturns 200.
Changed
- Wizard footer version is now dynamic.
webinstaller/app.pyresolves the Furtka version at startup via a Flask context processor — reads/opt/furtka/VERSIONon the live ISO (written byiso/build.shfrompyproject.tomlat build time), falls back topyproject.tomlin dev runs, then to literal"dev". The 26.4 footer was hand-pinned and drifted within hours of release; that follow-up item is now closed. - Docs realigned with 26.4-alpha reality.
apps/README.mdadded (manifest schema, volume namespacing,.env.exampleguardrails, SVG sanitiser limits, install/test flow). RootREADME.mdroadmap updated with Phase 1 HTTPS + smoke-VM pipeline as shipped items and 26.4-alpha in the release list.iso/README.mdcorrected: mDNS is wired (not "later milestone"), post-install default URL ishttp://furtka.local(notproksi.local), HTTPS is available viatls internalsince 26.4.website/README.mdnow documents the auto-deploy on push-to-main as the default path, manualdeploy.shas the SSH-hop fallback.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
- HTTPS handshake regression on the installed box (#10). Phase 1 shipped two linked bugs: the