• 26.15-alpha e68ed279cc

    26.15-alpha
    All checks were successful
    Build ISO / build-iso (push) Successful in 17m23s
    CI / lint (push) Successful in 27s
    CI / test (push) Successful in 1m2s
    CI / validate-json (push) Successful in 24s
    CI / markdown-links (push) Successful in 15s
    Release / release (push) Successful in 11m34s
    Pre-release

    daniel released this 2026-04-21 19:30:04 +02:00 | 5 commits to main since this release

    Fixed

    • 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 :80 only; visiting https://furtka.local
        now yields a clean connection-refused instead of the crypto
        fault.
      • Added top-level import /etc/caddy/furtka-https.d/*.caddyfile.
        The /settings HTTPS toggle (via furtka.https.set_force_https)
        now writes TWO snippets atomically — the top-level hostname +
        tls internal block (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 existing furtka.d/.
      • updater._refresh_caddyfile runs 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.
    • status.force_https now reads the listener snippet, not the
      redirect snippet.
      A lone redirect without a :443 listener
      wouldn't actually serve HTTPS, so the listener file is the
      authoritative "HTTPS is on" signal. The UI on /settings sees the
      correct state as a result.

    Known remaining UX wart: a browser that trusted a previous Furtka box
    still sees BAD_SIGNATURE when visiting this box's https:// 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 → clear cert9.db.

    Downloads
  • 26.14-alpha 26f0424ae3

    26.14-alpha
    All checks were successful
    Build ISO / build-iso (push) Successful in 17m14s
    CI / lint (push) Successful in 26s
    CI / test (push) Successful in 1m2s
    CI / validate-json (push) Successful in 24s
    CI / markdown-links (push) Successful in 15s
    Release / release (push) Successful in 11m26s
    Pre-release

    daniel released this 2026-04-21 18:16:42 +02:00 | 6 commits to main since this release

    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.
    Downloads
  • 26.13-alpha 8c1fd1da2b

    26.13-alpha
    All checks were successful
    Build ISO / build-iso (push) Successful in 17m28s
    CI / lint (push) Successful in 27s
    CI / test (push) Successful in 59s
    CI / validate-json (push) Successful in 23s
    CI / markdown-links (push) Successful in 15s
    Release / release (push) Successful in 11m38s
    Pre-release

    daniel released this 2026-04-21 17:03:28 +02:00 | 7 commits to main since this release

    Fixed

    • Upgrade path from pre-auth releases actually works. 26.11-alpha
      introduced from werkzeug.security import ... in furtka/auth.py,
      but werkzeug isn't installed on the target system — core runs as
      system Python with stdlib only, and flask>=3.0 in pyproject.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.py that uses hashlib.pbkdf2_hmac for new hashes
      and parses werkzeug's scrypt:N:r:p$salt$hex format for backward
      compatibility — existing users.json files 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_check pinged /api/apps and 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 reads install-state.json and refuses with 409 if the stage
      is non-terminal (pulling_image, creating_volumes,
      starting_container). The fcntl lock stays as belt-and-suspenders.
    Downloads
  • 26.12-alpha f3cd9e963c

    26.12-alpha
    All checks were successful
    Build ISO / build-iso (push) Successful in 17m24s
    CI / lint (push) Successful in 26s
    CI / test (push) Successful in 43s
    CI / validate-json (push) Successful in 24s
    CI / markdown-links (push) Successful in 16s
    Release / release (push) Successful in 11m34s
    Pre-release

    daniel released this 2026-04-21 15:50:49 +02:00 | 8 commits to main since this release

    Changed

    • App-Install geht async mit Live-Progress. POST /api/apps/install
      returnt jetzt 202 Accepted nach der synchronen Pre-Validation
      (Source auflösen, Files kopieren, .env schreiben, Placeholder- und
      Path-Checks). Den eigentlichen Docker-Teil (compose pull → volumes
      compose up) dispatched der Handler als systemd-run --unit=furtka-install-<app> Hintergrund-Job, der seine Phase in
      /var/lib/furtka/install-state.json schreibt. Neues
      GET /api/apps/install/status fü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/apply und /api/furtka/update/apply. Neue CLI-
      Subcommand furtka app install-bg <name> (intern, von der API
      aufgerufen); furtka app install fü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
  • 26.11-alpha 470823b347

    26.11-alpha
    All checks were successful
    Build ISO / build-iso (push) Successful in 17m30s
    CI / lint (push) Successful in 27s
    CI / test (push) Successful in 43s
    CI / validate-json (push) Successful in 31s
    CI / markdown-links (push) Successful in 15s
    Release / release (push) Successful in 11m38s
    Pre-release

    daniel released this 2026-04-21 13:01:17 +02:00 | 9 commits to main since this release

    Added

    • Login-auth for the Furtka web UI. Every /apps, /api/*, /,
      and /settings/ route now requires a signed-in session. New
      /login page serves a username/password form; POST /login
      validates against /var/lib/furtka/users.json (werkzeug PBKDF2-
      hashed), sets a furtka_session cookie (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 /apps header picks up a Logout link
      instead.
    • First-run setup fallback for upgrade-path boxes. Boxes
      upgrading from 26.10-alpha have no users.json yet — on the first
      visit /login renders a setup form (username + password +
      password-confirm) that creates the admin record on submit. Fresh
      installs skip this: the webinstaller writes users.json during
      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 /login and /logout. assets/Caddyfile
      gets two new handle blocks in the shared (furtka_routes)
      snippet so both the :80 block and the hostname.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.py ruff-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.toml version string bumped from the stale 26.8-alpha
      to 26.11-alpha. Release pipeline uses GITHUB_REF_NAME as source
      of truth for the artefact name, but having the two agree matters
      for local dev runs that read pyproject.toml.
    Downloads
  • 26.10-alpha e8c5317660

    26.10-alpha
    Some checks failed
    CI / lint (push) Failing after 50s
    CI / test (push) Successful in 1m6s
    CI / validate-json (push) Successful in 42s
    CI / markdown-links (push) Successful in 22s
    Release / release (push) Successful in 13m27s
    Pre-release

    daniel released this 2026-04-21 11:48:07 +02:00 | 11 commits to main since this release

    Added

    • Remove-USB-stick hint on the installer's post-install screen.
      webinstaller/templates/install/rebooting.html now 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 path setting 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 (after Path.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
  • 26.9-alpha c7e7c8b1e5

    26.9-alpha
    All checks were successful
    Build ISO / build-iso (push) Successful in 20m49s
    CI / lint (push) Successful in 1m13s
    CI / test (push) Successful in 48s
    CI / validate-json (push) Successful in 44s
    CI / markdown-links (push) Successful in 16s
    Release / release (push) Successful in 13m31s
    Pre-release

    daniel released this 2026-04-20 18:51:30 +02:00 | 15 commits to main since this release

    Fixed

    • Landing-page app tiles with an open_url now 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 (the Manage → fallback for apps without an
      open_url) still open in the same tab.
    • scripts/publish-release.sh no 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 --json now mirrors /api/apps field-for-field —
      previously the CLI emitted a slim projection missing
      description_long, open_url, and settings. Anyone piping the
      CLI output into jq for automation was seeing an incomplete view.
    Downloads
  • 26.8-alpha cf93ef44cb

    26.8-alpha
    Some checks failed
    Build ISO / build-iso (push) Successful in 26m56s
    Deploy site / deploy (push) Successful in 23s
    CI / lint (push) Successful in 34s
    CI / test (push) Successful in 1m4s
    CI / validate-json (push) Successful in 51s
    CI / markdown-links (push) Successful in 28s
    Release / release (push) Failing after 7m38s
    Pre-release

    daniel released this 2026-04-20 16:00:52 +02:00 | 16 commits to main since this release

    Added

    • Live-installer ISO attached to the Forgejo release page. .forgejo/workflows/release.yml moves to the self-hosted runner, builds both the self-update tarball and the ISO, and scripts/publish-release.sh uploads 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 through build-iso.yml artifact retention windows. ISO build step is continue-on-error so 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 by POST /api/furtka/power ({"action": "reboot" | "poweroff"}). Handler kicks a delayed systemd-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.json until the box is back, reloading the page automatically. No auth (same posture as install/remove).
    • Manifest open_url field + Open button in /apps and on the landing page. Apps declare a URL template (e.g. smb://{host}/files for 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 hardcoded if app.name === 'fileshare' special-case is gone — any app with an open_url in its manifest now gets a proper "Open" link. The core seed apps/fileshare/manifest.json bumps to v0.1.2 to carry it.

    Changed

    • .btn CSS 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> carry href.

    Notes

    • 26.7-alpha was tagged but never published — the tag push didn't trigger release.yml (Forgejo race with the concurrent main push). 26.8-alpha supersedes it and carries the same content plus power actions.
    Downloads
  • 26.6-alpha 018f2e20b0

    26.6-alpha
    All checks were successful
    Build ISO / build-iso (push) Successful in 21m23s
    CI / lint (push) Successful in 1m31s
    CI / test (push) Successful in 1m20s
    CI / validate-json (push) Successful in 48s
    CI / markdown-links (push) Successful in 27s
    Deploy site / deploy (push) Successful in 8s
    Release / release (push) Successful in 24s
    Pre-release

    daniel released this 2026-04-20 14:49:31 +02:00 | 18 commits to main since this release

    Added

    • Apps catalog synced independently of core. A new daniel/furtka-apps Forgejo repo carries the bundled app catalog; running boxes pull the latest release via furtka-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, or sudo furtka catalog sync at 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 /apps to move an existing install onto a newer catalog version (settings merge-preserved via the existing installer.install_from path).
    • Catalog CLI: furtka catalog sync [--check] [--json] + furtka catalog status [--json]. Same shape as the core furtka update commands.
    • Catalog API endpoints: POST /api/catalog/sync/check, POST /api/catalog/sync/apply (detached via systemd-run for symmetry with /api/furtka/update/apply), GET /api/catalog/status. The existing /api/bundled endpoint 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_common extracted from furtka.updater. Both updater and the new catalog module 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_units now auto-enables newly-linked .timer units. On self-update, a fresh timer file (e.g. furtka-catalog-sync.timer added in this release) needs systemctl enable to actually start firing — linking alone isn't enough. Fresh installs get their enable via the webinstaller's _FURTKA_UNITS list as before.

    Fixed

    • SHA-256 CA fingerprint no longer overflows the /settings Local HTTPS card on narrow viewports. .kv dd grid items now set min-width: 0 + overflow-wrap: anywhere so the colon-separated hex string breaks within the card's right edge instead of pushing past it.
    Downloads
  • 26.5-alpha fec962e3d2

    26.5-alpha
    Some checks failed
    Build ISO / build-iso (push) Successful in 20m10s
    Deploy site / deploy (push) Successful in 13s
    CI / lint (push) Failing after 26s
    CI / test (push) Successful in 33s
    CI / validate-json (push) Successful in 24s
    CI / markdown-links (push) Successful in 14s
    Release / release (push) Successful in 6s
    Pre-release

    daniel released this 2026-04-20 11:52:36 +02:00 | 22 commits to main since this release

    Fixed

    • 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 with SSL_ERROR_INTERNAL_ERROR_ALERT; and both furtka.https and the Caddyfile's /rootCA.crt handler referenced /var/lib/caddy/.local/share/caddy/pki/…, a path that doesn't exist because our systemd unit sets XDG_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_caddyfile reads /etc/hostname). auto_https disable_redirects keeps Caddy's built-in redirect out of the way of the /settings toggle. PKI paths corrected in both furtka/https.py and assets/Caddyfile. Verified end-to-end on the 192.168.178.110 test VM: TLS 1.3 handshake completes, leaf cert issued, /rootCA.crt returns 200.

    Changed

    • Wizard footer version is now dynamic. webinstaller/app.py resolves the Furtka version at startup via a Flask context processor — reads /opt/furtka/VERSION on the live ISO (written by iso/build.sh from pyproject.toml at build time), falls back to pyproject.toml in 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.md added (manifest schema, volume namespacing, .env.example guardrails, SVG sanitiser limits, install/test flow). Root README.md roadmap updated with Phase 1 HTTPS + smoke-VM pipeline as shipped items and 26.4-alpha in the release list. iso/README.md corrected: mDNS is wired (not "later milestone"), post-install default URL is http://furtka.local (not proksi.local), HTTPS is available via tls internal since 26.4. website/README.md now documents the auto-deploy on push-to-main as the default path, manual deploy.sh as the SSH-hop fallback.
    Downloads