-
26.21-alpha
Pre-releaseAll checks were successfulDeploy site / deploy (push) Successful in 5sCI / lint (push) Successful in 30sCI / test (push) Successful in 2m55sCI / validate-json (push) Successful in 33sCI / markdown-links (push) Successful in 21sBuild ISO / build-iso (push) Successful in 22m11sRelease / release (push) Successful in 16m23sreleased this
2026-10-23 16:57:05 +02:00 | 2 commits to main since this releaseAdded
- New logo: Mark M (a lowercase f cut out of an arched gate), winner of
the furtka.org vote (29 of 42 votes). It is now in the website header,
favicon and share card, the installer header and tab icon, the on-box UI
(every page and the login), the console welcome screens of the live ISO
and the installed box, and the BIOS boot-menu splash. Brand files in
docs/brand/, generated bywebsite/tools/brand-assets.py. - furtka.org/download/ (and
/de/download/): ISO download with size,
date and checksum, requirements and step-by-step install guide. Releases
now publishfurtka-<ver>.iso.sha256and refresh the site.
Security
- App names are validated everywhere.
POST /api/apps/removewith
{"name": ".."}or"/"sentshutil.rmtreeat/var/lib/furtkaor the
root filesystem (and wipingusers.jsonreopened first-run admin setup to
the LAN). Names from URLs, JSON bodies and manifests must now match
^[a-z][a-z0-9_-]*$;installer.remove/update_envcheck it too. - Settings can't inject into
.env. Line breaks are refused; values
with$are single-quoted so compose doesn't expand them (pa$wordused
to becomepa). Validation runs before the write, so a rejected/etc
path is no longer saved anyway. - Damaged
users.jsonfails closed. An empty/corrupt file used to read
as "no admin yet" and turned/logininto an open create-admin form.
/loginnow answers 503 with SSH recovery steps;save_usersfsyncs. - API server hardening: threaded (state changes still serialised), 1 MiB
body cap, 30 s socket timeout, cross-site POSTs refused via
Sec-Fetch-Site, JSON 500 instead of dropped connections.
Fixed
-
Installer disk picker: SATA SSDs were labelled "HDD" and lost the
recommendation to bigger spinning disks; type now comes from lsblk
TRAN/ROTA. zram, eMMC boot/RPMB areas, read-only and 0-byte disks are
hidden; disks under 16 GB are greyed out; USB disks rank last; >2 TB disks
on BIOS machines get an MBR warning; the boot stick is also found via the
archiso kernel args whenbootmntis gone. Step 2 only accepts a listed disk. -
Installer runs one install at a time (double-click / second browser
started a second archinstall on the same disk), and a crashed archinstall
(OOM kill, signal) now shows as an error instead of a progress bar stuck
forever. Disk-probe exceptions land in the log instead of a bare 500. -
Failed installs can be retried from the error page without re-entering
everything. -
No 2-minute boot stall on boards with a second NIC: the target gets a
systemd-networkd-wait-online --anydrop-in. -
Self-update: symlink flip is atomic; any error after the flip rolls
back (and restores the previous Caddyfile); health check survives read
timeouts; every failure ends in anerrorstage the settings page shows. -
Interrupted app installs no longer block all later installs — a stale
state with no live runner is marked failed after 60 s. -
Catalog sync / update checks no longer strand on socket resets or disk
full. -
Apps page after an API restart redirects to
/logininstead of
showing an empty app list. -
32-bit UEFI machines (Atom Bay Trail / Cherry Trail) get Limine
instead of systemd-boot: Arch has no ia32 systemd-boot and archinstall's
GRUB path hardcodesx86_64-efi, so these boxes installed but didn't boot. -
Force-HTTPS redirect only covers
<hostname>.local/<hostname>and
is a 302. The old 301 for every host sent devices that use the IP to a
TLS handshake that can't succeed, and stayed cached after HTTPS was
turned off. Self-update migrates the old snippet. -
Self-update starts newly added timers immediately and links units
throughcurrent._link_new_units()enabled a timer a release
introduced but never started it, so after the 26.19 → 26.20 update the
bench sat withfurtka-welcome.timerinactive (dead)— and the console
banner without its IP line — until the next reboot. It also linked the
unit from/opt/furtka/versions/<ver>/…instead of/opt/furtka/current/…,
so the link would have stopped following later updates and rollbacks
(installer-created links usecurrent). Now: link viacurrent, enable,
start.
Changed
- Wizard wording: "Server name" with a live
http://<name>.localpreview,
"Pick a disk" with an explicit erase warning, readable confirm table,
password hint explains it's also the SSH/console password, names are
lowercased instead of rejected, system account names are refused. - Live console banner says a network cable with internet is required.
- ISO builds pin archinstall (4.4-1, sha256-checked from the Arch archive)
instead of taking whatever the repos ship that day; the build fails
clearly when Arch's Python moves on. Bump steps iniso/README.md. - On-box UI polish: sticky top nav, bordered cards and status tiles, app
tile hover lift, brand-red section markers; animations respect
prefers-reduced-motion.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
-
furtka-26.21-alpha.iso
4 downloads · 1.5 GiB
-
furtka-26.21-alpha.iso.sha256
4 downloads · 89 B
-
furtka-26.21-alpha.tar.gz
1 download · 88 KiB
-
furtka-26.21-alpha.tar.gz.sha256
1 download · 92 B
-
release.json
1 download · 173 B
- New logo: Mark M (a lowercase f cut out of an arched gate), winner of
-
26.20-alpha
Pre-releaseAll checks were successfulCI / lint (pull_request) Successful in 24sCI / test (pull_request) Successful in 1m19sCI / validate-json (pull_request) Successful in 21sCI / markdown-links (pull_request) Successful in 14sBuild ISO / build-iso (push) Successful in 18m15sDeploy site / deploy (push) Successful in 4sCI / lint (push) Successful in 23sCI / test (push) Successful in 1m17sCI / validate-json (push) Successful in 21sCI / markdown-links (push) Successful in 13sRelease / release (push) Successful in 12m2sreleased this
2026-08-25 17:12:48 +02:00 | 43 commits to main since this releaseFirst release shaped by a real-hardware install (old laptop mainboard, BIOS boot,
SSD with a previous Proxmox VE on it). All four fixes are things an empty
Proxmox VM never shows.Fixed
- First catalog sync no longer dies silently at boot. On dual-stack
LANsnetwork-online.targetis satisfied by the IPv6 SLAAC address a
second before DHCPv4 lands, sofurtka-catalog-sync.serviceran into
Network is unreachable, exited, and the next attempt was the daily timer
— up to 6 h of jitter later. Meanwhilecatalog-state.jsonstayed on
checkingand the apps page showed "checking…" with nothing running
(first hardware bench, 2026-08-25). The service now retries on failure
(every 20 s, up to 8 times in 15 min), a failed sync is recorded as stage
errorwith the message, and the apps page shows "last sync failed: …"
instead of a fake in-progress state. - Installer frees the target disk before partitioning. The live ISO
auto-activates whatever an attached drive carries (LVM volume groups, md
arrays, swap) — the first hardware bench's SSD still had a Proxmox VE
pveVG on it — and archinstall then failed at 12 % withunable to inform the kernel of the change, probably because it/they are in use.
install_runnow runsdiskprep.release_disk()first: swapoff/umount
leaves-first,dmsetup remove/mdadm --stopfor every dm/md node
stacked on the disk,wipefs -aon partitions + disk, then
blockdev --rereadpt. Every step is written to the top of the install
log. The progress page also names this failure explicitly instead of a
bare "hit a snag" if it does still happen. - Console banner always shows the IP fallback. On both the live ISO and
the installed system the/etc/issuewelcome was written once, after
network-online.target, and simply omitted thehttp://<ip>line when no
address existed yet — so on a slow-DHCP NIC (first hardware bench,
2026-08-24) the screen only ever offeredproksi.local. The banner script
now runs from a timer (furtka-issue.timeron the ISO,
furtka-welcome.timeron the installed box, every 5 s) and always prints
a fallback line: every global IPv4 address once there is one, or a
"no IP address yet — check the cable / DHCP" hint until then. It only
rewrites/etc/issueand nudges agetty when the text changed, so the
login prompt doesn't flicker. The live ISO no longer auto-logsrootin
on tty1 (releng default) — that handed the console to a shell before the
banner could update; tty1 now stays at the login prompt with a live
banner, and the banner tells you a shell isroot+ Enter away. Boxes
that self-update get the new timer linked + enabled automatically; the
refresh loop takes effect after their next reboot.
Downloads
-
Source code (ZIP)
3 downloads
-
Source code (TAR.GZ)
3 downloads
-
furtka-26.20-alpha.iso
14 downloads · 1.5 GiB
-
furtka-26.20-alpha.tar.gz
6 downloads · 73 KiB
-
furtka-26.20-alpha.tar.gz.sha256
18 downloads · 92 B
-
release.json
18 downloads · 173 B
- First catalog sync no longer dies silently at boot. On dual-stack
-
26.19-alpha
Pre-releaseAll checks were successfulBuild ISO / build-iso (push) Successful in 19m56sDeploy site / deploy (push) Successful in 4sCI / lint (push) Successful in 23sCI / test (push) Successful in 1m18sCI / validate-json (push) Successful in 21sCI / markdown-links (push) Successful in 13sRelease / release (push) Successful in 12m36sreleased this
2026-08-23 16:05:43 +02:00 | 48 commits to main since this releaseFixed
- App installs now copy subdirectories, so dependency hooks actually
ship.install_from()only copied an app's top-level files, so the
scripts/folder — where a provider'son_install/on_startdependency
hooks live — never reached/var/lib/furtka/apps/<app>/, and every hooked
dependency failed at reconcile withhook ... missing in provider. The
whole app folder is copied now (rmtree+copytree, so a reinstall also
drops files that were removed upstream). Found during the first real
end-to-end run of the mosquitto + zigbee2mqtt pair on a test VM. furtka app install <name>now runson_installhooks. Named CLI
installs copied the app folders and then ran a bare reconcile, which fires
on_startbut neveron_install— so a CLI install of a consumer brought
its provider up without ever provisioning it (no account created, empty
MQTT_*values in the consumer's.env). Named installs now go through
install_runner.run_install(writing the plan file first), the same docker
phase the API dispatches, so providers come up before consumers and
on_installhooks run. Path-based installs keep the copy + reconcile
dev/test path.
Downloads
-
Source code (ZIP)
3 downloads
-
Source code (TAR.GZ)
4 downloads
-
furtka-26.19-alpha.iso
9 downloads · 1.5 GiB
-
furtka-26.19-alpha.tar.gz
4 downloads · 72 KiB
-
furtka-26.19-alpha.tar.gz.sha256
15 downloads · 92 B
-
release.json
12 downloads · 173 B
- App installs now copy subdirectories, so dependency hooks actually
-
26.18-alpha
Pre-releaseAll checks were successfulBuild ISO / build-iso (push) Successful in 18m44sDeploy site / deploy (push) Successful in 4sCI / lint (push) Successful in 28sCI / test (push) Successful in 1m28sCI / validate-json (push) Successful in 24sCI / markdown-links (push) Successful in 14sRelease / release (push) Successful in 12m5sreleased this
2026-06-04 16:39:04 +02:00 | 52 commits to main since this releaseFixed
on_startdependency hooks now receive the consumer's stored
credentials. Previously the reconciler handed anon_starthook only
FURTKA_CONSUMER_APP/FURTKA_CONSUMER_VERSION, so it had no way to learn
the consumer's existing secrets — which made the feature's own headline use
case (re-create a provider account, e.g. an MQTT user, after a wipe, with
the same password the consumer already holds) impossible without the
provider stashing a copy itself. The hook now also gets the consumer's.env
values, namespaced underFURTKA_CONSUMER_ENV_<KEY>(only UPPER_SNAKE_CASE
keys, so a hand-edited.envcan't produce a malformed--envargument).
on_startstays read-only with respect to the consumer: unlikeon_install,
its stdout is intentionally not merged back into the consumer's.env— it
reads consumer state to reconcile provider state, it doesn't mutate it.
Surfaced by building the first real provider/consumer catalog pair
(mosquitto + zigbee2mqtt) in daniel/furtka-apps.
Downloads
-
Source code (ZIP)
20 downloads
-
Source code (TAR.GZ)
16 downloads
-
furtka-26.18-alpha.iso
19 downloads · 1.5 GiB
-
furtka-26.18-alpha.tar.gz
19 downloads · 72 KiB
-
furtka-26.18-alpha.tar.gz.sha256
35 downloads · 92 B
-
release.json
23 downloads · 173 B
-
26.17-alpha
Pre-releaseAll checks were successfulBuild ISO / build-iso (push) Successful in 18m3sCI / lint (push) Successful in 27sCI / test (push) Successful in 1m22sCI / validate-json (push) Successful in 25sCI / markdown-links (push) Successful in 13sRelease / release (push) Successful in 12m13sreleased this
2026-05-11 20:10:04 +02:00 | 54 commits to main since this releaseAdded
- App-to-app dependencies. Manifests gain an optional
requires
array; each entry names a provider app plus two optional hook scripts
that live in the provider's folder.on_installruns once via
docker compose execagainst the provider's running container while
the consumer is being installed (use case:mosquitto_passwda new
MQTT user for the consumer).on_startruns every boot during
reconcile, before the consumer's container starts (use case: make
sure the user still exists after a Mosquitto wipe). Hook stdout
parses asKEY=VALUElines and optionalFURTKA_JSON: {…}sentinel
lines, both validated against the existingSETTING_NAMEregex; the
values get merged into the consumer's.env(hook wins on conflict)
and the placeholder-secret check runs again over the merged file so
a hook returningMQTT_PASS=changemeis refused the same way an
unedited.env.exampleis. POST /api/apps/install/plan. New read-only endpoint that
returns the topo-sorted install order for a target app plus per-app
summaries (display_name, version, has_settings, installed flag). The
catalog UI calls this before opening the settings dialog so it can
show a confirm modal — "Installing zigbee2mqtt also installs
Mosquitto" — before anything mutates. Circular dependencies surface
as400 {error: "circular dependency: A -> B -> A"}; missing
providers as400 {error: "required app 'X' not found …"}./var/lib/furtka/install-plan.json(overridable via
FURTKA_INSTALL_PLAN). The HTTP install endpoint writes this before
it spawns the systemd-run background job so the runner knows the
full chain to pull → create volumes → fire hooks →compose upfor
in plan order. The runner consumes the file after reading so a stale
plan from a previous install can't accidentally steer the next one.
Changed
furtka reconcilenow visits apps in dependency order, not
alphabetical. Topo-sort overrequiresputs providers before
consumers so a consumer'son_starthook can talk to an already-up
provider. Within a tier, ties stay alphabetical so boot logs are
still deterministic across reboots. Apps with unresolvablerequires
(missing provider) are visited last; the per-app error-isolation in
reconcile then catches them without aborting the whole sweep.POST /api/apps/installrequiresconfirm_dependencies: true
when installing a named app would pull in transitive providers.
Without the flag, the endpoint returns409plus the full plan body
so the UI can render the confirm dialog without a second round-trip.
Lone-target installs (no transitive deps) keep the existing
one-click flow — no UX change forfileshare-style standalone apps.furtka app install <name>and the web UI now install transitive
dependencies automatically.furtka app install /path/to/dir
stays as today (single-app, dev/test workflow).compose_execandcompose_exec_scripthelpers in
furtka/dockerops.py. Both pass-T(no TTY) so they work from the
install runner and from reconcile; both raiseDockerErroron
non-zero exit or timeout.compose_exec_scriptstreams the script
body via stdin tosh -sso hooks don't need to be baked into the
provider's container image.
Notes
- Hook target service: v1 auto-picks the first service in the
provider's compose config. Works for Mosquitto, Postgres, Redis.
Multi-service providers (Authentik server+worker) will need an
optionalservicefield on the requirement entry; deferred until a
real case lands. - Hook timeouts:
on_install60 s,on_start30 s. Hardcoded for
v1 — revisit if a DB seed legitimately needs longer. - Removing an app is now blocked (
409 {dependents: […]}from the
API, exit 2 from the CLI) when other installed apps require it.
Downloads
-
Source code (ZIP)
16 downloads
-
Source code (TAR.GZ)
15 downloads
-
furtka-26.17-alpha.iso
17 downloads · 1.4 GiB
-
furtka-26.17-alpha.tar.gz
15 downloads · 72 KiB
-
furtka-26.17-alpha.tar.gz.sha256
26 downloads · 92 B
-
release.json
20 downloads · 173 B
- App-to-app dependencies. Manifests gain an optional
-
26.16-alpha
Pre-releaseAll checks were successfulBuild ISO / build-iso (push) Successful in 18m12sDeploy site / deploy (push) Successful in 3sCI / lint (push) Successful in 28sCI / test (push) Successful in 1m21sCI / validate-json (push) Successful in 24sCI / markdown-links (push) Successful in 13sRelease / release (push) Successful in 12m13sreleased this
2026-05-10 12:59:30 +02:00 | 56 commits to main since this releaseAdded
- Failed-login rate limit on
/login. A new in-memory
LoginAttemptsstore infurtka/auth.pyblocks brute-force attempts
after 10 failures in 15 minutes from the same (username, IP) pair,
with a 15-minute lockout. Successful logins clear the counter; a
systemctl restart furtkaclears any stuck lockout — fine for an
alpha single-user box. Tuple-keying means a flood from one source IP
can't lock the admin out from elsewhere; an attacker can rotate IPs
to keep probing forever, but each attempt still eats the PBKDF2 cost.
Locked attempts get aRetry-Afterheader so the UI can render the
cooldown. - Live-ISO boot USB is filtered out of the install drive picker. On
bare-metal installs,lsblkreports the USB stick the live ISO
booted from asTYPE=disk, so it showed up in the picker alongside
the real install target — a user could in theory pick the USB they
had just booted from.webinstaller/drives.pynow resolves
/run/archiso/bootmntviafindmnt, walks it up to its parent disk
vialsblk -no PKNAME, and drops that disk before scoring. On a
normal (non-live) box/run/archiso/bootmntdoes not exist and the
picker is unchanged.
Changed
- furtka.org homepage rebuild. Adopted the visual feel of Pascal's
prototype while keeping Furtka's voice, brand palette, and bilingual
structure: Three.js wireframe torus-knot behind the hero (color +
opacity tied to the existing--accentCSS var so light and dark
modes share one scene), scroll-driven camera zoom + tilt, GSAP +
ScrollTrigger card reveals, Lenis smooth scroll, gradient wordmark,
drop-shadow glow in dark mode, and a pulsing CTA pointing at
/releases. "What works today" / "What's coming next" lists moved
from markdown bullets into front-matter arrays and now render as
scroll-reveal cards. All vendor JS (Three.js r128, GSAP 3.12.2 +
ScrollTrigger, Lenis 1.0.33) is vendored locally under
website/assets/js/vendor/, fingerprinted with SRI, gated to the
homepage only, deferred so first paint isn't blocked, and
early-returned onprefers-reduced-motion. - Static-asset gzip on the furtka.org nginx (config only — needs a
deploy on forge-runner-01). Default nginx only gzipstext/html,
so the homepage HTML was the only asset coming back compressed. The
~600 KBthree.min.jsbundle (and the hashed CSS) were being shipped
uncompressed across the public openresty proxy.gzip_typesin
ops/nginx/furtka.org.confnow covers css/js/json/xml/svg/woff2.
Needssudo ops/nginx/setup-vm.shon forge-runner-01 to take effect
— the site-deploy workflow only rebuilds Hugo, it doesn't touch the
nginx config.
Downloads
-
Source code (ZIP)
19 downloads
-
Source code (TAR.GZ)
14 downloads
-
furtka-26.16-alpha.iso
21 downloads · 1.4 GiB
-
furtka-26.16-alpha.tar.gz
17 downloads · 61 KiB
-
furtka-26.16-alpha.tar.gz.sha256
38 downloads · 92 B
-
release.json
31 downloads · 173 B
- Failed-login rate limit on
-
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 | 62 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)
13 downloads
-
Source code (TAR.GZ)
15 downloads
-
furtka-26.15-alpha.iso
18 downloads · 1.4 GiB
-
furtka-26.15-alpha.tar.gz
20 downloads · 60 KiB
-
furtka-26.15-alpha.tar.gz.sha256
36 downloads · 92 B
-
release.json
24 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 | 63 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)
11 downloads
-
Source code (TAR.GZ)
19 downloads
- 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 | 64 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)
14 downloads
-
Source code (TAR.GZ)
14 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 | 65 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)
12 downloads
-
Source code (TAR.GZ)
13 downloads
- App-Install geht async mit Live-Progress.