Repository navigation
Releases: hasso5703/alzeo
Release list
v1.42.1 (2026-11-03): no client can hide a field from the proxy's guards in UTF-16, and a listed key or the other box of a pair can no longer end every generation of the box
What was wrong. SGLang aborts every request whose id starts with one it aborts, and it aborts a request's id when
its client leaves. A client of a listed key (keys.json, the identity wall) could name the ids itself, "0" to "f", in
a body or in the x-override-rid header, leave, and so end every generation of the box. v1.42.0 kept the other box of
a pair from doing it, but only for a top-level "rid" whose bytes showed it, and SGLang takes the id in three more
forms: the same key written with escapes ("r\u0069d"), request_id on /v1/responses, and the Vertex route's
parameters, which it spreads into the request. And the vLLM lanes read a body in UTF-16 or UTF-32 as well as UTF-8
(the SGLang lanes refuse it), in which the proxy's guards, which look at a body's bytes first, found none of the
fields they look for: any client could send those lanes what the guards refuse, and an id too. Found by security
reviews on 2026-11-03.
What changes. A body that is not UTF-8 gets a 415 on every lane (RFC 8259 wants JSON in UTF-8 between systems),
and is never relayed. The proxy (v6.51) no longer lets a client of a listed key name the engine's request id, as for
the other box of a pair: the headers are dropped (x-override-rid, and vLLM's X-Request-Id), the body is read
whenever it may hold an id and every form is dropped, vLLM's request_id too, and a body that may hold one and cannot
be read whole gets a 400. The proxy or the engine names the request. A client with the engine's key, on a box without
the identity wall, still names its requests as it did. The tests fail on v1.42.0: for a listed key in every form, for
the other box in the two new ones and in UTF-16, and for any client in UTF-16 and UTF-32.
Also. The cockpit reads the deployed proxy's version from its first line: it read it from a history line, which
sits past the first 4,000 bytes it read since v1.40.7, so Lanes and Settings said "unknown", and the warning that the
deployed proxy is not the repo's copy, which needs a version, could never show. The empty Timeline card of the Now
view reads on one line: its text filled the 62 px column of an event's time. Four test files held the tailnet address
of the reference box: they use an address of the same range that is no box's. Each fix but the addresses has a test
that fails on the code before it.
What an update does. The proxy (v6.51) restarts, as a proxy update always did: a generation in flight on the box
is cut, those the other box of a pair sent here too. The cockpit restarts once, which drops its open pages and the
Agent tab's connection. No engine, no lane and no unit file changes.
After the push. The reference box updated at 2af376a on 2026-11-03 with the command of this release,
cd ~/dgx-spark-qwen38 && curl -fsSL https://get.alzeo.ai | bash, once GitHub's CI had passed on that commit (7 jobs,
89 steps, none skipped) and get.alzeo.ai served its get.sh byte for byte: the run ended whole in 93 s. The proxy
(v6.51) and the cockpit restarted. Flash FP4-Local, the lane that served, kept running, and no unit file changed. The
8 real clients (Claude Code, opencode, Hermes, pi, the OpenAI and Anthropic SDKs, fetch under Node and Bun, LiteLLM)
answered through the proxy on Flash FP4-Local, and the production checks passed. The cockpit showed no banner on any
of its 5 views, with no JavaScript error, and Lanes and Settings read the proxy as "v6.51, the repo's copy", where
they said "unknown" since v1.40.7. The matrix then ran the 8 clients on each of the 7 lanes, each lane loaded as the
cockpit loads it: 54 of 56. The two misses are pi on the two Flash lanes, in the first matrix where pi runs without
the skills installed on the box (--no-skills, since its first run on v1.42.0 took a skill's description for the
file). On Flash, its read tool gave the model the file and its number, and the model answered 3.14159. On Flash
FP4-Local, the model called a tool that pi does not have, then said it had no tool to read the file. Nothing in this
release changes what pi sends. Each Flash lane loaded again as the cockpit loads it, the 8 clients passed on both,
then pi got the number 8 times in 10 on Flash, where its two misses answered 42 with no call to a tool, and 10 times
in 10 on Flash FP4-Local, after 26 times in 26 with --no-skills and 6 times in 6 without on the Flash FP4-Local
that served before the reloads.
v1.42.0 (2026-11-03): two boxes behind one endpoint, paired in one command, the other box in the cockpit, and non-streamed answers half a second sooner
Two boxes behind one endpoint. Two DGX Spark that each run Alzeo can answer as one (discussion #45). One
box can keep the 27B for the agents while the other serves Flash or another lane, and the clients of either
box reach both models at one address. A request that names a model of one of this repo's lanes goes to the box
that serves it, streamed or not, in the OpenAI and Anthropic dialects and on the Ollama and SageMaker routes.
Any other name (Claude Code's background tasks, gpt-*) stays on the box it was sent to, as on a box alone.
GET /v1/models lists the models of both boxes, each with a box field, and never waits for the other box. A
lane name that no box serves is answered by the box it was sent to, as on a box alone, so an agent set up for a
lane that is not loaded keeps working once the boxes are paired. docs/pair.md says it all.
Paired in one command. python3 ~/dgx-spark-qwen38/pair.py add <the other box> pairs both boxes over SSH (as
step 4 of NVIDIA's connect-two-sparks playbook sets it up), checks that each box reaches the other, and leaves
both boxes as they were when a check fails, a pair they already had included, with what to fix. Both proxies take
the pair within seconds: nothing restarts. pair.py status, remove, name and identity do the rest.
Security. The two boxes talk over TLS 1.3 both ways on port 30002, open only while the box is paired. Each
box has a certificate of its own, made once by pair.py, and pins the other's by its SHA-256, as the nodes of
NVIDIA's Personal AI Router trust each other. The certificate is the pair's only credential: no box holds the
other's API key, which is also the login of its cockpit. Port 30002 takes the generation routes, GET /v1/models and the summary the tile shows, and the box that serves a request admits it to its own engine
with its own key. Clients' keys and cookies never cross. SECURITY.md has the details.
The other box in the cockpit. The Now view shows the other box of the pair: what it serves, its memory,
its GPU, the job under way and its version, or why it does not answer. Its proxy sums its own cockpit up and
sends that summary alone, over the pair's TLS. To switch the other box's lane, use its own cockpit for now.
Non-streamed answers half a second sooner, on every box. The proxy watched the caller of a request in a
select() of 0.5 s, and relayed the answer once that watch had ended, at its next tick: a one-token answer the
engine gave in 35 ms took 502 ms through the proxy, eight times in eight (measured on the reference box,
v1.41.0). Every non-streamed request paid it (Claude Code's token counts, the SDKs' default calls), and a
stream whose engine took more than a second lost up to 0.45 s more before its first byte. The watch now looks
every 50 ms with poll(), which holds no descriptor of its own and has no limit at 1,024 descriptors as select()
had: the same answer takes 51 ms through this release's proxy. Found by the code review of the pair.
Reviews and tests. Two independent reviewers, then a code review at max effort, read the pair before this
release, and every finding was checked in the code. What they found and this release fixes: an API key swapped
as a second lock would have handed the other box the login of this one's cockpit (the certificate is the only
credential now), a client that left a TLS connection was never seen, an answer cut by the other box went out
as a whole one, a box that lost power held the requests in flight to it for up to an hour (TCP keepalive
between the boxes now sees it within 25 s), pair.py add undid a working pair while an engine was loading,
and the tile's reads every 5 s pushed a paired box's real requests out of the cockpit's view in half an hour.
Two more reviewers then read what had changed since, and found: a box restarted while the other one stayed off
held every lane it does not load for as long (a box that may serve a lane now holds it up for 30 s if it was
never read, 15 min if it served it), a box off without a reset made each such request wait 5 s (it is read
again in the background), a Ctrl-C during the checks of pair.py add lost a working pair (both boxes are put
back), the other box could end every generation of this one by naming request ids, which SGLang aborts by
prefix (a request it sends on never names one), nested JSON from it broke this box's model list and froze its
cockpit (what comes from it is rebuilt field by field), and the Ollama routes reached the client whole at the
end.
tests/test_proxy_pair.py runs two proxies over real TLS, as processes and in this one, tests/test_pair_tool.py
pairs two proxies through a fake ssh, and the tile and its collector have tests of their own. Each fix has a
test that fails on the code before it.
Proven on the reference box. One DGX Spark, two proxies of this release paired over TLS, both ways round. With
the entry serving a model of its own and the other proxy the box's real lane, Flash FP4-Local, the OpenAI and
Anthropic SDKs, fetch in Node and Bun, LiteLLM, Claude Code, opencode, Hermes and pi all went through the pair: 25
requests sent on, 25 answered, no error on either proxy (what they refused was the suites' own requests without a
key, and the agents' probes of routes the stand-in engine does not have). With the entry on the real lane, its
requests stayed there, and the other proxy's model was reached through the pair. The tile read the box's real
cockpit through the pair. Two physical boxes: not yet, the first pair of discussion #45 gets its second box this
week.
Also. No test file has the same name in tests/ and dashboard/tests/: a twin was loaded in place of the
other in the single-process step, and 19 tests never ran on a branch (2026-11-02). Ornith's installer tests
lose their twin.
After the push. The reference box updated at e07f2c2 on 2026-11-03 with the command of this release,
cd ~/dgx-spark-qwen38 && curl -fsSL https://get.alzeo.ai | bash, once GitHub's CI had passed on that commit and
get.alzeo.ai served its get.sh byte for byte: the run ended whole in 93 s. The proxy and the cockpit restarted. Flash
FP4-Local, the lane that served, kept running, and no unit file changed. The 8 real clients (Claude Code, opencode,
Hermes, pi, the OpenAI and Anthropic SDKs, fetch under Node and Bun, LiteLLM) answered through the proxy on Flash
FP4-Local, and the production checks passed. pi's first run said that the file it read held another text: the model
took the description of a skill installed on the box for the file, which its tool had read right, and pi passed three
times in three after that. The cockpit showed no banner on any of its 5 views, with no JavaScript error. A one-token
answer that took 38 ms at the engine took 52 ms through the installed proxy (medians of eight). The matrix then ran
the 8 clients on each of the 7 lanes of the box, each lane loaded as the cockpit loads it: 56 of 56. GitHub's CI
passed its 7 jobs, 89 steps, with no step skipped.
v1.41.0 (2026-11-01): the project is Alzeo, at github.com/hasso5703/alzeo, its command is get.alzeo.ai, and the cockpit is the Alzeo cockpit
Why. The repository was renamed hasso5703/alzeo on 2026-11-01, once v1.40.8 had made get.sh take a clone of either
name. GitHub sends the old addresses on, and this release writes the new name where a user reads it, gives the shorter
command, and names the cockpit after the project.
The command. curl -fsSL https://get.alzeo.ai | bash is the one-liner from now on, for a first install and for an
update alike. get.alzeo.ai answers with a redirect to get.sh on GitHub, so the file that runs is get.sh itself (byte
for byte the same file, checked from the reference box). The long forms keep working: the same file at
https://raw.githubusercontent.com/hasso5703/alzeo/main/get.sh, and at its old address. The README gives the first one
next to the short one, for whoever would rather trust GitHub alone, and SECURITY.md says what the short one trusts:
whoever controls get.alzeo.ai controls what the one-liner runs, as whoever controls the repository does. The cockpit
gives that first long form as its update, so that a box already installed trusts no new party for its updates, and
still updates behind a network that lets only GitHub through.
Where the new name is written. get.sh clones https://github.com/hasso5703/alzeo on a new box, into
~/dgx-spark-qwen38 as before, and its refusal under sudo names the new command, as install.sh does in its own. The
cockpit asks GitHub's API for the newest release under the new name, and gives
cd <the clone> && curl -fsSL https://raw.githubusercontent.com/hasso5703/alzeo/main/get.sh | bash as the update (and
the same with -s -- --recommended), the page too when the process that serves it sends none. The README, the docs,
CITATION.cff and the issue form link the new name. The README's manual clone, and the sentence that sends a reader to
clone before running anything, go into ~/dgx-spark-qwen38, the folder the box keeps: a plain clone of the new name
makes ~/alzeo, which the one-liner does not look for, and run later from outside it, the one-liner made a second clone.
docs/operations.md gives the cockpit's command to upgrade, where it gave a git pull that skips get.sh's checks and
worked from the home folder only.
What keeps the old name, on purpose. The folder ~/dgx-spark-qwen38, the qwen38 units, files and launchers, and the
name the installer writes into what it owns, by which an update tells its own files from a user's. get.sh still takes
a clone whose origin names the old repository, which GitHub sends on, so a box installed before goes on as it did. No
unit file changes, so the cockpit's unit and the Agent tab's opencode-web unit keep their descriptions, which say Spark
Cockpit, and opencode-web its documentation link: a change to that unit would have restarted opencode-web on every
box. The past sections of this changelog keep the links they were written with.
The Alzeo cockpit. The page, its sign-in page, the Agent relay's page, the installer's step 10/10 and its closing
line, and the line the cockpit writes to its journal when it starts say Alzeo cockpit, and so do its HTTP Server
headers (AlzeoCockpit/ and AlzeoCockpitRelay/, were SparkCockpit/ and SparkCockpitRelay/) and the User-Agent of its
release check (alzeo-cockpit/, was spark-cockpit/): nothing on the box reads them, searched in the repository, the
box's tools and its other projects. Its violet is warmer, as chosen on screenshots on 2026-10-31: the ground #170b38
(was #120b36), the brand #9a6bff, #ccb5ff and #6a3fe0 (were #8c6cff, #c4b5ff and #5b3fd9), what floats #22164f and
#2c1d66 (were #1d164d and #271e62), the halos of the background, painted and drawn, moved the same way. The 27B lane
keeps its own hue. Every text keeps 4.5:1 over the brightest of the halos. Measured again on every view, desktop and
phone, with the tool of 2026-10-11 (the brightest 5 % of the background under the glyphs, six moments of the backdrop's
cycle), the warmer violet had put two texts under it: a danger button's label in Library on a phone at 4.45 (4.51 with
the old violet, the two releases measured side by side on the same box) and the in-flight label of Traffic at 4.11.
Their text is a little lighter, #ffe1dc and #ebe5ff, and the 22 views measured again have no text under 4.5:1, the
lowest at 4.70 (Now, on a phone). The in-flight label shows only while a request runs: its 4.76 is the measured
background with the lighter text, a projection that gave the button's 4.71 to within 0.01.
Checked after the rename. From the reference box, once the repository had its new name: the API answers the old
name with a 301 to the repository's id and the new one with the repository, and the cockpit's release check, which
still asks under the old name in v1.40.8, found the newest release. get.sh at the old raw address, at the new one, at
github.com's /raw/ form and at get.alzeo.ai was, byte for byte, main's. git ls-remote gave the same commit by either
name, and the box's clone fetched through its origin under the old name. In a browser, the repository's page, its
newest release, a discussion, a pull request, an issue and the CI badge, at their old addresses, led to the new ones.
And a real update by the command the cockpit of v1.40.8 gives, under the old name, ended whole in 89 s.
Tests. The tests that hold the one-liner, the update command, the refusal under sudo, the installer's lines and the
clone get.sh makes, the headers and the User-Agent expect the new ones. A test of install.sh's refusal held the host
only, and holds the whole command again. The page's own update commands, which no test held, are held too. The whole
suite passes locally, with no step skipped.
What an update does. get.sh, install.sh, the cockpit's pages and code, the README, SECURITY.md, the docs,
CITATION.cff, the issue form and the tests change. The cockpit restarts once, which drops its open pages and the Agent
tab's connection, and comes back as the Alzeo cockpit. No engine, no proxy and no unit file changes. A box keeps its
clone in ~/dgx-spark-qwen38 and its origin under the old name, which GitHub sends on.
After the push. The reference box updated at 214593a on 2026-11-01 with the command of this release,
cd ~/dgx-spark-qwen38 && curl -fsSL https://get.alzeo.ai | bash, once get.alzeo.ai served this release's get.sh byte
for byte. Its clone, whose origin still names the old repository, took the new commit through GitHub's redirect, and
the run ended whole in 96 s. The cockpit restarted once and came back as the Alzeo cockpit v1.41.0, with the Server
headers AlzeoCockpit/1.41.0 and AlzeoCockpitRelay/1.1. The proxy and Flash FP4-Local, the lane that served, kept
running, and no unit file changed. The 8 real clients (Claude Code, opencode, Hermes, pi, the OpenAI and Anthropic
SDKs, fetch under Node and Bun, LiteLLM) answered through the proxy on Flash FP4-Local, the production checks passed,
and the cockpit showed no banner on any of its 5 views, with no JavaScript error. GitHub's CI passed its 7 jobs, 89
steps, with no step skipped.
v1.40.8 (2026-11-01): get.sh takes a clone of hasso5703/alzeo for this repo, before the repository is renamed, and an update no longer stops in silence on a clone with thousands of untracked files
Why. The repository is to be renamed hasso5703/alzeo. GitHub then sends the old address on to the new one, for
git, for the raw files the one-liner fetches and for its API, as long as no other repository takes the old name. But
get.sh takes a clone for this repo only if its origin names hasso5703/dgx-spark-qwen38. Once the repository has its
new name, a clone made from the new address would have been refused in ~/dgx-spark-qwen38 at its first update ("not a
clone of this repo"), and, run from inside it anywhere else, passed over for ~/dgx-spark-qwen38, cloned under the old
name when missing, onto which the box moved.
get.sh. A clone whose origin names hasso5703/dgx-spark-qwen38 or hasso5703/alzeo is this repo, for good: every box
installed before keeps the old name in its origin, matched by the pattern it always had, any origin that contains
hasso5703/dgx-spark-qwen38. The new name is matched as the last two parts of the path, by HTTPS or SSH, with or
without .git, on any host as the old name always was (a mirror of this repository counts): hasso5703/alzeo-site,
xhasso5703/alzeo or someone/alzeo is another repository, which get.sh neither updates nor installs from. Both are now
matched whatever the case, as GitHub's names are (a clone of https://github.com/Hasso5703/Alzeo is this repo), which
for the old name only takes more, and the rule is written once, for the two checks that read it. A refusal shows the
origin without the user name, password or token an address can carry: a password holding an @, a /, a line break or a
byte that is not UTF-8, the user of an scp-like address, a remote helper's address and a token in the query string
included, and any form still holding an @ shows only what follows its last @. An ext:: address, a command that can
carry a password, is not shown at all. And under sudo get.sh names the one-liner as the README gives it, on
raw.githubusercontent.com, which serves a renamed repository's files under its old name too, as long as no other
repository takes that name (measured on 2026-11-01 on three repositories renamed before). Where get.sh looks for the
clone does not change: DIR when given, else the clone it is run from, else ~/dgx-spark-qwen38, so a clone of the new
name kept elsewhere, ~/alzeo for one, is updated by running the command from inside it, as the cockpit's update
command does. The folder ~/dgx-spark-qwen38 and the qwen38 names on the box stay as they are.
Updates that stopped in silence. With thousands of untracked files in the clone (a .venv-mirror/, which MIRROR.md
creates there and .gitignore did not list, holds that many), get.sh stopped after listing five of them, with code 141
and no error, before the update, and the box stayed on its release: the list closed its pipe early, and under pipefail
the writer's SIGPIPE ended the script. With hundreds of modified tracked files, its refusal ended the same way before
giving the way out. Both lists are now read to their end and print what they printed before. A folder that is
untracked whole is now named once in that list, and .gitignore leaves .venv-mirror/ out, beside .venv/. Found in
review of this release, in lines older than it.
Tests. Two tests fail on v1.40.7 and pass on this release. get.sh run against a bare repository named
hasso5703/alzeo, given as DIR and from inside the clone, goes through to the installer, and ones named someone/alzeo,
hasso5703/alzeo-site and xhasso5703/alzeo are refused. Twenty-six origins as git keeps them, HTTPS or SSH, with
credentials, .git, a final slash, a port, capitals or a .git folder, seven of them the old name as older boxes may
carry it, are each taken or refused as they should be, given as DIR and from inside the clone, and a refused clone,
given a local edit and an untracked file first, keeps its origin, its commit and every file as it was. A refusal shows
no secret in any of the forms above, a byte that is not UTF-8 included (under a UTF-8 locale, where the machine has
one). A clone holding 3,000 untracked files is updated and lists exactly the first five, a folder of 2,000 untracked
files is listed as one line, and a clone with 1,500 modified tracked files is refused with its first ten listed and
the whole advice: each list outgrows the 64 KiB pipe of a 4 KiB-page kernel more than twice, so a head put back fails
every run. get.sh is checked for the bash 4 and 5 constructs a script reaches for first, which bash 3.2, macOS's
/bin/bash, lacks (${x,,}, ${x@L}, [[ -v, declare -l and the like). Nineteen changes that each put one defect back fail
the tests: v1.40.7's get.sh, a head back in either list, sed's p flag dropped, the list without --directory, a refusal
that touches or empties the clone it refuses, the mask without its last-@ step, its line join, its query cut or its
ext:: rule, and, that step removed too, without its C locale, its remote helper's prefix or its last-@ rule in sed,
${1,,}, ${1@L} or [[ -v back in, and the old name matched with its case. Every run of get.sh gets PATH and nothing
else of the shell's environment, a throwaway home, no network protocol but local paths, and no repository looked for
above its folder, and the fixture's own git inherits none of the variables that point git at a repository, so no test
reaches the network, a real clone of this repo or a real installer. And the refusal under sudo names the one-liner the
README gives.
What an update does. get.sh, .gitignore and this changelog change, and the tests. The cockpit restarts once, to
name the new release, which drops its open pages and the Agent tab's connection. Nothing else restarts, and get.sh
updates the clone it updated before.
After the push. The reference box updated with the public one-liner at f528ef5 on 2026-11-01, through the get.sh
of this release, which took ~/dgx-spark-qwen38 as before, and the run ended whole in 91 s. The cockpit restarted once
and names v1.40.8, the proxy and Flash FP4-Local, the lane that served, kept running, and no unit file changed. The 8
real clients (Claude Code, opencode, Hermes, pi, the OpenAI and Anthropic SDKs, fetch under Node and Bun, LiteLLM)
answered through the proxy on Flash FP4-Local, the production checks passed, and the cockpit showed no banner on any
of its 5 views, with no JavaScript error. GitHub's CI passed its 7 jobs with no step skipped.
v1.40.7 (2026-10-31): on the Ornith lane a long prompt no longer holds up the other requests, and the cockpit says while a prompt is computed (#61)
Why. On 2026-10-30 a user of the Ornith lane on a 128 GB Lenovo box reported that requests stop answering once
their size in the cockpit's Traffic view nears 1835 KB, in a long chat in a WebUI and in Claude Code near 450,000 to
500,000 tokens (#61). Reproduced on the reference box the same evening, nothing hangs. A prompt of that size that the
prefix cache cannot serve is computed whole before its first token, and on Ornith that took 19 min for 494,089 tokens,
the time growing about with the square of the length. Through all of it the cockpit showed the request "in flight"
with no time, its last five minutes "quiet", and vLLM's own log printed 0.0 prompt tokens a second, as vLLM counts a
prompt's tokens once it is done. And Claude Code with its own default timeouts left such a turn after 620 s, twice,
then sent it without streaming and left after 300 s, twice: each request its client had left was closed, the engine
kept part of what it had computed only every other time, and the answer came at the fifth attempt, after 35 min. The
user then wrote that while such a prompt is computed, his other services and his other users' requests stall too,
small ones of 20 KB taking ages, then timing out and coming again. Measured on 2026-10-31 with 10 clients sending
requests of about 20 KB in a loop while a cold prompt of 362,694 tokens was computed: their 64 tokens came after 265 s
at the median and 484 s at worst, where ten such requests at once take 20 s with nothing else running.
The Ornith lane. Its engine starts with four of vLLM's scheduler settings. --max-parallel-prefills auto lets up
to four prompts share an engine step, round-robin, a prompt alone keeping the whole budget. --prefill-policy decode-aware keeps one of those places for the prompt nearest its answer. --prefill-compute-share 0.25 gives the
prompts a quarter of the time the engine is shared, and the answers being written the rest. And
--max-num-prefill-tokens-per-step 1024 caps a shared step's prompt tokens, a step the prompt has alone keeping its
4,096. On the same prompt and the same 10 clients, each setting started by the lane's own command: the 64 tokens came
after 34 s at the median and 62 s at worst instead of 265 and 484, the first token after 30 s instead of 101, and
16.7 requests were served a minute instead of 2.4. The long prompt took 1,653 s instead of 698, sharing the engine for
its first 20 min 35 s, until the clients stopped, and alone for the rest. Alone it takes
the time it took: 418 s for 277,608 tokens, 416 s with v1.40.6's settings, as vLLM schedules a step the prompt has
alone as before. Answers changed no more than the engine's own noise: two runs of the same ten prompts at temperature
0 on one engine gave the same text for two to five of them, and these settings against v1.40.6's for three, the
largest gap of a token's log-probability 0.50 against 0.54 between two runs of v1.40.6's own. A share of "auto"
served the small requests less well (86 s at the median, 161 s at worst), and steps of 8,192 tokens made a prompt
alone 5 % faster but kept 8 small requests of 80 waiting 668 to 777 s for their first token, so the lane keeps 4,096
(docs/ornith-lane.md).
The cockpit. A request in flight shows how long it has been so in the Traffic view. On the vLLM lanes, while one
request computes its prompt alone (no token written for 10 s), the Now view, that request's row in the Traffic view
and the Traffic view's state say how much of its prompt the engine holds, from the KV cache it fills and the prompt's
length the prefix cache counted when it was admitted, with a floor of the time left: "computing its prompt: 37 % of
523,914 tokens after 2 min 29, at least 16 min left", with 18.7 min left. The floor is read off the request's own curve (the time grows as
the tokens to a power k, held between 1 and 2), with the half second the cockpit reads the engine at and a block of its
KV cache counted against it, and rounded down, on the page too. The last five minutes say "computing a prompt" instead
of "quiet". It follows a prompt only from an admission vLLM's counters prove to be one request (one more running, none
waiting, none finished and none preempted between two reads), says it only while that request runs alone and before its
first token, and says no floor once another request has shared the engine with it. With small requests coming and
going beside a long prompt, a page that followed the last admission would have said "99 % of 4,812 tokens, almost
done" of a prompt of 388,753 that went on (found in review before the release, and replayed in its tests). A read the cockpit could not make changes nothing, and what it
knew is said no longer than 10 s after its last read.
The proxy (v6.49). A client that leaves a request of at least 256 KiB after at least 60 s and before any answer
(streamed or not, but not while its answer is relayed, nor while a starting lane holds it) has its prompt kept for two
minutes (its whole body hashed, but for the stream and the answer's length, which Claude Code's retries change),
and the same prompt sent again within them is written as "RESENT the prompt its client left without an answer after
631 s (1 time in a row, claude-cli/2.1.314)", with what widens that client's timeout: for Claude Code, claude-local,
or API_TIMEOUT_MS=3600000 with CLAUDE_STREAM_IDLE_TIMEOUT_MS and CLAUDE_BYTE_STREAM_IDLE_TIMEOUT_MS at 1800000. The
cockpit shows the lines of the last day as a banner. Nothing relayed changes.
Proved on the reference box. On 2026-10-31 at night, the branch's cockpit, served on a port of its own and acting
on nothing, watched Claude Code with claude-local's timeouts send a cold prompt of 523,914 tokens through the box's
proxy: one request, its answer after 1289 s. About every 2 min 40 the Now view said, for example, "37 % of 523,914 tokens
after 2 min 29, at least 16 min left" when 18.7 were left, then 52 % and at least 14 when 16.0 were, down to 99 % and
at least 35 s, and "almost done": every floor under the time that was left, the Traffic view saying "computing a
prompt" until the prompt was done, and no JavaScript error. The same afternoon, Claude Code 2.1.314 with its own default timeouts sent a cold
prompt of 1,762,107 bytes on Ornith through the branch's proxy: it left after 630.8 s and sent the same prompt again at
once, and the proxy wrote its RESENT line with the advice for Claude Code. The scheduler settings were measured as said
above. Then the lane's unit as this release writes it, started as is, served the 8 real clients through this
release's proxy, all OK: Claude Code, opencode, Hermes, pi, the OpenAI and Anthropic SDKs, fetch under Node and Bun,
and LiteLLM, its engine ready in 155 s.
Tests. 47 tests fail on v1.40.6 and pass on this release. The cockpit's reading of a prompt: the real counters of
the prompt of 494,089 tokens replayed, every floor under the time that was left, the exact edges of each rule, and a
request admitted beside others, a jump of two prompts at one read, a probe that came and went in the same read, a
prompt preempted and admitted again, a request waiting beside it, a read that failed, a decode that stalls after the
first token, and an idle lane that keeps nothing growing. The four counters read off vLLM's page. The page's words, the
floor rounded down and the banner. The proxy's RESENT line, from a real proxy in front of an engine that answers
nothing, every way the proxy ends a request on a client that left counted or not on purpose, and the fingerprint. And
the Ornith unit's four settings. lifecycle.py stays at 100 % of its branches.
After the push. The reference box updated with the public one-liner at a291a64 on 2026-10-31: the proxy restarted
once, on v6.49, and the cockpit once, and nothing else, the Ornith unit rewritten with its four settings while Flash
FP4-Local, the lane that served, kept serving. The 8 real clients answered through the proxy on Flash FP4-Local, the
production checks passed, and the cockpit showed no banner on any of its 5 views, with no JavaScript error. Then the 8
clients ran on the 7 text lanes, each loaded as the cockpit's Load does, on what the one-liner installed: 56 of 56 OK,
Ornith on its new settings among them. On every lane a tool result in the shape Claude Code sends after a ToolSearch
got an answer, the proxy wrote its line for a tool call naming an Alibaba Cloud host, and the prefix cache served, to
the token, the counts the v1.40.3 notes give, and Ornith decoded at 43.0, 42.4 and 42.5 tok/s (42.8, 42.7 and 42.2 in
v1.40.5's matrix). Last, the installed cockpit watched Ornith compute a cold prompt alone: "52 % of 297,464 tokens after
88 s, at least 83 s left", then 72 % and at least 71 s, 88 % and at least 38 s, every floor under the time that was
left (its first token came at 468 s), then "almost done". And a cold prompt of 264,382 tokens with 3 clients sending
small requests beside it for 5 min: while they ran, the page said only how many requests were running, and once the
prompt ran alone, "90 % of 264,382 tokens after 6 min 20" with no floor, then "almost done". It never named another
request's tokens.
What an update does. The proxy restarts once (v6.49), which ends a request it relays then, and so does the cockpit,
which drops its open pages and the Agent tab's connection. On a 128 GB box the Ornith unit is rewritten (a 64 GB box has
none): a box whose serving lane is Ornith keeps its running engine until its next Load, as the installer's NOTE and the
cockpit say, and has the new settings from there. Nothing else moves.
v1.40.6 (2026-10-31): the engines' own coordination ports stay off the network, where SECURITY.md said no setting could keep them
Why. SECURITY.md has said since 2026-09-30 that each engine opens its own coordination port on every interface, with
no key and no setting to narrow it, and advised a firewall: an engine of one process still runs torch.distributed's
store, which torch binds to every interface whatever address it is given. No model and no data sit behind those ports,
but a hostile connection can disturb the engine. There are settings after all. Measured again on the reference box on
2026-10-30, which has no firewall: the installed image and video lanes listened on *:30005 and on four ports of
192.168.1.71, the box's LAN address, the 27B lane on *:23906, a port drawn at each start, and under the real clients the
27B and flash lanes on four ports of 192.168.1.71 as well, though the box's name resolves to 127.0.0.1. A connection to
30005 from the box's LAN address and from its tailnet address went through. The vLLM lanes opened no such port.
The image and video lanes. Their units pin gloo and NCCL to lo (GLOO_SOCKET_IFNAME, NCCL_SOCKET_IFNAME) and, with a
bind on any loopback address (127.* or localhost, 127.0.0.1 by default), set IPAddressDeny=any with
IPAddressAllow=localhost: the kernel lets the lane's processes talk to loopback alone, so the store, which still listens
on every interface, is out of reach of any other machine (what one sends it is dropped, unanswered, and ss still lists
*:30005). A lane no longer fetches an image named by its URL: an edit or a reference sends the image itself, as the
cockpit always did. A bind past loopback (IMAGE_BIND or VIDEO_BIND) serves others on purpose: its unit's two filter lines
are written empty, which sets no filter, and the installer now says that port 30005 is then open to the network. The
rule is written once, in lanes.sh (lane_ip_deny).
The SGLang text lanes. The 27B lane, in all three of its units (the default, 1M of context, the 64 GB box), and the
flash lane pass SGLANG_DISTRIBUTED_INIT_METHOD_OVERRIDE=file:///tmp/sglang-rdv: their rendezvous goes through a file in
the container, which --rm makes new at each start, and no port opens for it. Both pinned images read it (the 27B's
v0.5.22 and the flash lane's nightly, srt/distributed/bootstrap.py). They pin gloo and NCCL to lo too: under the real
clients, both lanes had listened on four ports of the box's LAN address, though its name resolves to 127.0.0.1.
Proved on the reference box on 2026-10-30. The installed image lane with the change laid on as a drop-in: nothing
past loopback but *:30005, a connection to it from the box's LAN address and from its tailnet address dropped (no answer
in 4 s) where both went through without the change a minute before, one from 127.0.0.1 accepted, and a 512x512 image in
4 s. The video lane with the change: nothing past loopback but *:30005 either, the same two connections dropped and the
one from 127.0.0.1 accepted, and the lane answered 724 s after its start, against 691 to 715 s without the change.
During the v1.40.5 matrix the same day, the 27B lane listened on *:28195 and on four ports of the box's LAN address, the
flash lane on *:23283 and four more, and the five vLLM lanes on nothing past loopback. The four ports came only once
clients ran, 60 and 104 s after each lane was ready, so the text lanes were proved under the matrix's eight real clients
(through a second proxy, the file that serves, in front of a test engine), with what listened past loopback read every 5
s until 5 min after the engine was ready. The 27B lane started by its unit's own docker run as v1.40.5 has it listened
on its store and, 70 s after it was ready, on four ports of the LAN address. Started with the file rendezvous and gloo
and NCCL on lo (ready in 376 s), and the flash lane by the installed launcher with the same three variables (ready in
832 s), neither listened on any TCP port past loopback, and all eight clients answered on each.
Tests. Six tests fail on v1.40.5 and pass on this release: each diffusion unit pins the bootstrap to lo and renders
its filter for a loopback bind only, as the installer's own lines compute it for 127.0.0.1, localhost, 127.0.0.2 and
addresses past loopback, and says what a bind past loopback leaves open (image and video). An update restarts a serving
image lane that runs older files and leaves one that does not alone, tried on the block alone and on the installer's
lines with files older and newer than the lane, a restart that fails said as a NOTE (test_side_lane_proof.py, whose
NOTE for the image lane this release replaces). Every SGLang text start passes the file rendezvous and the two variables
once, in a container --rm makes new, with the reason in its unit (the 27B's three units, the flash lane's launcher and
run.sh). The conformity tests take the diffusion units' rule from lanes.sh instead of a copy, a rule v1.40.5's lanes.sh
does not have.
After the push. The reference box updated with the public one-liner at efadad7 on 2026-10-31: the cockpit restarted
once and nothing else, and Flash FP4-Local, the lane that served, kept serving. The 27B's unit, the flash lane's launcher
and the image and video units were rewritten. The 8 real clients answered through the proxy on Flash FP4-Local, the
production checks passed, and the cockpit showed no banner on any of its 5 views, with no JavaScript error. Then the 8
clients ran on the 27B and flash lanes, each loaded as the cockpit's Load does, on what the one-liner installed: 16 of
16 OK. On both, a tool result in the shape Claude Code sends after a ToolSearch got an answer, the proxy wrote its line
for a tool call naming an Alibaba Cloud host, and the prefix cache served the 8,192 tokens of a repeated prompt, as the
OpenAI and the Anthropic usage both said. What listened past loopback, read every 20 s through those 42 min, stayed what
listened with Flash FP4-Local before them, with no port of either engine, where during the v1.40.5 matrix each had
listened on its store and on four ports of the box's LAN address.
What an update does. A box whose serving lane is the 27B or the flash SGLang lane restarts that engine once, as its
unit changed (about 7 min for the 27B, 378 and 401 s on the reference box that day, 12 to 15 for the flash). A serving
image lane restarts too, in about 70 s, and an image being made then is lost (Hasan's choice, 2026-10-30). Every other
lane takes the change at its next start, a serving video lane included, as the installer says. The vLLM lanes do not
move. The cockpit restarts once.
v1.40.5 (2026-10-30): when the box and its clone are one release, the cockpit's update banner names the commits between them, and opencode 1.19.5
Why. On 2026-10-29 the box ran v1.40.4 as installed at 90bb772 while its clone had moved to d120eae, the note of
the same release: the orange banner said "the services run v1.40.4, the clone is v1.40.4", which named no difference
and left the next step to a guess.
The banner names the two commits and the ones between. When the release recorded in installed.json and the
clone's are the same, the banner says which commit the services run, how many commits the clone is past it and which
commit it is at, and lists the clone's commits past it, newest first, one a line, at most five and then how many
more. It says "past it", and lists those commits, only of a clone that descends from the recorded commit, as git's
merge-base tells: a clone reset to an older commit, or whose history was rewritten, gets the two commits named and no
list. The cockpit asks git about them only when both sides are one release, at most three short commands at each
collect.
A proxy test no longer fails on a loaded runner. tests/test_proxy_engine_withholds_headers.py read the proxy's
end line as soon as the engine had seen the client go, and on a loaded GitHub runner the handler thread had not
written it yet (2026-10-29). It now waits for that line, as the client-gone test of the same file already did.
The CI runs as six jobs side by side, on arm64. It ran as one job, 4,312 s for v1.40.3, the sum of its steps. It
now runs as six (static, suite, single, coverage, cockpit and fuzz), each on a runner of its own, on
ubuntu-24.04-arm, the architecture of the boxes this repo serves, and checks ends the run under the name it always
had: 14 min 35 s for the run that merged it. ./ci-local.sh runs the same jobs side by side, each in a worktree of
HEAD, so a full run wants a clean tree: about 13 minutes on the reference box, against 64 one step after the other.
Every step's script is the one it was. On one machine the jobs share its ports: the test files that took a port
number out of a socket they then closed, so that another job could be handed it in between, now take their ports from
tests/localports.py, and the browser checks start theirs on port 0 and read it back, or below the range the system
hands out.
opencode 1.19.5. It lets a terminal's ticket stand for the password only on the route the server dispatches the
request to, where 1.19.4 took it on any request whose path matched that route's pattern (anomalyco/opencode#58258).
It also answers a request of the TUI's worker that fails instead of leaving it waiting, and otherwise removes unused
code. It was checked against 1.19.4 side by side on 2026-10-30: the overflow phrases and exclusions are the same block
of the binary byte for byte, max_tokens 32,000 and 182,000 on a fake endpoint with the same headers, the same default
permissions, --yolo still read as --auto, keys still masked, sessions read both ways, and the Agent tab through a
copy of the relay with its terminal and its phone layer. A fake endpoint that asked for a shell command naming an
aliyuncs.com host had it refused, under --yolo and under AGENT_AUTO's permission, where a command naming no such
host ran. On the box's FP4-Local lane both sent the same request shapes, read hello.txt with a tool and answered 7351.
Tests. Six tests fail on v1.40.4 and pass on this release: three of the cockpit's (the commits between the two, a
recorded commit git no longer knows, a clone off the recorded commit's history) and three of the page's (the commits
one a line, the count past five, both commits named when no list is known). Four more hold what this release got
wrong on its way and v1.40.4 never did: git asked when the clone names another release, a recorded commit that is no
string or no hash, and a commit subject git cannot convert to UTF-8. The opencode pin's test fails on the installer
before it. The page's banner check passes 30 of 30, and the banner was seen on a cockpit served from the branch,
desktop and phone, with no JavaScript error.
Proved on the reference box. After the push, the box updated with the public one-liner: the cockpit restarted
with its new page and server, then opencode-web on opencode 1.19.5, and nothing else. The 8 real clients answered
through the proxy on the lane that serves, Flash FP4-Local, the production checks passed, and the cockpit showed no
banner on any of its 5 views, with no JavaScript error. Then the 8 clients, opencode 1.19.5 among them, ran on the 7
text lanes, each loaded as the cockpit's Load does, on what the one-liner installed: 56 of 56 OK on 2026-10-30. On
every lane a tool result in the shape Claude Code sends after a ToolSearch got an answer, the proxy wrote its line for
a tool call naming an Alibaba Cloud host, and the prefix cache served, to the token, the counts the v1.40.3 notes give.
What an update does. opencode 1.19.5 replaces an older one in ~/.opencode/bin (a newer one is kept). The cockpit
restarts once, with its new page and server, and then opencode-web once, on the new opencode, which ends a turn the
Agent tab is in. Nothing else: the lane that serves keeps serving, and the lane chosen for boot stays the one it was.
v1.40.4 (2026-10-28): opencode on this box refuses a shell command that names an Alibaba Cloud host or a cloud metadata address, AGENT_AUTO included, the cockpit says when a model asks for one, and pair-probe.py reads what a pair of boxes needs (#45)
Why. On 2026-10-26 Cohere reported that Qwen3.8-Flash-Next, given a shell in an agent test without a
network, sent a PUT to an Alibaba Cloud OSS bucket in Beijing, in fewer than 1 in 100,000 calls. The behavior
behind it, an Alibaba Cloud host the model writes on its own, has turned up in builds from four publishers,
Alibaba's included. Read in opencode 1.19.4's binary: its permissions start from "*": "allow", so every
shell command runs unasked in opencode, in oc (--yolo, an alias of --auto there) and in the Agent tab.
On the box, opencode run under the Agent tab's permission ran an echo naming an aliyuncs.com host.
The box's opencode configs deny those hosts. The rules "*aliyuncs.com*", "*169.254.169.254*" and
"*100.100.100.200*" (Alibaba Cloud's whole domain, the metadata address of Cohere's report and Alibaba
Cloud's own) are deny, under a key of their own, "bash*" (a wildcard that matches the shell tool), as the
last member of "permission", since opencode's last matching rule wins: a "*" after them would allow what
they deny. They are in the config install.sh generates, which oc and the Agent tab load over your own, so
they come after your global config's rules there, but for a key added after the block in a global config that
holds it too (this repo's copy does), until the next install puts the block last again. They are also put last
in your global config, the one plain opencode reads, when it is this repo's copy or lists this repo's
providers (oc-merge-limits.py --deny-bash): a key you add after them later is overridden at the next
install, your own "bash" stays as you wrote it, and a pattern you set inside the block stays yours. A config
of your own, in which none of this repo's provider names shows, never gets the rules. Measured on the box with
opencode 1.19.4 and Flash FP4-Local: the same echo is refused under oc's --yolo and under the Agent
tab's permission (opencode run with its OPENCODE_PERMISSION).
AGENT_AUTO=1 no longer overrides them. opencode merges OPENCODE_PERMISSION after every rule of its
config files, so the unit's bare {"*":"allow"} beat any deny of a config without a "*" of its own: the
echo ran under it, and SECURITY.md said your own deny rules held there, which they did not. The unit now
repeats the rules after its "*", under "*bash", and the echo is refused. Under AGENT_AUTO=1 your own
rules still give way to its "*", unless your config has a "*" of its own before them, which SECURITY.md
now says. The cockpit still reads that mode as auto.
The proxy says when a model asks a tool to reach one of them (v6.48, its role 8). The proxy sees every
tool call before the client runs it, whatever the client. The arguments of each call, put back together from a
stream (OpenAI's tool_calls, Anthropic's input_json_delta, the Responses API) or read from a whole answer
on the generation routes that names one of them, are matched when the request ends against aliyuncs.com and
the two metadata addresses. A match is one journal line, the host and the tool's name, never the arguments,
and the answer is relayed as it came. A tool that reads, searches or writes files, or hands work to a
sub-agent, is not read, by its exact name: a page that quotes these hosts, or a search for them, is no request
to them. The cockpit reads a day of those lines every 5 minutes and shows a red banner with the count, the
host, the tool and the route. It says what the proxy saw, not what the client did with it.
What it does not cover. opencode's patterns tell case apart: a host written in capitals is not refused,
and the proxy, which does not, still says it. Neither stops a model that writes the address another way (a
variable, an encoding, a script it writes first), and the rules leave opencode's webfetch alone. What
opencode reads after the rules can allow what they deny: a project's own opencode.json or .opencode
directory, an agent's own permission block, and an "Always" given to a command. The proxy does not read the
tool arguments of a streamed request past its first MiB, nor a whole answer over 8 MiB.
docs/agents-and-network.md says what runs unasked and how to approve each
command. In a config of yours the rules stay in place after uninstall.sh, as autoupdate does, and this
repo's own copy is moved aside whole, as before.
The memory pages say which memory the cgroup misses. It is the engine's own GPU memory, plain cudaMalloc
on GB10, while managed and pinned memory are charged to it, as another owner measured on the NVIDIA forum
(t/387182). The old text said "unified allocations". A
host-memory runaway is what the Docker cap stops, not any runaway, and 21.30 is GiB everywhere.
Found in review before the release. A named client's lines (key=<label>) were never read by the cockpit.
--deny-bash called rules "already there" that a later key overrode. A whole answer of an unexpected shape
raised where the request ends, before its end line. SGLang writes "tool_calls":null in every chunk, so the
filter meant to skip events without a tool call parsed every token. The bare word "routify" matched the Svelte
framework of that name, and an agent editing a page that quotes these hosts raised the banner. A second review
found more, fixed the same way: a key that allows only some commands, put after the block, was not seen.
write_stdin, which types into a shell, was taken for a tool that writes files, by a pattern on part of its
name, and a search for the hosts raised the banner. A client's text the proxy logs could forge a line the
cockpit counted. A large file written in a stream filled the budget and hid a shell call after it, calls a
parser streams without an index merged, a read of the journal that failed kept lines older than a day, and
every whole answer was parsed. A third review found the file and search tools of Hermes and pi taken for tools
that act, a host over short reads of a whole answer unseen, a parser that repeats a tool's name on every piece
splitting one call, spaces around "tool_calls" hiding it, and a journal vacuumed after a line keeping the
banner up a day: journalctl -g ends 1 when nothing matches, as a read that failed does, so the day is read
whole. A fourth review found two calls a parser streams at one index merged under the first one's name. The
journal line now says how the request ended. Each has its test. Three verifications before the push, read in
opencode's own code and replayed against it, found what these notes claimed beyond it: patterns that tell case
apart, a project's own config, an agent's own permission and an "Always" answer that come after the rules, and
an uninstall.sh that kept this repo's copy of the config, now moved aside again. The notes above say each,
and the uninstall has its test.
A probe for a pair of boxes (#45). pair-probe.py, run on each box, prints what a lane over two boxes
needs to know about each one: the DGX OS update it runs, the kernel, the GPU driver, this repo's release and
lanes, the ConnectX-7 ports, their RDMA side and the mlx5 modules, Docker, the time sync and the free disk,
and with --peer the port the route to the other box takes, its loss and its round trip. It needs no sudo and
changes nothing. It never prints a MAC or IP address (only its kind), the home directory, the host's or the
user's name or the serial number, and it opens no file that holds a key. A name every such box prints of
itself (spark, gx10, nvidia, ubuntu) stays, since it says nothing of whose box it is and masking it erased the
product and the kernel the two boxes are compared on. On the reference GX10, lspci shows no ConnectX-7
although the mlx5 modules are loaded, and the probe says both. Three reviews of the probe found what it missed
before the release, from a peer that does not answer read as not measured to a host's name written in another
case and a version of five parts read as an address. Each is fixed with its test.
Proved on the reference box. This release was installed on the reference box from its branch, and the 8 real
clients ran on the 7 text lanes there before it went to main. After the push, the box updated with the public
one-liner, the 8 real clients answered through the final proxy on the lane that serves, Flash FP4-Local, and the
cockpit showed no banner on any of its views. Then the 8 clients ran again on the 7 text lanes, each loaded as the
cockpit's Load does, on what the one-liner installed: 55 of 56 OK on 2026-10-29, and on every lane the proxy wrote
its line for a tool call naming one of those hosts. The one miss is Kolibri in Claude Code, which answered 1, the
line number in the Read tool's answer, instead of the number on that line. The tool's answer reached the model
whole, and the same question asked 5 times the night before and 5 times after this run got the right number 4 times,
then 3: each miss gave the line number, or glued it to the number (17351). Four code reviews read the release and
three more the probe, and three independent verifiers read the update and a fresh install, the security, and the
notes with the tests. Each defect they found is fixed with a test that fails on the code before it.
What an update does. The proxy (v6.48), the cockpit and the Agent tab's server restart, and nothing else: the
lane that serves keeps serving, and the lane chosen for boot stays the one it was.
Tests. test_oc_merge_limits.py (the order, the forms, a key added after the block, a user's own
pattern), test_install_opencode_pin.py, test_install_oc_artifact.py, test_agent_auto_deny.py (three
configs, one ending with a "*" of its own), test_uninstall_opencode.py (this repo's copy with the rules
goes aside, rules of the user's keep the file), test_proxy_tool_targets.py (a real proxy, the host split
between two piec...
v1.40.3 (2026-10-26): Claude Code's ToolSearch works on the Qwen lanes, every lane says what its prefix cache served (#49), and a stopped generation never reaches its client as a whole answer
Claude Code's ToolSearch works on the Qwen lanes. Claude Code 2.1.304 answers a ToolSearch call with
tool_reference content blocks. The Qwen chat template's render_content loop raised "Unexpected item
type in content.": a 500 before generation on the SGLang lanes (seen live on 2026-10-25 at 01:31, on every
turn of a claude-local session), and a 400 on Flash FP4-Local's, measured on 2026-10-26. The fifth
patch-template.py patch renders each referenced tool as a text marker, so the model sees what the search
loaded, and the schemas arrive with the next request's tools. The 27B, flash and Flash FP4-Local templates
take it with their other fixes, and the Ornith lane, whose loop is the same, now asks for it beside its
system-reminder fix. Kolibri's and the two Nemotron lanes' templates take such a block as they are. The same
ToolSearch result, replayed through the proxy on each of the seven text lanes of the reference box, was
answered on all seven (HTTP 200). A next checkpoint whose template raises where the fix cannot reach now
stops the install instead of serving that error again. A session already carrying such a block needs a
new session until its lane restarts with this release.
Every lane says what its prefix cache served (#49). The flash launcher starts SGLang with
--enable-cache-report, as the 27B's units do since v1.40.2, and the Kolibri, Nemotron Bolt, Super VL,
Flash FP4-Local and Ornith units pass --enable-prompt-tokens-details: each answer carries
usage.prompt_tokens_details.cached_tokens, and cache_read_input_tokens beside input_tokens on
/v1/messages. The engines counted those tokens all along and said nothing. Measured on each lane through
the proxy, with two requests sharing an 8,000-token prefix and ending differently, then the same two prompts
again on /v1/messages, the count is what each engine's cache served, and its time says the same:
- the 27B and the flash: 8,192 of the second request's 8,248 tokens (0.5 s, against 4.0 to 4.5 s for the
first), and 8,192 again on/v1/messages. - Ornith: 4,224, one whole block of this hybrid model (3.0 s against 4.5), and 8,128 of 8,150 on
/v1/messages. - Kolibri, Flash FP4-Local and Super VL: 32, 0 and 0 for the request that ends differently, which took as
long as the first: these engines reuse a prompt only from the points their cache keeps, and that request
passed none. The same prompt again came back from the cache whole but for a few tokens: 10,880 of 10,892,
8,192 of 8,192 and 7,168 of 7,256, in 0.5 to 1.0 s. An agent's conversation, which grows at every turn,
is what those points serve. On Flash FP4-Local such a conversation got 7,971 and then 8,001 of its 7,996
and 8,023 tokens from the cache on/v1/chat/completions(0.5 s a turn, against 4.0 s cold), and 7,980
of 8,023 and 8,047 on/v1/messages, the engine's own counters saying the same. On Super VL, all but
76 to 168 tokens from the third turn (docs/nemotron-super-lane.md). - Nemotron Bolt: 0 for all four. Its drafter keeps the cache's hits on 4,544-token blocks, one block back,
which a prompt of 7,256 tokens does not reach (an agent's 40,000-token conversation got 31,808 from it,
docs/nemotron-lane.md).
Decode does not move where a reference exists. bench.sh three times on each lane gave 68.2, 67.4 and 68.1
tok/s on the 27B (67.0 to 69.6 on 2026-10-23, the same engine), 56.8, 54.2 and 54.4 on Flash FP4-Local (55.1
to 55.4) and 43.8, 43.8 and 43.7 on Ornith (42.2 to 42.4). The other lanes had no bench.sh reference, and
these are theirs now: the flash 50.4, 51.5 and 50.1, Kolibri 48.9, 48.8 and 48.9, Nemotron Bolt 102.2, 100.7
and 100.5, Super VL 57.8, 56.7 and 55.6.
A cut answer is never handed over as a whole one. SGLang ends an aborted generation like a finished
one: stop_reason end_turn on /v1/messages (its STOP_REASON_MAP has no abort) and a last chunk with
finish_reason "abort" on the OpenAI routes, which LiteLLM reads as stop. The cockpit's memory floor
aborted at the engine directly, so each client kept the part generated before the stop as its whole answer:
on the reference box on 2026-10-25 at 07:42, Claude Code kept a compaction summary cut in the middle of a
word. The floor and its Abort-all now ask the proxy (POST /proxy/stop, loopback only, engine key
required, fail-closed): each relayed generation is stopped the way its engine has (by rid on SGLang, by
closing the connection on vLLM) and its client's connection is cut, so Claude Code, opencode, pi and Hermes
retry and get the whole answer with no duplicate. SGLang's abort_all still goes at every floor stop, for
the generations sent to the engine straight (bench.sh, needle.sh, an eval), which no relay can cut, and
the cockpit sends it itself when the proxy's did not go, as it did before. After a floor stop that cut a
generation, new generations are refused with 529 (503 off /v1/messages) and a Retry-After for the floor's
pause (60 s), then while MemAvailable stays under the floor, 5 minutes at most: agents retry within 1 to
3 s instead of burning whole prefills. Memory alone refuses nothing, so an idle box short of memory for
other programs is served as in v1.40.2. In the proxy's unit, PROXY_MEM_FLOOR_GIB=0 keeps the refusal for
memory off and PROXY_FLOOR_HOLD_S=0 the pause. The reviews before this release shaped all of this
(2026-10-26: a certainty review, two independent verifiers, then code reviews of the release and of its
fixes, each finding fixed with a test that fails on the code before). A floor stop that found nothing to
cut holds nothing back, a silly body (an infinite hold_s, a number of 310 digits, a chunked body) is
refused or read as the default instead of blacking the box out, each stopped generation is marked before
the engine hears of it, a vLLM request still in its prefill closes at once, one still on its way to the
engine is ended once its connection opens, abort_all goes first, the aborts by rid only when it did not
(each in its own thread, the cockpit having its answer without waiting for them), and the floor never
claims an abort it could not make: on a vLLM lane where no generation ran through the proxy it says the
abort failed, and its event and banner say how many were cut and whether new requests wait. One thing to
know: "Abort all" cuts each client too, so an agent retries its request (end an agent's turn in the agent),
and the cockpit still refuses it while a vLLM lane serves.
bench.sh names what holds a CPU core before it measures, and says when it could not look. On a GB10 the
CPU and the GPU share one power budget, so a stray spinning process lowers what the engine measures and
shows only as a load average (three abandoned command-line calls each held a core for four to six days on
another Spark in October 2026). bench.sh spends a 3 s window (HOGS_WINDOW) while the engine waits for
its first request and names every process outside the engine at half a core or more. cpu-hogs.py does the
reading from /proc. A cpu-hogs.py that dies (missing file, crash, a non-numeric window) no longer reads
as a quiet box: the report says cpu-hogs failed (exit N: its reason), the quiet outside the engine is NOT proven instead of claiming nothing held a core. The report names a busy process by its pid and name, never
by its command line, which can hold a token and gets pasted in public: python3 cpu-hogs.py prints the
command lines, on the box. A process name the kernel cut inside a character no longer makes it fail, and
its window is 0.5 to 60 s.
A greyed Qwen4 27B card says coming. Qwen named a Qwen4 27B at Apsara (22/09) with no date and no
weights. The cockpit shows a dimmed, dashed card after the 27B it will follow, saying no date and loading
nothing until the release that serves it.
The README links the website. https://hasso5703.github.io/ has every model measured, guides that answer
what owners ask, and every GB10 machine.
CI survives the clocks going back. The logs test waited for "CEST" where the box now says "CET": it
would have failed every CI until spring. The stamps themselves were right all along. And the offline suite
touches nothing outside itself again: the class in tests/test_tools.py that sets QWEN38_API_KEY now
restores the environment like every other class of the file.
Proved before the push. This release was installed on the reference box from its branch and proved
there before it went to main: the 8 real clients on the 7 text lanes, each loaded as the cockpit's Load
does (56 of 56 OK on 2026-10-26), the cache report and the ToolSearch result on each lane, and bench.sh
three times on each. A code review and two independent verifiers read it first, one on an existing box's
update lane by lane, one on a fresh install, a 64 GB box and security, then two more code reviews read the
fixes. Each defect they found is fixed with a test that fails on the code before it, and what waits is
below. After the push, the reference box updated with the public one-liner (the proxy and the cockpit
restarted, nothing else), the 8 real clients answered through the final proxy on the lane that serves,
Flash FP4-Local, and the cockpit showed no banner on any of its views.
Known, for the next release. "Abort all" on the vLLM lanes through the proxy, which can stop their
generations by closing their connections. A stop when the proxy listens elsewhere than 127.0.0.1:30001, or
behind its optional TLS: the cockpit falls back to the engine's abort_all, and an SGLang client then gets
the cut answer as a whole one, as in v1.40.2. /v1/systemone is neither refused under the floor nor
reached by a stop, and /v1/tokenize is counted as a generation. The floor's line in the Machine view says
it stops through the proxy even when the proxy cannot take the stop. A non-streamed request whose client
sends bytes while it waits, stopped before its connection to the engine opens, is cut o...
v1.40.2 (2026-10-24): opencode runs on the Ornith lane again, claude-local asks before each command and keeps a session on the box, and CI renders the real clients' message shapes through every lane's chat template, as each engine hands them
opencode on the Ornith lane. opencode 1.19.4 sends two system messages before the user's
(captured on the reference box on 2026-10-24, roles only), and the Ornith 1.5 122B checkpoint's
published chat template refuses the second ("System message must be at the beginning."): every
opencode request on that lane got a 400, and the Agent tab with it, from v1.38.6, which moved
opencode from 1.18.35 to 1.19.4, to v1.40.1, whose notes said so. With 1.18.35, in v1.38.0, opencode
ran four turns on it. The lane's installer now writes the checkpoint's template with the one fix the
27B and the two flash lanes already serve theirs with: a system message past the first one becomes a
<system-reminder> block (patch-template.py --patches=system-reminder, for a template with no
reasoning effort of its own). It writes it at every install and every switch, and on an install of
the units alone when the pinned checkpoint is in the cache already (otherwise the lane's first Load
writes it before it starts the lane), and the unit mounts it read-only and passes it to vLLM.
Rendered with transformers 5.12.1's own template compiler on the pinned template, opencode's shapes
raised before and render now, and a conversation with one system message renders byte for byte as
before. Proven end to end on the reference box before the tag: the lane restarted on its new unit
and template, and served 2 min 50 s later, its container with --chat-template and the template
mounted, and the eight clients ran on it, opencode among them.
How it got through, and what now stops it. Each release proved the real clients on the lane that
served that day: opencode's move to 1.19.4 was proven on another lane, and nothing held a new lane
to the template fix the others had. A new CI step, tests/lane-templates-check.py, fetches every
text lane's chat template at its pinned revision (the seven targets of the 27B and flash lanes, and
the six of the vLLM lanes, Kolibri's FP8 and NVFP4 each at its own pin), as transformers assembles
it, patches it as its installer does, and renders the message shapes of the real clients
(tests/fixtures/client-shapes.json, roles captured on the reference box: opencode's two requests,
Hermes Agent and pi with a tool round and without, the OpenAI SDK, fetch and LiteLLM, and two
derived from them, opencode's tool round and an agent's reminder after its first turn), with tools
and without, sent as strings and as lists of parts, and handed to the template as each engine hands
them. Both engines' code for it is ported from their sources at the versions the lanes pin: vLLM
reads the template, gives one that loops over the content every content as a list of text parts, and
joins them for any other, SGLang leaves a string a string, and both make a tool's answer one string.
It renders as transformers does, with the template named tool_use when tools are sent. It renders no
request's own parameters yet (a reasoning effort, thinking on or off), and a template that refused
one of them would pass it, nor a vision lane's processor template, which vLLM reads first and which
is the tokenizer's own on the two Nemotron lanes today. On the reference box, its 364 renders, none
refused, were identical to transformers' own, and its reading of each template is the one its
engine's own code gives, run from its sources. With the Ornith lane served as v1.40.1 served it, the
step fails: 16 of that lane's 28 renders refused. It also fails on a text target of
switch-model.sh it does not know, on an installer that serves its template otherwise than its
table says or runs patch-template.py outside its TEMPLATE_FIXES, on install.sh or
switch-model.sh running it with fixes of their own, and on a patched lane without the
chat_template.jinja its installer patches. It first renders a template that refuses a second system
message, and fails unless it sees that refusal: a renderer that saw none would pass anything. A 429
from Hugging Face is waited out until the end of the window its RateLimit header names (up to five
minutes), and a brief 5xx or a cut connection is tried again. Before each release, the eight clients
now also run on every text lane, each loaded in turn.
claude-local asks before each command, and keeps a session on the box. An account set to Claude
Code's auto mode gave claude-local's sessions auto mode, where the box's own model judged which
commands ran unasked (auto mode's classifier requests go to the box like every other request), and
its notice ("this session isn't eligible") held the first command until Enter. A session now starts
in the normal permission mode, which asks before each command, unless you pass a mode of your own
(--permission-mode auto, --dangerously-skip-permissions). Claude Code's own commands, their
aliases (kill, plugins, upgrade) and what it reads as its first argument but its help does not
list (daemon, Remote Control's remote-control, rc, remote, sync and bridge, and
self-hosted-runner) get none of a session's flags: a flag in front made claude-local kill <name>
open a session where it should stop one (a short id still passed), and --update alone gets none
either. agents, which starts sessions of its own, gets them, and hands them to those sessions. And
nothing of a session goes to Anthropic, so nothing is billed there, nor to the other providers
Claude Code reads from the environment. The model's requests went to the box already, and Claude
Code's nonessential traffic was off already, unless a settings file emptied its variable.
claude-local also turns off the claude.ai connectors (ENABLE_CLAUDEAI_MCP_SERVERS=false), the
official marketplace's registration, auto mode's request for the server's checks, which the box
cannot give, and its notice with it (CLAUDE_CODE_AUTO_MODE_SERVER=0), WebFetch's hostname check at
api.anthropic.com (skipWebFetchPreflight), and Claude Code's other providers (Bedrock, Vertex,
Foundry and the others it reads before the endpoint, left set in a shell or a settings file). The
settings it passes carry the same environment, with the endpoint and never the key: Claude Code
applies a settings file's env over its own, and a user's settings with that traffic variable empty
turned it back on, where these come after it (claude doctor, 2.1.304). With settings of your own
(--settings), claude-local passes none, and these are yours to set. A settings file of yours that
sets its own key or model (ANTHROPIC_AUTH_TOKEN, ANTHROPIC_MODEL) still wins over
claude-local's, as the key cannot ride on the command line: leave them out of it. Traced on the
reference box (every socket call of a session, the user's own configuration): asked to read a page
with WebFetch, the old claude-local looked up api.anthropic.com and connected to it, the new one
neither, and still answered. Your own MCP servers stay. Claude Code's built-in WebSearch is run by
Anthropic's API, and a local engine refuses its request (a 400 on the Ornith lane, 2026-10-24): an
MCP server of your own does the searching instead.
install.sh names the images it pins. Its help still offered IMAGE=lmsysorg/sglang:v0.5.21 and
dated the 27B image's validation 2026-10-09, and the advice a failed docker pull gives named
v0.5.21, though the pin has been v0.5.22 since v1.39.0: both now say v0.5.22, validated on the
reference box on 2026-10-23, and the file's header keeps no date it cannot source. The box itself
prepared it: the Ornith lane, through claude-local, in two passes, the second after a review that
corrected its date. The flash lane's advice offered dev-qwen38-next-local, its image of v1.8: it
now says that the pinned image went with its dated tag, and to update the repo.
Reviewed before the tag. This release went to main first as it stood after its CI, then through
a code review and two independent reviews, and its fixes through the same again, and these are fixed
here before its tag. A vLLM lane that starts at boot, updated while its checkpoint is out of reach,
keeps the unit it has and says so: it got a unit mounting a template not yet written, which docker
refuses at the next boot, and the box served nothing until a Load. The fixes a template takes are
chosen on patch-template.py's command line: TEMPLATE_PATCHES, read from the environment, made
./install.sh write the 27B's template without its effort fixes when a shell had it set. The Flash
FP4-Local and Ornith lanes write their template through one function of vllm-lane.sh. A test
reloaded the box's systemd through the system's sudo, and runs on stubs now. And the claude-local
commands and settings, the flash advice, opencode's second request, Hugging Face's window and the
check's reading of templates and messages, above. A box that took this release as first pushed while
its Ornith lane started at boot with the checkpoint out of reach got a unit that mounts a template
not yet written, and that lane does not start until a Load, or ./install.sh once the checkpoint is
back, writes it (one whose image had been pruned instead gets its template at this update).
What an update does. The cockpit restarts, to name the new release. The Ornith lane's unit and
template change. A box that serves Ornith keeps serving it with the template it booted with, and the
installer says so: Load it again from the cockpit, or run sudo systemctl restart qwen38-ornith-122b.service (it served again after 6 min 17 s, and after 2 min 50 s, at two restarts
on the reference box on 2026-10-24), and opencode runs on it. A box that serves another lane keeps
it running, and Ornith's next Load takes the new unit. The 27B, flash and Flash FP4-Local templates
come out byte for byte as v1.40.1 wrote them (their eight targets, compared at their pins). The
proxy and the Agent tab's server do not change. claude-local runs the new launcher at its next
start.
Tests. Each change has tests that fail on v1.40.1: `tests/test_patch_template....