• 26.16-alpha 863ffa9737

    26.16-alpha
    All checks were successful
    Build ISO / build-iso (push) Successful in 18m12s
    Deploy site / deploy (push) Successful in 3s
    CI / lint (push) Successful in 28s
    CI / test (push) Successful in 1m21s
    CI / validate-json (push) Successful in 24s
    CI / markdown-links (push) Successful in 13s
    Release / release (push) Successful in 12m13s
    Pre-release

    daniel released this 2026-05-10 12:59:30 +02:00 | 0 commits to main since this release

    Added

    • Failed-login rate limit on /login. A new in-memory
      LoginAttempts store in furtka/auth.py blocks brute-force attempts
      after 10 failures in 15 minutes from the same (username, IP) pair,
      with a 15-minute lockout. Successful logins clear the counter; a
      systemctl restart furtka clears any stuck lockout — fine for an
      alpha single-user box. Tuple-keying means a flood from one source IP
      can't lock the admin out from elsewhere; an attacker can rotate IPs
      to keep probing forever, but each attempt still eats the PBKDF2 cost.
      Locked attempts get a Retry-After header so the UI can render the
      cooldown.
    • Live-ISO boot USB is filtered out of the install drive picker. On
      bare-metal installs, lsblk reports the USB stick the live ISO
      booted from as TYPE=disk, so it showed up in the picker alongside
      the real install target — a user could in theory pick the USB they
      had just booted from. webinstaller/drives.py now resolves
      /run/archiso/bootmnt via findmnt, walks it up to its parent disk
      via lsblk -no PKNAME, and drops that disk before scoring. On a
      normal (non-live) box /run/archiso/bootmnt does not exist and the
      picker is unchanged.

    Changed

    • furtka.org homepage rebuild. Adopted the visual feel of Pascal's
      prototype while keeping Furtka's voice, brand palette, and bilingual
      structure: Three.js wireframe torus-knot behind the hero (color +
      opacity tied to the existing --accent CSS var so light and dark
      modes share one scene), scroll-driven camera zoom + tilt, GSAP +
      ScrollTrigger card reveals, Lenis smooth scroll, gradient wordmark,
      drop-shadow glow in dark mode, and a pulsing CTA pointing at
      /releases. "What works today" / "What's coming next" lists moved
      from markdown bullets into front-matter arrays and now render as
      scroll-reveal cards. All vendor JS (Three.js r128, GSAP 3.12.2 +
      ScrollTrigger, Lenis 1.0.33) is vendored locally under
      website/assets/js/vendor/, fingerprinted with SRI, gated to the
      homepage only, deferred so first paint isn't blocked, and
      early-returned on prefers-reduced-motion.
    • Static-asset gzip on the furtka.org nginx (config only — needs a
      deploy on forge-runner-01).
      Default nginx only gzips text/html,
      so the homepage HTML was the only asset coming back compressed. The
      ~600 KB three.min.js bundle (and the hashed CSS) were being shipped
      uncompressed across the public openresty proxy. gzip_types in
      ops/nginx/furtka.org.conf now covers css/js/json/xml/svg/woff2.
      Needs sudo ops/nginx/setup-vm.sh on forge-runner-01 to take effect
      — the site-deploy workflow only rebuilds Hugo, it doesn't touch the
      nginx config.
    Downloads