furtka/CHANGELOG.md
Daniel Maksymilian Syrnicki b8fdb62b41 fix(furtka): pre-ISO audit fixes — chmod, Caddyfile refresh, unit linking
Five issues surfaced by the Phase-2 audit before the next ISO rebuild:

P1 (real blockers for a fresh install / self-update):

1. chmod +x furtka/assets/bin/furtka-status, furtka-welcome. They were
   mode 644 in git, so the tarball shipped them non-executable and every
   ExecStart referencing /opt/furtka/current/assets/bin/furtka-* would
   have failed on first boot with Permission denied.

2. apply_update now refreshes /etc/caddy/Caddyfile from the new version
   when the content differs, then reloads caddy. Without this, a release
   that changes Caddy routes silently stays on the old config.

3. apply_update now systemctl-links any new unit files shipped by the
   update, not just the five linked at install time. A future release
   that adds furtka-foo.service would otherwise never appear in
   /etc/systemd/system/.

P2 (hardening, not blockers today):

4. _resource_manager_commands now aborts the install if the tarball's
   VERSION file is empty — otherwise `mv "$staging" /opt/furtka/versions/`
   would move the staging dir in as a subdirectory and the symlink
   target would be invalid.

5. _extract_tarball passes filter='data' to tarfile.extractall on
   Python 3.12+ to catch symlink-escape / setuid / device-node tricks
   that the regex path-check can't see. Falls back silently on older
   interpreters.

Plus the CHANGELOG [Unreleased] section got filled in with the whole
Phase-1 + Phase-2 + UI-uplevel body so a 26.1-alpha tag cut off main
has meaningful release notes.

Test additions / updates:
- test_refresh_caddyfile_{copies_when_different,noops_if_source_missing}
- test_link_new_units_only_links_missing
- test_extract_tarball_uses_data_filter_when_available
- test_apply_update_happy_path now verifies the Caddyfile gets copied.
- test_resource_manager_extracts_to_versioned_slot verifies the
  empty-VERSION guard is present in the install command.

Paths now overridable via FURTKA_CADDYFILE_PATH + FURTKA_SYSTEMD_DIR so
tests can pin a tmpdir for these new fs operations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 14:10:07 +02:00

8.2 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

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