Full quality pass over the whole codebase: security hardening, reliability fixes, dead code removal, docs overhaul. Nothing was rebuilt from scratch: every change is a targeted fix verified against the 1068-test suite, a real TUI smoke test, and live daemon probes.
Security
- API keys are no longer served raw over HTTP:
GET /api/key/:provideron the daemon and the--webdashboard now return a masked key (••••••••+hasKey). Before, any local process (or any web page, via wildcard CORS) could read every saved provider key with onecurl. - DNS rebinding closed: the daemon now validates the
Hostheader and rejects anything that is not loopback (403) before routing. - CSRF gaps closed:
Origin: null(sandboxed iframes) is no longer trusted, and/daemon/shutdown, the canonical/setsroutes, the benchmark endpoints and the SSE streams now enforce the same-origin check. SSE connections are also capped per origin (5 of the 10 slots) so a hostile page cannot starve the real dashboard. - web server (
--webmode): theAccess-Control-Allow-Origin: *wildcard is gone and the server binds127.0.0.1unlessFCM_HOSTsays otherwise. /v1proxy: no longer forwards your browser cookies upstream, no longer relaysset-cookie/access-control-*headers downstream. New optionalFCM_ROUTER_TOKENenv var adds bearer-token auth to/v1(off by default, useful in LAN/Docker setups).- Keys on disk: every file the endpoint installer writes (aider, goose, env files, JSON configs) is now mode
0600, and pre-existing wide-permission files get tightened on rewrite. Env files shell-escape the key, so a key containing quotes can no longer execute commands when sourced. - No more shell interpolation on POSIX: OpenCode/Kilo/desktop launches spawn binaries directly instead of going through a shell (injection-proof by construction).
- Telemetry: upstream error bodies are no longer shipped to PostHog (the module header always promised this; now it's true),
telemetry.enabled: falsein the config is finally honored, and the two hardcoded Discord webhook URLs were removed: setFCM_DISCORD_FEATURE_WEBHOOK/FCM_DISCORD_BUG_WEBHOOKto re-enable feedback.
Fixed
- Multi-key providers worked for nobody: adding a second API key in Settings stored an array, and every request then sent
Authorization: Bearer key1,key2(guaranteed 401). The first key is now used. - Auto-hide of broken models now persists:
saveConfigused to silently wipeconfig.hiddenModels, so the Ctrl+Shift+P hide feature never survived a restart, and stale writers could resurrect deleted rows from the probe cache. - Honest verdicts: models whose key is rejected (401/no key) no longer show a green "Perfect" verdict and can no longer win
--fiable/findBestModel: they show "Not Active". - TUI memory + startup: ping history is capped at 300 samples per model (stats are windowed instead of growing forever), and the startup burst is bounded to 5 concurrent pings instead of ~226 simultaneous requests (no more 429 storms on launch).
- TUI truthfulness: the broken-rows auto-hide is no longer re-shown 83 ms after being hidden (Shift+B and the footer counter now tell the truth), Ctrl+P over the token/runtime overlays no longer opens an invisible palette that swallows keystrokes, and clicking the "AI Speed Test" footer badges launches the benchmarks instead of silently re-sorting the table.
- Daemon stability: a stalled upstream stream no longer feeds the unhandled-rejection crash counter, so a flaky provider day cannot kill the daemon into a restart loop; PID files are verified before SIGTERM;
x-request-idis clamped. - OpenCode/Kilo configs: saves are atomic, and a corrupt existing config now blocks the save with a clear message instead of being replaced from an empty base. Aider configs are only touched when they carry the FCM marker.
- 8 providers now inject their keys into OpenCode/Kilo: huggingface, deepinfra, fireworks, together, hyperbolic, perplexity, chutes, iflow were missing from the env-var map.
- Data quality: 4 tier/score mismatches fixed in the catalog (S+ scale), quota snapshots no longer fake freshness, the drift report no longer drowns real drift in hundreds of false "add" rows, and the updater no longer treats an npm dist-tag rollback as an update.
- CLI parsing:
--tier=S(equals form) works, repeated flags no longer leak a value into the API key argument, sort comparators can no longer return NaN, and the invalid-tier error message strips control characters. - Docker: the entrypoint no longer pipes the daemon through
sed(SIGTERM now reaches the real node process and exit codes are accurate), and 13 missing provider env passthroughs were added to docker-compose. - Website: 19 TypeScript errors fixed (typecheck is green again), the vendored catalog was 17 models behind the CLI (re-synced to 24 providers / 229 models), PostHog placeholder snippet and Google Fonts removed (the dashboard and site now work fully offline), unknown URLs render the styled 404, sitemap.xml added.
Added
- CI finally runs the tests: a new "Tests" workflow runs the suite on Node 20 and 22 for every push and PR. Before this release, nothing in CI ever executed the test suite.
- +185 tests (883 to 1068): 5 orphaned test files were revived from the dead (stale import paths, never executed), and new coverage covers key masking, Host rejection, multi-key handling, hiddenModels persistence, ping 401/429 classification, zero-chunk SSE failover, hostile-key shell escaping, and more.
- Self-updating docs: provider tables in
docs/providers.mdare generated fromsources.jsbyscripts/generate-provider-table.mjs, so counts can no longer drift from reality. - New docs:
docs/tui.md(every key binding verified against the code),docs/web-dashboard.md,docs/router.md,docs/resources.md, plusCONTRIBUTORS.md.
Changed
- README: 67 KB to 16 KB. Restructured around the quick start; every number, flag, key binding and path is now verified against the code (the old one claimed NVIDIA had 27 models, linked a 404 demo GIF and 5 nonexistent videos).
- npm package 27% lighter: 1.8 MB to 1.3 MB packed (397 files, was 514): build-time-only web sources and duplicate favicons no longer ship, and 4.4 MB of dead root media (logo duplicates) are gone from the repo.
kandownmoved to devDependencies: npm users no longer install a task-board CLI.vite-plus(unused) removed.- 200+ em dashes purged from docs and comments (project style rule).
- The "Commit Log to Discord" action now self-diagnoses a dead webhook (404) instead of failing every push with a bare error: note that the current
DISCORD_WEBHOOK_GITHUBsecret points at a deleted webhook and needs a fresh one.
Changed (breaking-ish, opt-in)
- LAN/Docker users: private-network origins are no longer implicitly trusted by the daemon. Same-origin requests keep working; other origins need
FCM_ALLOWED_ORIGINS./v1stays open by default; setFCM_ROUTER_TOKENto require a token.