proxy-2 has been enforcing since 14:21:54 without incident, so the role
default moves to nftables and the host_vars override goes away rather than
being duplicated for proxy-1. proxy-2's rendered config is unchanged by this,
so it will not restart; only proxy-1 does.
The staged rollout did its job and can be read off the deploy log:
proxy-1: deployed in mode "dry-run"
proxy-2: deployed in mode "nftables"
and only proxy-2 restarted, which showed up as a 1,887,882-byte startup pull
from its address alone.
The delivery path was tested in enforcing mode with a decision on 192.0.2.1
(TEST-NET-1): both hosts had the ban 3s later as a 205-byte delta, and the
revocation as a 203-byte delta 1-2s after deletion.
What is still not proven is that a packet is actually dropped, and the
measurement for that argues for finishing the rollout rather than waiting.
Requests from banned addresses reaching Traefik stopped after proxy-2 armed —
1 at 14:02, 3 at 14:07, 22 at 14:08, 1 at 14:18, none after — but 25 of those
27 arrived in a single burst beforehand, so at this volume that is consistent
with enforcement rather than evidence of it. With both hosts armed the claim
becomes falsifiable instead: no request from an address in the decision set
should reach Traefik at all, and a single one would prove otherwise.
dry-run remains available per host through inventory/host_vars, which is what
a newly added proxy should get before it enforces.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Arming the bouncer on proxy-2 (#766) merged and deployed nothing. The switch
lives in ansible/inventory/host_vars/proxy-2.renner.uno.yml, and the trigger
list covered ansible/roles/** and the play but no inventory path at all. The
change sat in git looking applied while the host kept running dry-run, and
nothing reported a problem — there was simply no pipeline run to fail.
The bouncer's LAPI key has the same shape: it lives in
group_vars/netbird_proxy/vault.yml, so a rotation would never have reached the
hosts either.
Both paths are now listed. pve_hosts.yml deliberately is not: it is the single
inventory file for the whole fleet, so listing it would redeploy the proxies
for any unrelated host edit. The cost is that a change to [netbird_proxy] group
membership needs a manual deployment run.
Merging this triggers the run itself, since .woodpecker/deploy-netbird-proxy.yml
is in its own trigger list — so the arming from #766 applies with it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`llama-server --help` documents both halves of what #765 got wrong, and
it ships in the image:
-np, --parallel N number of server slots (default: -1, -1 = auto)
-kvu, --kv-unified use single unified KV buffer shared across all
sequences (default: enabled if number of slots
is auto)
So leaving --parallel unset did not merely pick a slot count, it enabled
a shared KV buffer that every slot could draw the full --ctx-size from.
That is why this server reported n_ctx_slot 16384 across four slots, and
the startup log said so outright: kv_unified = 'true'. Naming a slot
count switches that off and splits the context.
The value stays 65536. Only the reasoning in the comment changes, from an
inference drawn off two /props readings to the documented behaviour.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#765 claimed --ctx-size was per slot and halved the slot count to pay for
doubling it. Both halves of that were wrong. With --parallel set,
llama.cpp divides --ctx-size across the slots, so 32768 over two slots
gave 16384 each -- exactly what was there before -- and the Assist
request fails with the same message it did then:
request (18910 tokens) exceeds the available context size (16384)
The flag does behave differently without --parallel, which is where the
wrong reading came from: the four-slot server reported n_ctx_slot equal
to --ctx-size rather than a quarter of it. Adding --parallel changed
that, and the memory reading confirms it -- the pod fell from 24979 MiB
to 21601 MiB, so total KV halved rather than holding steady.
65536 across two slots is 32768 each, and puts total KV back where it was
before #765 along with the memory that came with it.
Verify rather than assume this time:
kubectl -n ai exec deploy/llama-server -- curl -sS \
http://127.0.0.1:8080/props | jq .default_generation_settings.n_ctx
must report 32768.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
First host goes from dry-run to enforcing. proxy-2 deliberately: it is the
dual-stack member, while proxy-1 is IPv6-only and the one where every
network-shaped problem has surfaced first — the dhcpcd/resolv.conf breakage,
and a GitHub download that could not work at all. host_vars keeps the change
to one host; proxy-1 stays on the role default.
Verified before committing: ansible-inventory resolves nftables for proxy-2
and nothing for proxy-1.
Adds netbird.renner.uno to capi_whitelists first, and that is not decoration.
The bouncer's chain sits on the INPUT hook, the proxy hosts run no other
firewall, and there is therefore no conntrack rule accepting established flows
ahead of it. A ban on the management address would drop the return traffic of
the proxies' own outbound control connection and take them off the mesh, with
no inbound attack involved at all. The same reasoning already covers the proxy
addresses, which were listed from the start.
Checked at the time of writing: none of these addresses appear in the current
decision set — searched all ~15k entries. That is the state today, not a
guarantee; they are rented addresses on a hoster whose ranges do end up on
community blocklists, which is the whole reason for listing them.
The delivery path was tested end to end beforehand with a decision on
192.0.2.1 (TEST-NET-1, so no real traffic could be affected). Both bouncers
received the ban within one poll cycle — a 206-byte delta against the usual
26 — and the revocation the same way, 203 bytes, ~5s after deletion. What that
did not test is whether a packet is actually dropped, which dry-run cannot
show. This commit is what makes that testable.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Home Assistant conversation agent is configured and refuses every
request:
API error: request (18910 tokens) exceeds the available
context size (16384 tokens), try increasing it
18910 breaks down as ~12k for the exposed-entity overview and ~7k for
the base instructions and tool schemas, whose parameters enumerate every
exposed name, area and floor. Trimming the exposed set from 845 entities
to 666 already took the overview from 16045 tokens to 12048; the
remainder is not the sort of thing curation reaches.
--ctx-size is per slot rather than the total, which is what makes this
affordable: /props reports n_ctx_slot equal to the flag while running
four slots, where a total would have divided down to 4096. Doubling the
flag alone would double the KV cache, so the slot count halves in step
and the allocation lands where it already is -- 24979 MiB against the
32Gi limit, most of it the 19.5 GiB of weights.
Two slots matches the actual callers: one holds Home Assistant's prompt,
one holds Paperless's, and prompt caching keeps each warm. A cold prefill
of this prompt costs about two minutes, so a third caller queueing is a
better outcome than it evicting one of them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The LLM index rebuild ran for 79 minutes and then died:
input (1069 tokens) is too large to process.
increase the physical batch size (current batch size: 1024)
Setting the batch to Paperless's chunk size looked like an exact match
and was not. Paperless counts its 1024 tokens with llama_index's
tokenizer; this server measures the batch in bge-m3's, which is
XLM-RoBERTa and splits German into more pieces. A chunk Paperless
considered full arrived here 4.4% over the limit, and the request failed
rather than truncating.
1536 absorbs a tokenizer ratio rather than a rounding error. 2048 stays
out of reach: that is where the chat model hit ErrorDeviceLost on this
Vulkan stack, under a 20 GB model's memory pressure that a 600 MB one may
well not reproduce — but there is no reason to go looking.
The index is left partial by the failed run and has to be rebuilt with
`document_llmindex rebuild` once this is live.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
HA 2026.8.3 ships a native `llama_cpp` integration, so an Assist
conversation agent backed by the cluster's own model needs no custom
component — only a path from the HA pod to llama-server, which two
default-deny policies currently block in both directions. Verified from
inside the pod: the call to llama-server.ai.svc.cluster.local:8080 times
out today.
Both grants are scoped to the single pod and port, mirroring the
paperless grant next to them, so neither widens if something else is
placed in the other namespace. llama-embed is deliberately excluded: the
conversation agent uses chat completions only.
Tool calling was verified against the running model before adding this —
the Assist API requires it, and llama.cpp only exposes it for some
builds. b10731 returns finish_reason=tool_calls and parses German
commands into the right arguments without --jinja.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 14:18:30 +02:00
7 changed files with 109 additions and 19 deletions
# The cluster's own chat model, for the llama.cpp conversation agent that
# backs Assist. Scoped to the one pod and port rather than the ai namespace
# as a whole, so this grant cannot widen if something else is placed there —
# the embedding endpoint next to it is deliberately not included.
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": ai
app.kubernetes.io/name:llama-server
toPorts:
- ports:
- port:"8080"
protocol:TCP
# traefik for east-west OIDC roundtrips that go through the
# public hostname via k8s_gateway.
- toEndpoints:
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.