furtka/CHANGELOG.md
Daniel Maksymilian Syrnicki e8c5317660
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
chore: release 26.10-alpha
Ships the new path-type setting (the schema extension that unlocks
host bind mounts for Jellyfin / Paperless / Nextcloud / Immich-class
apps), server-side path validation, app-author docs for the new type,
and the remove-USB-stick hint on the installer's reboot screen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 11:48:07 +02:00

22 KiB
Raw Blame History

Changelog

All notable changes to Furtka will be documented in this file.

The format is based on Keep a Changelog. This project uses calendar versioning: YY.N-stage (e.g. 26.0-alpha = 2026, release 0, alpha stage).

Unreleased

26.10-alpha - 2026-04-21

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.

26.9-alpha - 2026-04-21

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.

26.8-alpha - 2026-04-20

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.

26.6-alpha - 2026-04-20

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.

26.5-alpha - 2026-04-20

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.

26.4-alpha - 2026-04-18

Added

  • Local HTTPS via Caddy tls internal on port 443. Caddy generates a per-box local root CA on first start; the Caddyfile now serves both :80 and :443 from the same routes. HTTP stays on by default — no regression for users who haven't trusted the CA yet. New "Local HTTPS" section in /settings shows the CA's SHA-256 fingerprint, offers a one-click download of rootCA.crt, links to the per-OS install guide at /https-install/, and exposes an opt-in "force HTTPS" toggle that only unhides itself once the current browser has already trusted the cert (so enabling it can't lock the user out of the settings page). Backend: GET /api/furtka/https/status and POST /api/furtka/https/force in furtka.https. The force toggle drops a Caddy import snippet into /etc/caddy/furtka.d/redirect.caddyfile and reloads Caddy; reload failure automatically rolls the snippet state back so a bad config can't wedge the next service start.
  • Impressum + Datenschutzerklärung on furtka.org (both DE and EN) covering §5 DDG and Art. 13 GDPR. Linked from the site footer on every page; bilingual with DE as the legally binding version.
  • Auto-deploy of furtka.org on push-to-main. New .forgejo/workflows/deploy-site.yml runs on the self-hosted runner (which is forge-runner-01 — the webserver host), so the deploy is just a local rsync + hugo --minify into /var/www/furtka.org/. No SSH, no secrets. Manual website/deploy.sh remains for out-of-band deploys.
  • Post-build smoke VM on Proxmox test host 192.168.178.165. Every build-iso run boots the freshly built ISO in a throwaway VM on pollux (8 GiB RAM / 2 vCPU — the 4 GB default OOM-ed the host during mkinitcpio), then curls :5000 to confirm the webinstaller is alive. VMs in VMID range 90009099 tagged with the commit SHA; last 5 kept for post-mortem debugging. Optional workflow_dispatch "Smoke latest ISO" re-tests the cached ISO in ~2 min without rebuilding. Step-level continue-on-error means a VM-side flake doesn't mark the ISO build red.

Fixed

  • Settings page "Installed" field now refreshes after a self-update. The /api/furtka/update/check response already carries current — the settings JS now drives upd-current from it the same way it drives upd-latest, so clicking "Check for updates" after a successful update reflects the new installed version without a force-reload.
  • Auto-reload on update completion is now reliable. Clicking "Update now" arms a 45 s fallback setTimeout(location.reload) in addition to the existing /update-state.json polling loop. If the mid-apply API restart drops the poll connection before stage: done is ever observed (as seen on the 2026-04-16 VM test), the fallback still brings the page up on the new version. The fallback is cleared on done (5 s reload wins) or rolled_back (user needs the error visible).
  • Version string in the webinstaller footer was pinned at 26.0-alpha and didn't track releases. Bumped to 26.4-alpha for this release; follow-up will make it render from pyproject.toml dynamically.

26.3-alpha - 2026-04-16

Fixed

  • Release workflow no longer depends on jq. The previous apt-get install -y jq step hung on a slow mirror for 15+ minutes and stalled the 26.2-alpha publish. publish-release.sh now assembles the release-create payload via a tiny python3 -c block — Python is always available on the Forgejo Actions runner. apt-get path removed entirely.

26.2-alpha - 2026-04-16

Fixed

  • Updater "Check for updates" no longer 404s when every release is a pre-release. check_update() queried Forgejo's /releases/latest, which silently excludes pre-releases (anything tagged -alpha/-beta/-rc) and returns 404 when there is no stable release. Switched to /releases?limit=1, which Forgejo sorts newest-first across all release kinds. During the alpha stage where every tag is a pre-release this is the only thing that works; once we tag a stable release, the same query still picks it up.

26.1-alpha - 2026-04-16

Added

  • Furtka self-update (Phase 2). Tagging a release on main fires .forgejo/workflows/release.yml, which packages furtka/ + apps/ + a root-level VERSION file as furtka-<tag>.tar.gz, uploads it plus a .sha256 + release.json to the Forgejo releases page, and makes the release available to running boxes. New CLI: furtka update [--check] + furtka rollback. New endpoints: POST /api/furtka/update/check + /apply + GET /api/furtka/update/status. UI: "Furtka updates" card on /settings shows installed vs latest, Update button runs the apply flow detached via systemd-run, progress polls /update-state.json served by Caddy so the mid-update API restart doesn't interrupt reporting. Atomic /opt/furtka/current symlink flip, auto-rollback on health-check failure post-restart, SHA256-verified downloads.

  • Per-app container image updates (Phase 1). POST /api/apps/<name>/update runs docker compose pull, compares the running container's image digest to the just-pulled local image digest per service, and only restarts containers whose image actually changed. Update button on each installed-app row in /apps. Keeps image: :latest pins simple — no compose-file mutations.

  • Per-version install layout on /opt/furtka/. Install now extracts the resource-manager payload to /opt/furtka/versions/<VERSION>/ and creates /opt/furtka/current as an atomic symlink; updates flip the symlink in place and systemctl link every unit from the shipped assets/systemd/ tree. Runtime JSON (status.json, furtka.json, update-state.json) moved to /var/lib/furtka/ so self-updates never clobber it.

  • On-box UI uplevel across three pages sharing one design system (/style.css served by Caddy). Redesigned landing page with a "Your apps" tile grid driven by /api/apps, a fileshare app tile that deep-links to smb://<host>.local/files, status tiles, and subtle "Coming next" links to furtka.org. /apps page renders real app icons inlined from each manifest's icon.svg (defensive SVG sanitiser — strips script/on*/javascript: content, 16 KB cap). New /settings page with About-this-box, Appearance, Furtka-updates, and Coming-next sections. Persistent top nav (Jakob's Law) on every page. Light-mode support via prefers-color-scheme.

  • Webinstaller step 2 (boot drive) now shows size / type / health chips plus a "Recommended" badge on the auto-selected drive instead of a raw numeric score.

  • Forgejo branch protection on main — no direct pushes except owner-whitelisted, required status checks (CI / lint*, CI / test*, CI / validate-json*), applied via the idempotent ops/forgejo/apply-branch-protection.sh script.

  • In-browser app settings, so users no longer need SSH + vim to configure an app before first install. Manifest gains optional settings (name/label/description/type/required/default) and description_long fields. Installing a bundled app opens a form rendered from the manifest; installed apps grow a "Settings" button that edits merged values (password fields blank = keep current). API: POST /api/apps/install now accepts a settings object in the JSON body; new GET/POST /api/apps/<name>/settings for inspecting and updating an installed app. Password values never leave the server.

  • nano added to the installer package list so users have a beginner-friendly editor at the console/SSH (was vim-only, which command not found'd under Arch 4.x because it was actually missing from the package set too).

  • openssh added explicitly to the installer package list and sshd added to enabled services. archinstall: true in archinstall 4.x did not actually install openssh-server, so the documented recovery path (SSH → edit .env) silently failed.

  • Forgejo Actions runner live on Proxmox VM (forge-runner-01, Ubuntu 24.04) with DinD sidecar — CI green end-to-end. Setup scripts in ops/forgejo-runner/.

  • Walking-skeleton live ISO (iso/build.sh). Overlays an Arch releng profile with Flask + the webinstaller, bakes a systemd unit that auto-starts the wizard on boot, produces a hybrid BIOS/UEFI ISO via mkarchiso in a privileged archlinux:latest container. Tested booting under OVMF in Proxmox — wizard screens 13 respond at http://<vm-ip>:5000.

  • Public website at furtka.org (website/). Hugo static site, English + German, served from /var/www/furtka.org on forge-runner-01 via nginx. Upstream openresty proxy handles TLS. Intentionally minimal single-page copy while the project is pre-alpha. Deploy is ./website/deploy.sh (rsync + remote Hugo build); one-time VM setup in ops/nginx/setup-vm.sh.

Changed

  • Every on-box asset (landing page, settings page, style.css, status/welcome scripts, systemd units, Caddyfile) moved from inline Python string constants in webinstaller/app.py into real files under furtka/assets/. The installer reads them from disk at install time; the self-updater ships them in the release tarball.
  • Settings-button label went from "Einstellungen" (prototyping leftover) to "Settings" — rest of the UI chrome is English.
  • Keyboard layout at the TTY now follows the chosen installer language (dede, plpl, enus) instead of hardcoding us. Previously German users couldn't type /, -, or = at the recovery console.
  • fileshare app: description_long + settings (SMB_USER, SMB_PASSWORD) for the new settings form. Docker-level healthcheck from dperson/samba is disabled in the compose override — it timed out under normal operation and marked a working share "unhealthy" in docker ps.
  • Project name finalized: Furtka. Working title "Homebase" retired. Domain furtka.org registered via Strato 2026-04-13.
  • Managed gateway NS hostnames updated from ns1.homebase.cloud / ns2.homebase.cloud to ns1.furtka.org / ns2.furtka.org.
  • Python package renamed from homebasefurtka in pyproject.toml.

26.0-alpha - 2026-04-13

First tagged snapshot. Pre-alpha — the installer does not yet boot, but the design is locked and the prototype components are shaped.

Added

  • Installer webapp prototype (webinstaller/) — Flask app with 3-step wizard (hostname → drive → overview), serves drive list via drives.py::list_scored_devices().
  • Drive scoring module (webinstaller/drives.py) — scores attached disks by type (NVMe/SSD/HDD), SMART health (smartctl -H), and size. Consumed by the installer and usable as a CLI.
  • Base archinstall configuration (archinstall/user_configuration.json) — systemd-boot, ext4, Docker + Compose preinstalled, server profile, SSH. Credentials template at archinstall/user_credentials.example.json (real credentials gitignored).
  • Installer wireframes (docs/installer-wireframes.md) — Robert's hand-drawn 4-screen UX sketches.
  • Competitor analysis (docs/competitors.md) — CasaOS, Umbrel, YunoHost reviewed across install flow, hardware detection, app store UX, reverse proxy/SSL, user complaints. Key finding: device-aware wizard + managed gateway are uncontested differentiators.
  • Wizard flow spec (docs/wizard-flow.md) — 8-screen first-boot flow extending Robert's wireframe with YunoHost-style domain/SSL/diagnostic/confirm screens. Locked tech picks with rejected alternatives.
  • Project README — vision, principles, architecture, key decisions, landscape, roadmap.

Decisions locked

  • Reverse proxy: Caddy (auto Let's Encrypt, simplest config)
  • Identity provider: Authentik (bundled SSO, every app auto-wired at install)
  • Managed gateway DNS: NS delegation to ns1.furtka.org / ns2.furtka.org (wildcard cert via Let's Encrypt DNS-01)
  • Local HTTPS: Local CA installed by user once (no browser warnings on *.proksi.local)
  • Base OS: Arch (rolling, Debian remains fallback)
  • Containers: Docker + Compose
  • License: AGPL-3.0