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
Three interlocking issues that made 26.11/26.12 effectively un-upgradable from pre-auth versions without manual pacman + symlink surgery. Caught while SSH-testing the .196 VM which landed on a rollback loop after every Update-now click. 1. auth.py imported werkzeug.security, but the target system runs core as bare system Python — neither flask nor werkzeug are pip-installed. Fresh 26.11+ boxes died on import. Replaced with a 50-line stdlib `furtka/passwd.py` using hashlib.pbkdf2_hmac for new hashes and parsing werkzeug's `scrypt:N:r:p$salt$hex` format for backward-read so existing users.json survives. 2. updater._health_check pinged /api/apps expecting 200. Post- auth, /api/apps returns 401 for unauth requests → HTTPError caught as URLError → retry loop → 30s timeout → rollback. Now any 2xx-4xx counts as "server alive"; only 5xx / connection errors fail. Server responding at all is proof it came back up. 3. _do_install released the fcntl lock between sync pre-validation and the systemd-run dispatch. A second POST could slip in, pass the lock check, return 202, and leave its install-bg child to die silently on the in-child lock. Now the API also reads install-state.json and refuses 409 on non-terminal stages — the state file is the reliable signal, the fcntl lock is defence in depth. Test coverage: - tests/test_passwd.py (new, 6 cases): roundtrip, salt uniqueness, format shape, werkzeug scrypt backward-compat against a real hash captured from the .196 box, malformed + non-string rejection. - tests/test_updater.py: +3 cases for _health_check — 4xx=healthy, 5xx=unhealthy, URLError retry loop. - tests/test_api.py: +2 cases for install 409 on non-terminal state + 202 after terminal. All 267 tests green, ruff check + format clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
295 lines
28 KiB
Markdown
295 lines
28 KiB
Markdown
# Changelog
|
||
|
||
All notable changes to Furtka will be documented in this file.
|
||
|
||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||
This project uses calendar versioning: `YY.N-stage` (e.g. `26.0-alpha` = 2026, release 0, alpha stage).
|
||
|
||
## [Unreleased]
|
||
|
||
## [26.13-alpha] - 2026-04-21
|
||
|
||
### 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.
|
||
|
||
## [26.12-alpha] - 2026-04-21
|
||
|
||
### 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.
|
||
|
||
## [26.11-alpha] - 2026-04-21
|
||
|
||
### 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`.
|
||
|
||
## [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 9000–9099 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 1–3 respond at `http://<vm-ip>:5000`.
|
||
- **Public website at [furtka.org](https://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 (`de` → `de`, `pl` → `pl`, `en` → `us`) 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 `homebase` → `furtka` 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
|
||
|
||
[Unreleased]: https://forgejo.sourcegate.online/daniel/furtka/compare/26.13-alpha...HEAD
|
||
[26.13-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.13-alpha
|
||
[26.12-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.12-alpha
|
||
[26.11-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.11-alpha
|
||
[26.10-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.10-alpha
|
||
[26.9-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.9-alpha
|
||
[26.8-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.8-alpha
|
||
[26.6-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.6-alpha
|
||
[26.5-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.5-alpha
|
||
[26.4-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.4-alpha
|
||
[26.3-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.3-alpha
|
||
[26.2-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.2-alpha
|
||
[26.1-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.1-alpha
|
||
[26.0-alpha]: https://forgejo.sourcegate.online/daniel/furtka/releases/tag/26.0-alpha
|