Commit Graph
552 Commits
Author SHA1 Message Date
Ilker ManapandClaude Opus 5.5 0bf7e46fbf llmgw ctl tool-check asks with the engine's key
Every replica's engine demands its own key, and the check sent none, so it
failed with a 401 on every engine in the cluster. The key is asked of the
admin API for the replica at --endpoint, as bench does, or given with --key.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 11:24:37 +02:00
Ilker ManapandClaude Opus 5.5 b0273ffb5f Qwen3Guard is the judge, and judges on a GPU and on CPUs are asked in turn
Llama Guard 3 1B could not tell ordinary coding requests from harmful ones:
on the PoC it scored "continue" 0.51, "continue the code above" 0.89 and
"how do I kill all child processes in bash" 0.92 unsafe, against 0.97 to
1.00 for harmful requests, at Q4_K_M and Q8_0 alike, with its own template.
A site had "continue" refused. Qwen3Guard-Gen 0.6B gave the ordinary ones
0.15 at the most and the harmful ones 1.00, in 0.8 GB on CPUs.

- format qwen3guard: its prompt written out as its template writes it, for
  requests and answers, and its Safe / Unsafe / Controversial verdict read;
  controversial counts as the policy's judge.controversial says, safe by
  default.
- judge.models: judges asked in turn, the first that answers deciding; a
  failure is recorded against the judge that failed. guard judge use sets
  the order, replacing categories none of the judges names.
- known judges (Qwen3Guard 0.6B/4B/8B, Llama Guard 1B/8B): guard judge
  enable serves any of them, --gpu the 4B on the roomiest GPU class as a
  standard model; the leader fetches and serves one a policy names that the
  catalog lacks.
- server init installs qwen3guard-0.6b with the baseline's categories.
- a judge reads a long text by its start and end, which fit its context,
  rather than overflowing it and refusing the request under fail: closed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 10:22:47 +02:00
Ilker ManapandClaude Opus 5.5 63f7c8cfd7 llmgw start --wait waits for the models, not for the setting
CI / test (push) Successful in 3m57s
Every model of a stopped system asks for no replica and so has what it asks
for. Until the control plane took the setting up, seconds later, start
--wait saw 18 of 18 satisfied and returned at once, with nothing started.
It now counts only once the control plane says it is in the state asked
for, both ways.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 21:43:45 +02:00
Ilker ManapandClaude Opus 5.5 2d14918b99 An adopted engine that turns ready after registration is reported
The registration carries each shard's state as it is then. An adopted
engine is health-checked after that, and nothing reported the ready that
followed, so when the agent registered first the control plane kept the
replica starting while it served. After llmgw restart on the PoC two
replicas stayed so, one the only replica of qwen2.5-coder-14b. The
session's watcher now reports every shard the registration called not
ready once it is.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 19:43:13 +02:00
Ilker ManapandClaude Opus 5.5 3f8cc06f72 llmgw restart goes on past a control plane it cannot reach
One standby that ssh could not log in to stopped the whole restart before
this control plane's own. It is now said and left, the rest restarts - the
leader last - and the command fails at the end naming what was not
restarted and how to do it there.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 18:55:52 +02:00
Ilker ManapandClaude Opus 5.5 7245dbe1dd llmgw stop, start and restart act on the whole system
Issue #3 asked for commands to stop and start the system, and 05aa21c gave
only one machine's services. Now:

- llmgw stop sets serving.stopped in the cluster's settings, live: the
  reconciler wants every model at no replicas, freeing every GPU, and the
  gateway answers every POST under /v1/ with 503 system_stopped. The control
  planes, agents and mirror keep running, and no model's definition changes.
- llmgw start clears it; every model is placed again as it asks.
- llmgw restart has every agent restart over its stream (RestartAgent), then
  the other control planes over ssh, standbys before a leader, then this
  one; engines keep serving.
- llmgw status is the cluster's summary, saying when it is stopped.

One machine's services moved to llmgw service status|start|stop|restart.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 18:01:45 +02:00
Ilker ManapandClaude Opus 5.5 05aa21c4d4 llmgw status, start, stop and restart for this machine's services
CI / test (push) Successful in 3m44s
There was no command to stop or start llmgw on a machine (issue #3). These
act on the llmgw units the machine has - mirror, server, agent - or those
named, in dependency order, saying what each did. stop leaves a node's
engines serving unless --engines.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 14:52:52 +02:00
Ilker ManapandClaude Opus 5.5 2f595f5999 An adopted engine's state is written without its command; engine-check finds the installed build
CI / test (push) Successful in 3m46s
SetCompute on an engine adopted from an earlier agent called writeState,
which read the command line from sh.cmd - nil for a process this agent did
not start - and the agent crashed at every start on the PoC's nodes until
fixed. An adopted shard now keeps the command line its state file had.

engine-check looked for llama-server one directory too shallow; the agent
installs it under <version-variant>/llama-<build>/. It now looks at both,
preferring a CUDA build on a machine with NVIDIA GPUs. Run on cuda01: a
token computed on CUDA0, 25 of 25 layers offloaded.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 14:26:28 +02:00
Ilker ManapandClaude Opus 5.5 b5d650af16 What a replica computes on comes from nvidia-smi; llama.cpp says its layers only at -lv 4
Checked against the installed llama.cpp (b10991): at its default verbosity
it prints neither the devices it uses nor the layers it offloads, so the
start-up output said nothing and every replica's ON column stayed empty.
The agent now takes compute from nvidia-smi - the GPUs the shard's processes
hold memory on, and how much - or CPU, for every engine, falling back to it
when llama.cpp's output says nothing; adopted engines get it too, and each
session reports it once per shard as a compute-only status. engine-check
runs its GPU token at -lv 4, where llama.cpp does say where its layers went.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 14:04:08 +02:00
Ilker ManapandClaude Opus 5.5 25b3e129fe What a shard computes on survives the agent's and the control plane's restarts
It was sent once, with READY, and kept only in the leader's memory: a
failover or an agent restart lost it. The agent now keeps it with the
shard's state on disk (ShardSpec.Compute, set by Supervisor.SetCompute) and
reports it with each running shard when it registers (RunningShard.compute),
which adoption carries onto the replica.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 13:51:40 +02:00
Ilker ManapandClaude Opus 5.5 0add189e02 A standby that takes over waits for the fleet before placing
The settling window ran from process start, so a standby that took over
after a minute placed at once, before the agents reconnected and reported
what they run: it started second copies of replicas it did not know of and
drained the healthy ones when they were adopted. Seen on the PoC: a leader
restart replaced llama-guard-3-1b and a qwen2.5-1.5b replica. Reconcile now
reopens the window on the first pass after standing by.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 13:40:28 +02:00
Ilker ManapandClaude Opus 5.5 66f702d5c1 A forgotten password is set through a one-time link
/forgot mails a link to set a new password when the cluster's mail settings
say how - SMTP relay, sender, and the public base_url the link points at,
never the request's Host - and says the same whether or not the address has
an account. /reset sets it once, closing every session. Links are kept by
their hash in password_resets, good for an hour, a newer one replacing an
older. Without mail, /forgot says to ask an operator, and llmgw users
reset-link makes the link to hand over. Single sign-on accounts get none.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 13:20:11 +02:00
Ilker ManapandClaude Opus 5.5 f2b2e24e1f A node's own readiness and its multi-node readiness are kept apart
doctor sums findings up as two readinesses - the node's, which decides
whether it joins, and multi-node, its fabrics for replicas spanning nodes -
prints fabric findings in a section of their own, and gives both in --json.
node add says the multi-node one. NodeView.multi_node says whether a node's
declared fabrics are up, naming adapters that are down, and ctl nodes shows
it beside the fabric.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 13:11:16 +02:00
Ilker ManapandClaude Opus 5.5 5ae870e1b2 ctl restart --max-surge and --max-unavailable
A model's replicas restarted one at a time, always. RestartPace says how
many restart at once: MaxSurge beside themselves (replacement first),
MaxUnavailable stopped first (no room needed, out while loading); the
reconciler begins that many per model per pass. The admin API takes
max_surge and max_unavailable on POST /api/v1/restart.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 13:09:10 +02:00
Ilker ManapandClaude Opus 5.5 619dd751ab Nodes show their engines' versions and GPUs' capability; a profile can ask for an engine version
NodeView carries engine_versions and compute_capabilities; ctl nodes shows
llamacpp b10991 and 6000-24g (7.5). A profile's engine_version restricts it
to nodes whose engine version starts with it, and explain-placement says
which version a node has, or which engines it has when it lacks the one
asked for.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 13:06:57 +02:00
Ilker ManapandClaude Opus 5.5 e4e15200d6 engine-check computes a token on the GPU, and finds the installed llama.cpp
Seeing a device is not computing on it. For llama.cpp on a machine with
NVIDIA GPUs, engine-check now loads a small GGUF (--model, or the smallest in
the weights cache) with every layer on the GPU, asks for one token, and
reads from llama.cpp's own output where the layers went: a build that
computes on the CPU fails, naming the fix. With no --bin it checks the build
the agent installed under /opt/llmgw/engines.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 13:04:38 +02:00
Ilker ManapandClaude Opus 5.5 0bd0d881c5 A replica says what its engine computes on, and llama.cpp's word on the CPU refuses a GPU placement
llama.cpp's start-up output is read as a shard becomes ready: the devices it
used, its backend, and the layers it offloaded. That goes up with READY in a
new ShardStatus.compute, is kept on the shard, and is shown by the admin
API, ctl replicas (ON) and the console (COMPUTE). A shard given GPUs whose
engine says it found no usable GPU or offloaded no layer is stopped and
failed, even where nvidia-smi cannot be asked.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 13:02:17 +02:00
Ilker ManapandClaude Opus 5.5 f285aa0548 v0.12.2
Release / build (push) Successful in 4m41s
CI / test (push) Successful in 4m43s
Release / notes (push) Successful in 3s
Guardrails off and on; a judge's refusal names the hazard; ctl guard judge ask.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
v0.12.2
2026-10-06 13:35:16 +02:00
Ilker ManapandClaude Opus 5.5 4721b8e353 Llama Guard 3's verdicts carry their hazards' names without a categories list
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 12:44:53 +02:00
Ilker ManapandClaude Opus 5.5 249a91aeec Guardrails turn off and on; a judge's refusal says which hazard; ctl guard judge ask
guard.enabled (unset is on) makes every policy check nothing while false,
live: llmgw ctl guard off/on set it in the cluster's settings, and ctl
status, ctl guard list and the guardrails page say when they are off.

A refusal by a judge names the hazard it saw - judge: S6,Specialized
Advice - and the verdict is kept with the request in a new usage column,
guard_rule, shown by ctl request, the request log and the guardrails page.

llmgw ctl guard judge ask has a judge read a text, past the cache, and shows
its raw answer and how it is read; the endpoint is kept out of the audit so
no text is recorded.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 12:41:08 +02:00
Ilker ManapandClaude Opus 5.5 3f5a809373 v0.12.1
CI / test (push) Failing after 4m20s
Release / build (push) Successful in 4m36s
Release / notes (push) Successful in 3s
The guardrails' judge served and watched without server init.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
v0.12.1
2026-10-06 10:52:24 +02:00
Ilker ManapandClaude Opus 5.5 a8bb417618 The guardrails' judge has its own command, and its trouble is shown
llmgw ctl guard judge status lists each judge model the policies ask:
in the catalog, as a judge, replicas ready, its last failure to answer, and
what is wrong in words. enable serves the missing one through model serve -
Llama Guard 3 1B on CPUs, role guard, under the name the policies give it -
with --replace for a model of that name that is not a judge; the console has
it under Do. GET /api/v1/guard/judges, and the summary's judges, put the
problem on ctl status and the console's Overview.

A Llama Guard file served from the mirror page is added as a judge, under
the name the policies ask, rather than as a model clients use the policies
never find. ctl says plainly when the API answers with a page.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 10:47:37 +02:00
Ilker ManapandClaude Opus 5.5 2a942409ea v0.12.0
CI / test (push) Successful in 4m40s
Release / build (push) Successful in 4m44s
Release / notes (push) Successful in 3s
The cluster's settings in the store, in force when changed; the firewall
switch; llmgw wipe; the mirror's address one the nodes reach.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
v0.12.0
2026-10-06 09:39:54 +02:00
Ilker ManapandClaude Opus 5.5 ad04a78343 The mirror's address is one the nodes reach, and node add checks it
server init and mirror init took the first non-loopback address for the
mirror's --base, Docker's 172.17.0.1 on a machine with Docker; nodes asked
their own bridge for every model and were refused. primaryAddr takes the
default route's source, else the first address on an interface that is not
a container's, bridge's or overlay's. node add has the node reach the
mirror's files during the checks and fails, naming the address and the fix,
when it cannot.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 09:36:09 +02:00
Ilker ManapandClaude Opus 5.5 32c3942040 The firewall turns off and on with the cluster's settings; llmgw wipe
firewall.enabled (default true) is a cluster setting, applied live: the
leader sends a Firewall message to every agent when it registers and when
the setting or firewall_networks changes, and the agent (root) takes llmgw's
nft table out or installs it. A control plane writes the state to
/var/lib/llmgw/firewall.json, which llmgw-firewall-sync.path applies as root
with `llmgw firewall sync`; server init and server add install the units,
`llmgw firewall sync --install` on one set up before. node add and server
add put no firewall on a machine while the setting is off.

`llmgw wipe` removes llmgw from a machine after listing what goes and asking
for its name: units, engines' processes, the nft table, /etc/llmgw,
/var/lib/llmgw, the agent's state, the sudo grant, the CA from the trust
store, keepalived's configuration when llmgw wrote it, and the users.
--models, --store (Patroni member, etcd, or the local database) and --all
take more. The console has it under Do.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 08:00:06 +02:00
Ilker ManapandClaude Opus 5.5 0730d23b77 ctl guard says where policies live now; E1 done on the PoC
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 18:34:00 +02:00
Ilker ManapandClaude Opus 5.5 6289b01ed6 The cluster's settings live in the store, and every change takes effect
Everything in server.yaml that is not one machine's own is now a versioned
document in the store (cluster_settings), the same on every control plane.
The first start against a store imports the file's cluster keys, with the
baseline guard policy when there is none; after that those keys in a file
are ignored, with a warning.

Each control plane polls the document's version and its own server.yaml's
hash. A change is validated and applied: guard live (Gate.Replace), anything
else by a graceful restart that systemd brings back, standbys first and the
leader 30s later. A version that does not load on a machine is not applied;
at start the newest one that loads is used, so a bad edit cannot keep every
control plane down.

llmgw ctl config show/get/set/unset/edit/history/rollback, the admin API
under /api/v1/settings, and the console's Settings screen, which layers the
store's document over the machine's file, write it. Commands beside the
control plane that read cluster keys (init, node add, server add, backup,
upgrade) read the layered configuration; init --bundle writes offline to
the store once it holds the settings.

A standby that becomes the leader re-reads models, engine choices and
facility holds from the store.

Retires appendGuardIfMissing: the import does it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 15:25:23 +02:00
Ilker ManapandClaude Opus 5.5 cbbfce235e node add lets the node through the control plane's firewall
The control plane's firewall answered only the networks it was installed
with, so a node on another subnet was set up, could not reach 7443 and
showed as lost. node add now admits the node's network (or its address,
when routed) into the local ruleset, keeping everything already there.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 13:27:57 +02:00
Ilker ManapandClaude Opus 5.5 4ff62c5dd2 ctl upgrade asks the server what it runs, and waits for a node as long as it does
A binary installed by hand made ctl upgrade say this machine already ran
the release and leave the old server running. And a node had 15 seconds
to answer an upgrade the control plane gives five minutes, so a node that
was upgrading stopped the run.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 12:43:25 +02:00
Ilker ManapandClaude Opus 5.5 dc65ea6899 server init adds the guardrails to a server.yaml from before them
An upgrade replaces the binary and leaves server.yaml alone, so a control
plane installed before guardrails stayed unguarded. Run again, init now
appends the baseline guard section (and so fetches the judge) when the file
has no guard key at all, keeping the old file as server.yaml.bak-<time>.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 12:39:28 +02:00
Ilker ManapandClaude Opus 5.5 3558dba485 v0.11.4
Release / build (push) Successful in 4m19s
Release / notes (push) Successful in 3s
CI / test (push) Successful in 3m36s
The OpenAPI document carries serve's tools field.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
v0.11.4
2026-10-05 12:29:05 +02:00
Ilker ManapandClaude Opus 5.5 481523ca46 serve: tool calls declared, slots that hold the context, longer answers
CI / test (push) Failing after 2m41s
A --context-length 32768 --ctx-pool 32768 model got llama.cpp's default four
slots of 8192, and refused what the gateway admitted; --parallel is now
what the pool holds of the context. max_output was 1024 for every model,
which cut an editor's agent off mid-file; it is now a quarter of the
context, from 1024 to 8192. --tools writes capabilities.tools, which the
Continue configuration now reads, as it reads max_output.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 09:30:40 +02:00
Ilker ManapandClaude Opus 5.5 df6d243ee7 The Continue configuration says what the catalog says of the model
CI / test (push) Successful in 2m52s
It told Continue every model uses tools and nothing of its context: a model
whose calls come back as text stalled Continue's agent, and Continue sent
prompts far over a CPU replica's 4096 tokens, each refused. tool_use is now
written for a model whose catalog entry says tools, and the context length
with a fitting maxTokens whenever the catalog knows it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 09:16:34 +02:00
Ilker ManapandClaude Opus 5.5 ef33e52d3d ctl upgrade upgrades the leader last, wherever it is run
CI / test (push) Successful in 2m39s
Run on the standby, it upgraded the leader first, then itself, and the
leadership moved twice.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 06:45:56 +02:00
Ilker ManapandClaude Opus 5.5 abaf086fd5 A standby's portal is the leader's; the changelog as the release notes read it
CI / test (push) Successful in 2m42s
Release / notes (push) Successful in 3s
Release / build (push) Successful in 3m20s
A proxy left pointing at a standby showed an empty cluster: the portal and
the admin pages read the standby's own memory. A standby now passes its
client port to the leader's, all but /health, /ready and /leader, which
must describe the machine a load balancer is choosing; and its admin port
passes the dashboard as well as the API. The leader trusts the control
planes' X-Forwarded-For, so clients keep their own address.

The changelog's candidate section is "Unreleased" again: release notes
for an rc read that section, and v0.11.3-rc.1 got none.

The e2e wait for two endpoints is 30s: under the race detector on a busy
runner, 15s was missed once.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
v0.11.3-rc.2
2026-10-05 06:34:57 +02:00
Ilker ManapandClaude Opus 5.5 c5b34978f5 Guardrails in the console
CI / test (push) Successful in 2m52s
The console never mentioned them. A Guardrails view shows each policy with
its mode, fail behaviour and checks, whether its judge models have replicas
ready, where policies are attached (attach and detach from it), and what
they did in a window. The admin API gains GET /api/v1/guard/events, and GET
/api/v1/guard says what each policy does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 03:36:17 +02:00
Ilker ManapandClaude Opus 5.5 77fb48ba32 The probe reads the floating address from its file each time
CI / test (push) Successful in 2m49s
The change to read it each time left the constructor storing the address
itself, so the file read each time was one named 10.0.0.161.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 02:56:23 +02:00
Ilker ManapandClaude Opus 5.5 86cfb82f42 Merge features-ha: two control planes, a fast failover, ctl upgrade in waves
CI / test (push) Failing after 3m23s
Release / build (push) Successful in 4m5s
Release / notes (push) Failing after 2s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
v0.11.3-rc.1
2026-10-05 02:44:55 +02:00
Ilker ManapandClaude Opus 5.5 c5e388807a ctl upgrade in waves, and the other control plane first
One node at a time was hours on a fleet of hundreds. Nodes now go one, then
two, four, up to --parallel at once, each wave waited on whole; a wave that
does not come back stops it. The other control plane is upgraded first over
ssh, so a leader restarted last hands over to a standby on the release.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 02:43:33 +02:00
Ilker ManapandClaude Opus 5.5 964b659849 A stopping control plane gives up the lease before it lets its agents go
With the agent server closed rather than drained, the process could exit
before Run's release had reached the store, and the standby waited out the
lease. The lease is now given up first, synchronously, and nothing takes it
again after.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 02:31:13 +02:00
Ilker ManapandClaude Opus 5.5 c2b4985abe The floating address is read each time the summary is asked
server add records it after restarting the new control plane, which then
never named it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 02:29:00 +02:00
Ilker ManapandClaude Opus 5.5 1d1409faad A standby's admin API is the leader's
The console and ctl on a standby showed its own empty view: nodes and
replicas live in the leader's memory. A standby now passes /api/ to the
leader over the agent port, with the cluster's certificates both ways; the
leader answers only a certificate naming a control plane the store lists,
and only while it leads. Control plane certificates are issued for client
use as well, and server add reissues older ones.

An agent's reconnect backoff starts again after a session that worked, so
each failover it lives through no longer makes the next one slower.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 02:27:11 +02:00
Ilker ManapandClaude Opus 5.5 adef25d709 server add records the floating address where the summary can read it
keepalived's file holds its password and is root's alone; the control plane
runs as llmgw and could not read it, so the CONTROL line never named the
floating address.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 02:19:48 +02:00
Ilker ManapandClaude Opus 5.5 ec1918b23b Two control planes: a failover in seconds, and the console to set one up
A control plane being stopped held every agent's stream for the thirty
seconds of a graceful shutdown, so the standby led within four seconds and
had no nodes for thirty. The agent server is now closed, not drained. A
control plane that loses its lease while it lives on lets its agents go,
rather than keeping nodes the new leader cannot reach.

ctl status and the console's Overview say which control plane leads, which
stand by, and whether the floating address is on the leader. The console
adds a control plane (Do → Add a control plane), asking for the machine and
the floating address. server add says when the mirror would refuse the new
control plane.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 02:17:51 +02:00
Ilker ManapandClaude Opus 5.5 289a4ff8e8 Merge features-c2: section C of the backlog, items 6-24, for v0.11.2
Release / build (push) Successful in 3m18s
Release / notes (push) Successful in 2s
CI / test (push) Successful in 5m33s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
v0.11.2
2026-10-05 01:30:38 +02:00
Ilker ManapandClaude Opus 5.5 2158a933e0 v0.11.2
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 01:30:37 +02:00
Ilker ManapandClaude Opus 5.5 803d1d5fb6 CHANGELOG and BACKLOG for the rest of C24 and the engine fixes; README
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 01:30:01 +02:00
Ilker ManapandClaude Opus 5.5 33334c8f33 Merge C24 federation: another llmgw cluster as a provider
Conflicts with facility-aware placement (config, control, admin API routes,
store test tables) all keep both; the generated reference and OpenAPI
document regenerated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 01:29:35 +02:00
Ilker ManapandClaude Opus 5.5 f1a3199e55 KV routing tests wait for both replicas to be scraped: a replica not yet heard from is passed over, and the test was racing that
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 01:27:14 +02:00
Ilker ManapandClaude Opus 5.5 9e46ee83e4 Merge C24 KV-index routing from vLLM's cache events
Conflicts with the LoRA work: vLLM's arguments, the engine profile, router
metrics and the scheduler's request all keep both; agent.pb.go regenerated
from the merged proto; the two test fakes of vLLM given separate names.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 01:24:25 +02:00