Open-source home server OS — simple enough for everyone. Container-based, app-store UI, easy installation.
Find a file
Daniel Maksymilian Syrnicki e68ed279cc
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
fix(https): make HTTPS opt-in to stop the BAD_SIGNATURE trap on fresh installs
Every Furtka since 26.5 shipped a Caddyfile with a
`__FURTKA_HOSTNAME__.local { tls internal }` site block, so every
first boot auto-generated a fresh self-signed CA + intermediate +
leaf. That worked for the first-ever Furtka user, but every reinstall
(or second box on the same LAN) produced a new CA whose intermediate
shared the fixed CN `Caddy Local Authority - ECC Intermediate` with
the previous one. Firefox caches intermediates by CN across profiles
— even private windows share cert9.db — so any visitor who had
trusted an older Furtka's CA got a cached intermediate with
mismatched keys when they hit the new box, producing
`SEC_ERROR_BAD_SIGNATURE`. Unlike UNKNOWN_ISSUER, Firefox has NO
"Advanced → Accept Risk" bypass for BAD_SIGNATURE, so fresh-install
boxes were effectively unreachable over HTTPS in any browser that
had ever seen a previous Furtka.

Validated live on the .46 test VM: fresh 26.14 ISO install → Firefox
hits BAD_SIGNATURE on https://furtka.local/ (even in private mode).
Chromium bypasses it via mDNS failure but the issue is the same.
openssl verify on the box confirms the chain is internally valid —
this is purely client-side cache pollution across boxes.

Fix:
- assets/Caddyfile: removed the hostname site block. Default install
  serves :80 only — https://furtka.local connection-refuses, which is
  a normal error every browser handles instead of the unbypassable
  crypto fault. Added top-level import of
  /etc/caddy/furtka-https.d/*.caddyfile so the /settings HTTPS toggle
  can drop a listener snippet there when a user explicitly opts in.
- furtka/https.py: 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). Disable removes both.
  Reload failure rolls both back. Added _read_hostname + _https_snippet_content
  helpers with `/etc/hostname` → 'furtka' fallback so a missing
  hostname file doesn't produce an empty site block Caddy rejects.
- furtka/https.py::status: force_https now reads the listener
  snippet (was reading the redirect snippet). A redirect without a
  listener isn't actually HTTPS being served, so the listener is the
  authoritative "HTTPS is on" signal.
- furtka/updater.py: new _maybe_migrate_preserve_https hook runs
  inside _refresh_caddyfile on the 26.14 → 26.15 transition. If the
  box had the redirect snippet on disk (user had opted into HTTPS
  under the old regime), it writes the new listener snippet too so
  HTTPS keeps working after the Caddyfile swap removes the hostname
  block.
- webinstaller/app.py: post-install creates /etc/caddy/furtka-https.d/
  alongside /etc/caddy/furtka.d/ so the glob import can't trip an
  older Caddy on a missing path during the first reload.

Live-tested on .46: set_force_https(True) writes both snippets, Caddy
reloads, :443 listener comes up with fresh CA, curl -k returns 302,
HTTP 301-redirects. set_force_https(False) removes both snippets
atomically, :443 goes back to connection-refused.

Tests: test_https.py expanded from 13 to 15 cases. Toggle-on asserts
both snippets written + hostname substituted. Toggle-off asserts
both removed. Rollback cases verify BOTH snippets restore on reload
failure. New test_https_snippet_content_has_tls_internal_and_routes
locks the exact shape of the listener block.
test_webinstaller_assets.py: updated two old asserts that assumed
hostname block was in Caddyfile; new test_post_install_creates_https_snippet_dir
guards the new directory.

276 tests pass, ruff check + format clean.

Known remaining wart (documented in CHANGELOG): a browser that
trusted a prior Furtka CA still hits BAD_SIGNATURE on this box's
HTTPS after enabling it, because the fixed intermediate CN is a
Caddy-side limitation. Workaround: clear cert9.db or visit in a
fresh profile. Won't affect end users with one Furtka box ever.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 19:30:04 +02:00
.forgejo/workflows chore: release 26.8-alpha (power actions, supersedes orphan 26.7 tag) 2026-04-20 16:00:19 +02:00
apps docs(apps): document the new path setting type 2026-04-21 11:43:09 +02:00
archinstall feat(furtka): in-browser app settings + ISO recovery-path fixes 2026-04-15 13:00:02 +02:00
assets fix(https): make HTTPS opt-in to stop the BAD_SIGNATURE trap on fresh installs 2026-04-21 19:30:04 +02:00
docs feat(ci): auto-boot every main-ISO in smoke VM on .165 Proxmox 2026-04-18 11:41:44 +02:00
furtka fix(https): make HTTPS opt-in to stop the BAD_SIGNATURE trap on fresh installs 2026-04-21 19:30:04 +02:00
iso chore: release 26.5-alpha 2026-04-20 11:52:36 +02:00
ops feat(website): legal pages (Impressum/Datenschutz) + auto-deploy on push-to-main 2026-04-18 12:10:06 +02:00
scripts chore: release 26.9-alpha 2026-04-20 18:51:30 +02:00
tests fix(https): make HTTPS opt-in to stop the BAD_SIGNATURE trap on fresh installs 2026-04-21 19:30:04 +02:00
webinstaller fix(https): make HTTPS opt-in to stop the BAD_SIGNATURE trap on fresh installs 2026-04-21 19:30:04 +02:00
website chore: release 26.8-alpha (power actions, supersedes orphan 26.7 tag) 2026-04-20 16:00:19 +02:00
.gitignore feat: publish public website at furtka.org 2026-04-14 10:27:51 +02:00
CHANGELOG.md fix(https): make HTTPS opt-in to stop the BAD_SIGNATURE trap on fresh installs 2026-04-21 19:30:04 +02:00
CONTRIBUTING.md chore: rename project Homebase → Furtka, domain furtka.org 2026-04-13 21:43:34 +02:00
LICENSE chore: rename project Homebase → Furtka, domain furtka.org 2026-04-13 21:43:34 +02:00
pyproject.toml fix(https): make HTTPS opt-in to stop the BAD_SIGNATURE trap on fresh installs 2026-04-21 19:30:04 +02:00
README.md docs: add apps/ authoring guide + realign READMEs with 26.4-alpha 2026-04-20 11:39:48 +02:00
RELEASING.md chore: release 26.8-alpha (power actions, supersedes orphan 26.7 tag) 2026-04-20 16:00:19 +02:00

Furtka

Open-source home server OS — simple enough for everyone. · furtka.org

"Furtka" is Polish for gate — a play on the gateway concept (reverse proxy + DNS as your home's front door).

Turn any x86 PC into a powerful, self-hosted home server with an app-store experience. No terminal skills required.

Vision

People are tired of big companies knowing everything about them. Synology NAS comes close to solving this, but it's expensive and still too complicated for most people.

Furtka aims to be:

  • As easy to install as Windows — boot from USB, click through a wizard, done
  • As easy to use as an app store — want Nextcloud? Click install, pick a name, wait a few minutes, and you have nextcloud.yourdomain.de
  • Container-based — everything runs in Docker, with sensible default configs
  • Built for normal people — your dad should be able to run his own cloud server
  • Fully open source — with an optional support/infrastructure subscription (Proxmox model)

Principles

  • Everything already exists — We're not inventing, we're connecting. Docker, reverse proxies, Let's Encrypt — it all works. We just wire it together with default configs and a simple wrapper.
  • Dogfooding — We build what we use ourselves. If we wouldn't run it at home, we don't ship it.
  • Two-tier UX — Dead simple for beginners (click Install, done), full control for advanced users (SSH in, edit configs, do whatever you want).

Architecture

+------------------+
|   Web UI         |  <- Simple admin panel / app store
+------------------+
|   Settings       |  <- UI/API wrapper that generates Docker configs
|   Wrapper        |     from simple user choices
+------------------+
|   Docker         |  <- Containers with sensible default configs
+------------------+
|   Gateway        |  <- Reverse proxy, SSL, DNS (self-hosted or managed)
+------------------+
|   Base OS        |  <- Minimal Linux (leaning Arch, Debian as fallback)
+------------------+
|   Any x86 HW     |  <- Old PC, mini PC, NUC, whatever
+------------------+

Key Decisions

Decision Status Notes
Base OS Leaning Arch Robert already has Arch running on Proxmox and is building custom images. Debian remains fallback (FAI, Proxmox ecosystem).
Containers Docker Lower overhead than VMs, easier default configs
Installation Web-based wizard Robert's webapp prototype (device reader + form → JSON) is working. Full spec: wizard-flow.md
Reverse proxy Caddy Automatic Let's Encrypt, simplest config of any reverse proxy
Identity provider Authentik Bundled SSO from day one — every app template auto-wires to it at install
Managed gateway DNS NS delegation to ns1.furtka.org User delegates once at registrar; we handle wildcard cert + subdomain creation
Local HTTPS Local CA One-click CA install → green padlock on every service, no browser warnings
Gateway Flexible Own reverse proxy OR managed through our infrastructure
UI approach UI-first Design the simplest possible UI, then build everything to match

Landscape (Existing Projects)

Project Type Apps Key Trait
CasaOS Layer on existing Linux ~100 Simplest install, runs on any distro
Umbrel Debian-based full OS ~300 Slick UI, crypto/privacy focus
Runtipi Docker-based, GPL-3.0 200+ Largest default app catalog
HomeDock OS Pseudo-OS layer Hundreds Desktop-style UX with window manager
Cosmos Server All-in-one platform Docker Built-in 2FA, anti-DDoS, security focus
YunoHost Debian-based OS (since 2012) 400+ Most mature, biggest catalog
TurnKey Linux Pre-built system images Hundreds One image per use case

Recent signals (from competitors.md)

  • Umbrel's license is the #1 r/selfhosted complaint. PolyForm Noncommercial 1.0.0 isn't OSI-approved; Citadel forked explicitly over this.
  • Umbrel has refused HTTPS on its local UI for 4+ years. Issue #546 open since Feb 2021. Community quote: "all it takes is one Umbrel vuln to bring down half of the lightning network."
  • CasaOS is in maintenance mode. IceWhale pivoted focus to ZimaOS (paid hardware). Users are openly asking if the project is still alive.

Where we differentiate

  1. Full OS + device-aware installer wizard — Boot USB, open https://proksi.local, wizard detects hardware and configures everything. No existing project does this — CasaOS/HomeDock are layers on existing Linux, Umbrel's x86 installer asks you to type a drive number, YunoHost runs stock Debian partitioning.
  2. Auto setup intelligence — Tests drive speeds, auto-assigns boot/LVM storage. Competitors just ask you to pick a drive.
  3. Gateway-as-a-service — No competitor offers managed reverse proxy + DNS + SSL as a service. Even YunoHost (best SSL story of the three) punts DNS setup to the user's registrar — that's the UX cliff where newbies quit.
  4. HTTPS + AGPL from day one — HTTPS on the local UI via a one-click local CA install (no browser warnings, unlike YunoHost's self-signed model). Fully AGPL-3.0 — the exact counter-position to Umbrel's non-OSI license complaints.

Gap we're targeting

None of these nail the "your dad can set this up" experience. The installer wizard + managed gateway + HTTPS-by-default is the strongest angle.

Resources

Inspiration

  • Azure Local — cluster management for enterprises, we want this for home users
  • Proxmox community-scripts — great base, but VM-focused (more overhead)
  • Synology DSM — closest to our UX goal, but proprietary and expensive
  • Home Assistant — app-store model for smart home, we want this for all services

Roadmap

  • Installer webapp prototype — device reader + form → JSON (Robert)
  • Arch running on Proxmox, custom image builds in progress (Robert)
  • Competitor analysis — see docs/competitors.md
  • Wizard flow spec — see docs/wizard-flow.md
  • Release process + CI — CalVer tags, conventional commits, Forgejo Actions (ruff, pytest, JSON, link checks), 26.0-alpha tagged
  • Forgejo runner live on Proxmox VM (forge-runner-01, Ubuntu 24.04) — docker-outside-of-docker with host-mode jobs for ISO builds, setup captured in docs/runner-setup.md + ops/forgejo-runner/
  • ISO-build in CI.forgejo/workflows/build-iso.yml runs iso/build.sh on every push to main and publishes the resulting .iso as the furtka-iso artifact (14 d retention). Push → green run → download → test.
  • Forgejo Releases + tag-driven release pipeline.forgejo/workflows/release.yml fires on [0-9]* tags, scripts/build-release-tarball.sh packages furtka/ + apps/ + assets/ + a root VERSION, scripts/publish-release.sh uploads tarball + sha256 + release.json to the Forgejo releases page. Releases 26.1-alpha, 26.3-alpha, and 26.4-alpha live at releases (26.2 stalled on a jq apt hang, fixed in 26.3). Needs one repo secret (FORGEJO_RELEASE_TOKEN).
  • Walking-skeleton live ISO — end to endiso/build.sh produces a hybrid BIOS/UEFI Arch-based ISO. It boots in a Proxmox VM, DHCPs onto the LAN, shows a console welcome with http://proksi.local:5000 (+ IP fallback), serves the Flask webinstaller, runs archinstall --silent, reboots the VM via a Reboot-now button, and the installed system logs in and runs docker ps without sudo. Build infra in iso/.
  • Drop loop/rom devices from drive listwebinstaller/drives.py filters by lsblk TYPE=disk, so the live squashfs and CD-ROM no longer appear as install targets. Boot-USB filtering on bare metal is still TODO; see iso/README.md.
  • Rebrand GRUB menuiso/build.sh rewrites "Arch Linux install medium" → "Furtka Live Installer" across GRUB, syslinux, and systemd-boot configs; default entry marked (Recommended).
  • Wizard: account form → drive picker → overview → archinstall — S1 collects hostname/user/password/language with validation, S2 picks boot drive, overview confirms, /install/run writes user_configuration.json + user_credentials.json (0600) and execs archinstall --silent against its 4.x schema (default_layout disk_config + !root-password / !password sentinel keys + custom_commands for post-install group joins). Install log page polls a JSON endpoint and renders a phase-based progress bar with a collapsible raw log. FURTKA_DRY_RUN=1 skips the real exec for testing.
  • mDNS proksi.local — hostname baked into the live ISO, avahi + nss-mdns in the package list, advertised as soon as network-online fires. The HTTPS + local-CA half of this milestone is still open below.
  • Base OS post-install (demo level) — after reboot the installed system comes up with Caddy on :80 serving a Furtka landing page (welcome + live uptime/Docker/disk tiles), the console shows a banner pointing at http://<hostname>.local, and nss-mdns makes that URL resolve on the LAN. Written by webinstaller/app.py's _post_install_commands via archinstall's custom_commands.
  • Resource manager + first bundled app (fileshare/SMB)furtka/ Python package handles scan / install / remove / reinstall of apps shipped under apps/. Manifest schema with settings fields drives an in-browser config form (no SSH needed). First app is a dperson/samba share mountable from Mac/Win/Linux. Validated end-to-end on VM 2026-04-16.
  • On-box web UI uplevel — shared /style.css served by Caddy, persistent top nav, landing page with an "Your apps" tile grid + live status, /apps with real per-app icons (inlined SVG from each manifest), new /settings page (hostname, IP, version, kernel, RAM, Docker, uptime + Furtka-updates card). prefers-color-scheme light/dark.
  • Versioned on-box layout + Phase 1 per-app updates/opt/furtka/versions/<ver>/ + current symlink; /var/lib/furtka/ for runtime state. POST /api/apps/<name>/update runs docker compose pull + compares digests + conditional up -d.
  • Phase 2 Furtka self-update/settings → Check → Update now. Downloads signed tarball (SHA256), stages, atomic symlink flip, reloads Caddy, daemon-reload, restarts services, health-checks the new api with auto-rollback on failure. CLI: furtka update [--check] + furtka rollback. Validated end-to-end on VM 2026-04-16 (26.0-alpha26.3-alpha → rollback → reboot).
  • Local HTTPS Phase 1 — Caddy tls internal on :443 alongside plain :80. Per-box root CA generated on first start, rootCA.crt downloadable from /settings, per-OS install guide at /https-install/. Opt-in "force HTTPS" toggle only exposes itself once the current browser already trusts the cert, so enabling it can't lock the user out. Shipped in 26.4-alpha.
  • Post-build smoke VM on Proxmox.forgejo/workflows/build-iso.yml hands the freshly built ISO to scripts/smoke-vm.sh, which boots it in a throwaway VM on pollux (192.168.178.165) and curls the webinstaller on :5000. VMID range 90009099, last 5 kept. Green end-to-end since 26.4-alpha.
  • Installer wizard screens S3S7 — per-device purpose, network, domain, SSL, diagnostic. S5/S6 blocked on managed-gateway DNS infra not yet built.
  • Local HTTPS Phase 2 — dedicated local CA (not Caddy's tls internal), streamlined one-click install across Win/Mac/Linux/Android, and HTTPS on the live-installer wizard (https://proksi.local:5000).
  • Caddy + Authentik wired into first-boot bootstrap
  • Managed gateway infrastructure — ns1/ns2.furtka.org + DNS-01 wildcard automation
  • First containerized service (Nextcloud?) with auto-SSO + auto-subdomain
  • Competitor hands-on testing on Proxmox — validate findings from docs/competitors.md
  • UI mockups / drafts (Robert)

Business Model

Furtka starts as a private/personal project. The long-term model follows Proxmox:

  • Free & open source — anyone can download, install, and use it
  • Paid support & managed infrastructure — for users who want hassle-free setup
  • Managed gateway option — the gateway (reverse proxy, SSL, DNS) can be self-hosted or run through our managed infrastructure (potential subscription revenue)

Team

  • Robert — Architecture, UI design, webapp installer prototype
  • Daniel — Infrastructure, testing, DevOps

License

AGPL-3.0 — open source, community-driven.