Lemonade v11.9.0: the origin-hardening release — and upstream took our HRX backend
Upstream's latest Lemonade is a security-and-reliability release: it hardens where the server will accept requests, bumps llama.cpp to b10723, and fixes how streaming stalls are bounded. Our re-vendor was the cleanest yet — because upstream now carries the HRX backend we built, most of our local patch simply disappeared.
The release: harden where the server will answer
v11.9.0 is upstream's security-and-reliability pass, and the theme is origin control — deciding which callers a public-facing Lemonade server will trust. Three changes land together, and all three flow straight into our embed the moment the re-vendor lands:
- New allowed_origins global config key (#3420) — an authoritative allow-list in the top-level config schema, settable at runtime via lemonade config set allowed_origins=....
- Same-origin requests are now recognized by the request Host (#3419), which fixes the case where a legitimate same-host call was being rejected as a cross-origin request.
- Unauthorized origin rejections are logged (#3418), so a blocked request is a visible, debuggable event instead of a silent drop.
The origin machinery behind this was reworked substantially — origin_utils grew the layered allowed-origins resolver, and the whole flow is covered by new test_config_allowed_origins and test_origin_utils suites. For an engine that serves an OpenAI-compatible API on a LAN, this is the difference between "works until someone pokes it" and a real allow-list.
llama.cpp to b10723, and a few reliability fixes
The other big bump is the bundled backend: llama.cpp moves to b10723 (#3452), and the whole backend-version matrix follows — vulkan b10723, rocm-stable b10711, rocm-nightly b1319, cuda b10711, metal b10723, cpu b10723. (Our own HRX lane keeps its separate pin — more on that below.)
Two correctness fixes also landed. First, the streaming-stall window now follows global_timeout (#3386/#3477): a stream that goes silent is bounded by the same configured timeout as everything else, rather than the special-case hardcoded bound we had carried locally. Second, the directory watcher is joined before the ModelManager members are destroyed (#3453) — the shutdown-order fix that stops a teardown race. And model classification changed: extra models are now bucketed by reserved chat/embeddings/reranking directories instead of treating every model as chat (#3333).
The re-vendor, our cleanest yet
Here is the part that made this release special for us. Since v11.7.0 our loop has been: pull the pinned commit, re-apply the embeddability patch, fix any engine-facing API drift, build, smoke-test. Every release, the patch was the thing we guarded — CMAKE_CURRENT_SOURCE_DIR, system json/httplib reuse, PUBLIC include dirs, the add_test police, the copy_resources dependency.
This time most of the local work dissolved. To bump to v11.9.0 we did a 3-way merge (base = upstream v11.8.1, ours = our vendored tree, theirs = v11.9.0) and, when the dust settled, the C++ source came back byte-identical to upstream. The reason is that upstream now carries the HRX backend itself — src/cpp/server/backends/hrx/hrx_server.cpp and lemon/backends/hrx/ are in upstream's tree, and the file is byte-for-byte ours. We built it here, and they shipped it. For a project that has kept this backend out-of-tree since the start, that is the whole point of the sync loop.
Two deliberate choices remain, both flagged in UPSTREAM.md. First, we dropped our stream_stall_timeout config key — v11.9.0 owns that bound via global_timeout, and local review confirmed the extra key was no longer needed. Second, we keep the newer hrx-b66 pin where upstream pins hrx-b59 — our HRX lane is one binary revision ahead of upstream's, and the backend interface is identical, so the newer backend is a straight improvement. What we did not lose: the 44 *-HRX model-registry entries (routed via upstream's native "recipe": "llamacpp-hrx"), the HRX generator/annotator tools, and the embeddability patch itself.
We validated it the hard way before committing: cmake . reconfigured clean and ninja lemonade-server-core compiled all 54 units — server core, every backend, hrx_server.cpp included — with no errors.
Upstream took the HRX backend we built, and the re-vendor that used to be a patch-guarding exercise is now a clean merge. The engines drift less every release.
Run it
build/1bit unified --lemonade --port 8088 — the same binary serves all 14 backends plus the HRX lane through the OpenAI-compatible API. Set allowed_origins in config.json (or lemonade config set allowed_origins=...) to lock down who can reach it, and the same-origin-Host fix keeps your own calls working.