• 26.17-alpha b725bf1773

    26.17-alpha
    All checks were successful
    Build ISO / build-iso (push) Successful in 18m3s
    CI / lint (push) Successful in 27s
    CI / test (push) Successful in 1m22s
    CI / validate-json (push) Successful in 25s
    CI / markdown-links (push) Successful in 13s
    Release / release (push) Successful in 12m13s
    Pre-release

    daniel released this 2026-05-11 20:10:04 +02:00 | 2 commits to main since this release

    Added

    • App-to-app dependencies. Manifests gain an optional requires
      array; each entry names a provider app plus two optional hook scripts
      that live in the provider's folder. on_install runs once via
      docker compose exec against the provider's running container while
      the consumer is being installed (use case: mosquitto_passwd a new
      MQTT user for the consumer). on_start runs every boot during
      reconcile, before the consumer's container starts (use case: make
      sure the user still exists after a Mosquitto wipe). Hook stdout
      parses as KEY=VALUE lines and optional FURTKA_JSON: {…} sentinel
      lines, both validated against the existing SETTING_NAME regex; the
      values get merged into the consumer's .env (hook wins on conflict)
      and the placeholder-secret check runs again over the merged file so
      a hook returning MQTT_PASS=changeme is refused the same way an
      unedited .env.example is.
    • POST /api/apps/install/plan. New read-only endpoint that
      returns the topo-sorted install order for a target app plus per-app
      summaries (display_name, version, has_settings, installed flag). The
      catalog UI calls this before opening the settings dialog so it can
      show a confirm modal — "Installing zigbee2mqtt also installs
      Mosquitto" — before anything mutates. Circular dependencies surface
      as 400 {error: "circular dependency: A -> B -> A"}; missing
      providers as 400 {error: "required app 'X' not found …"}.
    • /var/lib/furtka/install-plan.json (overridable via
      FURTKA_INSTALL_PLAN). The HTTP install endpoint writes this before
      it spawns the systemd-run background job so the runner knows the
      full chain to pull → create volumes → fire hooks → compose up for
      in plan order. The runner consumes the file after reading so a stale
      plan from a previous install can't accidentally steer the next one.

    Changed

    • furtka reconcile now visits apps in dependency order, not
      alphabetical.
      Topo-sort over requires puts providers before
      consumers so a consumer's on_start hook can talk to an already-up
      provider. Within a tier, ties stay alphabetical so boot logs are
      still deterministic across reboots. Apps with unresolvable requires
      (missing provider) are visited last; the per-app error-isolation in
      reconcile then catches them without aborting the whole sweep.
    • POST /api/apps/install requires confirm_dependencies: true
      when installing a named app would pull in transitive providers.
      Without the flag, the endpoint returns 409 plus the full plan body
      so the UI can render the confirm dialog without a second round-trip.
      Lone-target installs (no transitive deps) keep the existing
      one-click flow — no UX change for fileshare-style standalone apps.
    • furtka app install <name> and the web UI now install transitive
      dependencies automatically.
      furtka app install /path/to/dir
      stays as today (single-app, dev/test workflow).
    • compose_exec and compose_exec_script helpers in
      furtka/dockerops.py. Both pass -T (no TTY) so they work from the
      install runner and from reconcile; both raise DockerError on
      non-zero exit or timeout. compose_exec_script streams the script
      body via stdin to sh -s so hooks don't need to be baked into the
      provider's container image.

    Notes

    • Hook target service: v1 auto-picks the first service in the
      provider's compose config. Works for Mosquitto, Postgres, Redis.
      Multi-service providers (Authentik server+worker) will need an
      optional service field on the requirement entry; deferred until a
      real case lands.
    • Hook timeouts: on_install 60 s, on_start 30 s. Hardcoded for
      v1 — revisit if a DB seed legitimately needs longer.
    • Removing an app is now blocked (409 {dependents: […]} from the
      API, exit 2 from the CLI) when other installed apps require it.
    Downloads