July 22, 2026

The Installer Was Broken on Half of Linux

Every dnf-family distro — Fedora, RHEL, CentOS, Alma, Rocky — silently failed to install Chalie at all, and two more account-data endpoints moved onto the newer API contract.

Found a bug today that should embarrass whoever wrote it — that’s me. chalie start didn’t just struggle on Fedora, RHEL, CentOS, Alma, and Rocky. It was never going to work there, and nothing told you why.

The install script fetches Chromium for Playwright with --with-deps, a flag that only knows apt package names. Point it at a dnf-family box and it falls back to calling apt-get, which doesn’t exist there, exits with code 127, and set -euo pipefail takes the whole installer down with it — before Deno, before the voice models, before the CLI ever gets touched. Every distro on the dnf side of the fence that cleared the Python 3.11+ gate hit this and just stopped. No useful error, no partial install to poke at, /ready never goes green. Fix was to stop pretending one flag covers every package manager: apt keeps --with-deps because it tracks Ubuntu’s own renames (24.04’s t64 transition is the reason that flag earns its keep at all), dnf gets Chromium’s runtime libraries installed directly and then just fetches the browser, anything else gets a warning and best-effort.

That kind of bug survives because nobody’s install-testing a matrix of distros on every change. So the actual fix isn’t the branch, it’s the harness that would’ve caught it. It boots the real installer inside pristine containers — apt and dnf, several versions of each, amd64 and arm64, no emulation — and checks that onnxruntime actually imports with its execution providers, that the artifacts the installer promises (voice models, the Playwright browser, Deno, the CLI) really land on disk, and where a clean boot is the whole point, it starts Chalie and polls /ready until every preflight check goes green. It also launches headless Chromium directly during the audit now, so a missing shared library shows up as a hard failure at verify time instead of waiting for the first time someone tries to browse the web. Wired into CI on any PR touching the installer, plus a weekly run to catch distro maintainers changing something out from under us. Ran it against fedora41, fedora40, debian12, and ubuntu2404 — clean pass on all four. A macOS job on real Apple Silicon joined the same CI run.

Smaller thread alongside it: two more surfaces moved onto the newer Endpoint/Action contract — snapshot export/import and the privacy endpoints (data summary, export, delete-all). Same URLs, same cookie-only auth, but the error handling got real. A corrupt snapshot zip or a wrong password now raises a proper 409 instead of a hand-assembled error tuple, and the privacy delete-all confirmation gate is a raised exception returning 400 instead of a 422 that never meant anything specific. Every leftover catch-all-500 on both surfaces is gone, so a real failure shows up as itself instead of a generic five-hundred. user_auth, system, threads, and voice are still sitting on the old namespace shape — that’s what’s left of this one.

All of this lives on the rc-1.2.0 branch, which exists to get proven before anything ships off it.

  • Fixed an installer bug that silently broke every dnf-family distro (Fedora, RHEL, CentOS, Alma, Rocky) — Playwright’s --with-deps only knows apt, so the whole install died before Deno or the CLI got touched

  • Branched Chromium dependency install by distro: apt keeps --with-deps for package-rename tracking, dnf installs runtime libraries directly, unknown distros get a warning and best-effort

  • Added a multi-distro install-verification harness — real installer, pristine containers, apt and dnf, amd64 and arm64 — wired into CI on installer PRs and a weekly schedule

  • Added a macOS-on-Apple-Silicon verify job to the same CI run

  • Moved snapshot and privacy endpoints onto the Endpoint/Action contract, replacing hand-built error tuples with proper 409/400 exceptions and clearing the last catch-all-500s on both surfaces