All ~173 managed skills read in full and merged, one polished reference page per skill-cluster: - LLM cross-cutting (17 pages): GGUF Compatibility, Llama Cpp Serving Sizing, Llama Cpp Multi-Device Splits, PCIe Link and Atomics, RDNA4 Container Serving, RDNA4 MoE Expert Offload VRAM Budget, vLLM Boot Forensics, vLLM Porting and Compile Triage, LLM Bench Windows, LLM Performance Diagnosis, Model Swap and Rollback, Tcclaviger Fork Ops, Heterogeneous Split Triage, FreeToken Builds + deltas folded into the retired runbooks (GLM DFlash2 port laws, Lucebox dual-R9700, R9V qualification pipeline, FLM pack registration, ik hybrid envelope, frank-medium split rebalance, llama.cpp GGUF preflight, batch-ubatch split section, bench-window D-state/stale-read notes, second-endpoint bring-up). - Architecture (3): Stale Signal Audit, Quadlet Lifecycle, Deploy State Triage. - Operations (6 + index): Remote Job Runner, Payload and Disk Reclaim, Container Host Reachability, Fleet Ops Playbook, Fleet Audit and Posture, SSH and Remote Access. - Infrastructure: Observability Provisioning + Homepage Status Probes; updates to Authentik, Anubis, LAN-DNS (per-client DoH bypass, Porkbun zone ops), Swap Configuration, OpenWebUI, Hermes (pre-existing page preserved and extended). - FrankBook-Pro: ARR-Stack filled (API split, schema-first probing, auth-posture, auth-config trap); qBittorrent E2E verification. - Desktop/Agent/Research: Noctalia and Niri, OMP Harness, Agent Docs and Edits, Command Center Dotfiles, PICO-8 Asset Standards. - Indexes: LLM Cross-Cutting References section, Operations/ and Agent/ index pages, root-index Skill Distillation section, AGENTS.md pointer. - log.md carries the wave plan. check-vault-links.py 0 unresolved (185 pages); ./tests/run.sh 366/366.
16 KiB
title, service, host, port, status, deployed, supersedes, tags
| title | service | host | port | status | deployed | supersedes | tags | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| frank-primary — R9V radiance-vLLM Qwen3.8-Flash-Next Q8_0 | frank-primary | dankserver | 7770 | active | 2026-09-27 | frank-primary-r9v-radiance-qwen38-uncensored |
|
frank-primary — R9V radiance-vLLM Q8_0 (dankserver, ACTIVE)
The Dankserver LLM endpoint is the R9V radiance-vLLM fork
(drwolfen/radiance-vllm-qwen4exp, docker-pinned prebuilt image bundle
2dac17a2) serving the unsloth Qwen3.8-Flash-Next-GGUF Q8_0 payload
(175.30 GiB, 6 GGUF shards, sha256-verified per part) with the blockfp8
MTP4 head and the mmproj, on both R9700s (gfx1201, BDFs 0000:03:00.0
/ 0000:07:00.0) as frank-primary on :7770.
Cut over from the uncensored IQ4_XS payload on 2026-09-27 at Frank's
direction. Rollback target: frank-primary-r9v-radiance-qwen38-uncensored
— the uncensored state dir (/home/dank/r9v-state/uncensored/, ready: true), its frozen config (restore-config-20260927.env) and payload stay
on disk; a rollback is an env-file R9V_STATE_FILE flip plus a redeploy.
Why the swap
The uncensored IQ4_XS payload measured 59–96 tok/s decode with draft-acceptance swings; the Q8_0 payload trades VRAM-resident expert headroom for precision — quantization to Q8_0 doubles expert bytes per block, so the tiered-expert residency budget halves — the probe-era 11 tok/s decode was residency-bound (expert streaming, not compute). The "Known costs" section below carries the accepted trade.
Production shape (repo-tracked)
The repo carries a thin wiring; the engine shape lives on-host:
| Piece | File | Law |
|---|---|---|
| Unit | scripts/frank-primary.service |
Root unit, User=dank, LimitMEMLOCK=infinity, ExecStop=podman stop -t 120 frank-primary-r9v, Restart=on-failure |
| Env | scripts/frank-primary.env |
Production overrides ONLY: bind 0.0.0.0, :7770, alias frank-primary, container frank-primary-r9v, GPU BDFs, dev-overlay adapter, R9V_FULL_MUTABLE_CACHE=0, R9V_PLE_READY_TIMEOUT=1800, prefix caching ON, sharp chat template |
| Serve | scripts/serve-frank-primary.sh |
Layers setup state under repo env → execs the fork's own scripts/launch.sh via the docker→podman shim → docker wait |
| Deployer | scripts/deploy-remote.sh deploy_llm_primary |
Gates on setup state ready, image digest, PLE bytes, all six shard files + mmproj; MainPID false-green check |
Why the tuned shape lives on-host: the q8 profile's setup run
qualified the R9V_* config (tiered-expert placement, per-rank offload
budgets, 80-slot tiered cache, PLE pinned residency 54.4 GB, MTP4,
parser wiring, MAX_MODEL_LEN=131072). It is written to
/home/dank/r9v-state/q8xl/setup.json and the serve script layers it
UNDER the repo env (repo keys win). Copying those values into the repo
would drift from the qualification — don't.
Spec decode: MTP4 only — the ngram-mod ask is NOT expressible
llama.cpp chains drafter types (--spec-type draft-mtp,ngram-mod on
frank-small/large). The fork's speculative_config is a single dict
(one method per engine): mtp, ngram, ngram_gpu, dflash, … are
mutually exclusive — no chaining seam exists in the fork. What IS active:
- MTP4 (
R9V_MTP_SPEC_TOKENS=4, blockfp8 draft head, draft TP=2) — measured mean acceptance length 3.19–5.00 during qualification. - The PLE n-gram — architectural (
ngram_embeddingmodule), rides along automatically; it is not the llama.cppngram-modmatcher and cannot be "added" as a second spec method. ngram_gpuexists as an ALTERNATIVE single method (fork's GPU n-gram proposer) — swapping MTP4 for it is an either/or, not a chain.
Sharp chat template (fleet parity with frank-large)
scripts/qwen-sharp-v22.4.0.jinja is served via the launcher's opt-in
R9V_CHAT_TEMPLATE_FILE passthrough (fork commit 30d4a32): the file is
mounted read-only at /opt/r9v/share/chat-template.jinja and passed as
--chat-template, which wins over the tokenizer dir's
metadata/chat_template.jinja; R9V_CHAT_TEMPLATE_KWARGS carries
--default-chat-template-kwargs {"preserve_thinking":true} (the template
defaults preserve_thinking true natively; request-level kwargs still win
per request). The template folds reasoning efforts (high/xhigh/max →
xhigh, minimal/low → low), matching models.yml's reasoningEffortMap.
The sha-pinned metadata/chat_template.jinja in the package is NEVER
edited in place — mutating it drifts the qualification.
Two q8-only runtime laws
- Quantized inject shard — the Q8_0 payload quantizes BOTH fused
hyper-connection shards (the IQ4_XS package ships inject as F32), so
the shard-uniformity check rejects the fused projection at load.
R9V_DEV_QWEN4_EXP_ADAPTER_PY(fork commit363d606) mounts a dev-overlay adapter that dequantizes a quantized inject shard in-stream; uncensored payloads pass through unchanged. R9V_FULL_MUTABLE_CACHE=0— the runtime overlay'senv.alwaysinjects the uncensored 160-slot full-mutable cache geometry, which the q8 profile's 80-slot tiered cache fails with a baseline mismatch. The launcher passes this key AFTER the overlay envs (docker last-wins).
Fork checkout + shim (on-host, outside the repo)
- Checkout:
/home/dank/GitPackages/radiance-vllm-qwen4exp(fork commits363d606dev-overlay,2fe6dbb,50a79ecqualification doc,30d4a32chat-template passthrough,2de0357PLE-timeout forwarding — local-only, remote is GitHub) - State:
/home/dank/r9v-state/q8xl/setup.json(0600,ready: true) - Model dir:
/home/dank/models/Qwen3.8-Flash-Next-Q8_0-R9V/— 175.30 GiB payload as sixQ8_0/Qwen3.8-Flash-Next-Q8_0-0000N-of-00006.ggufshards- mmproj; PLE derived at the pinned size 54,400,261,120 B
- Docker→podman shim:
/home/dank/GitPackages/radiance-vllm-qwen4exp/.podman-shim/docker(PATH-prepended by the serve script). The R9V tooling is docker-pinned; the shim carries three laws:- Image ID law — the bundle is a containerd/OCI docker-save; docker
keys the image by its OCI MANIFEST digest (
sha256:2dac17a2…), podman by config digest but preserves the manifest digest in RepoDigests. The shim answersimage inspect sha256:Xfrom RepoDigests and rewrites baresha256:Xrun refs torepo@sha256:X. - LogConfig law — podman inspect answers
HostConfig.LogConfig.Config: nullwhere docker returns the log-opts map (the doctor crashes on.get("Config", {}) == null). The shim recordsrun --log-optpairs and normalizes inspect output. - docker info law — podman lacks
SecurityOptions/ServerVersion; the shim answers rootless +27.5.1(liveness probe).
- Image ID law — the bundle is a containerd/OCI docker-save; docker
keys the image by its OCI MANIFEST digest (
- venv:
~/.r9v-venv(huggingface_hub for fetch/verify)
Bring-up battery (qualification, 2026-09-27)
- Setup
ready: true; all 20 payload artifacts size+sha256 verified (175.30 GiB total across the six shards). - Vision: 64×64 red PNG → "Red" (79 tok, 7.4 s end-to-end on the scratch port). Prose: 100 tok in 9.1 s ≈ 11.0 tok/s wall on a cold cache with reasoning tokens included in the wall clock — decode-rate context, not a bench (no throughput bench was run, per Frank's direction).
- MTP4: mean acceptance length 3.19–5.00 across the battery.
- PLE pinned proof:
mapping_bytes = mapping_rss_bytes = 54,400,261,120(fully RSS-resident); doubling for re-pin would need 108,800,522,240 ≤ available 140,876,808,192; registration took 446 s.
Memory shape (measured)
- Load: 21.84 GiB / worker at ~158 s; cold start → serve ~16 min.
- PLE pinned residency 54.4 GB in host RAM (vs the uncensored payload's ssd-mmap residency).
- Context envelope:
R9V_MAX_MODEL_LEN=131072; harnessmodels.ymlcarriescontextWindow: 131072,maxTokens: 36864(compaction-gap math: 70% × 131,072 = 91,750 + 36,864 = 128,614 ≤ 131,072). The KV envelope at 131,072 is smaller than the uncensored 196,608 shape. - Prefix caching OFF at cutover (
R9V_ENABLE_PREFIX_CACHING=0), CACHE192 off — superseded 2026-09-28: prefix caching is ON (see bench matrix below).
Prefill / decode bench matrix (2026-09-28, scratch port 7773)
Production stopped for the window (fleet bench law); one matrix plus three follow-up runs, fork's own bench-pp + decode_speed harness, corpus-sliced prompts with the fork's canonical decode rounds. Baseline E0 = production shape (chunk 4096, 80-slot sync cache, MTP4):
| Config | Override | PP8K tok/s | PP131K tok/s | Decode | Verdict |
|---|---|---|---|---|---|
| E0 | — | 954.6 | 883.0 | 141.9 ms/step ≈ 23.8 t/s eff | baseline |
| E1R | chunk 8192 | 1201.4 (+25.9%) | 844.6 (−4.4%) | 158.5 ms (−8.5% t/s) | short-prompt win, decode tax |
| E2 | chunk 16384 | — | — | — | boots; 33.3/34.2 GiB VRAM (0.93 free) → requests hang/OOM |
| E1/E3 | chunk 8192/32768 | — | — | — | PLE registration blew the 600 s engine timeout (see law below) |
| E4 | cache async=1 | — | — | — | doctor: cache-prepare backend requires synchronous single-fill LRU |
| E5/E6 | 96/112 slots | — | — | — | doctor: capacities (48, 64, 80) only; expert-budget [108, 198] rank maxima — 80 is the policy max, prod is at it |
| E7 | MTP depth 6 | 843.1 | 819.6 | 454–489 ms/step (3.7x worse) | depth 4 stays |
Laws from the matrix:
- 4096 is the prefill-chunk ceiling twice over: the engine caps scheduled tokens by the speculative config, and E2's VRAM (0.93 GiB free at 16384) shows bigger chunks OOM at request time. 883–955 tok/s is what this payload shape delivers at long context.
- Unqualified shapes fail mid-request, not at health — E2 and E7 both answered /health 200 and then died streaming.
- PLE registration can exceed the 600 s engine timeout
(
VLLM_PLE_OFFLOAD_READY_TIMEOUT) under page-cache churn — measured 740 s after a bench teardown; a production boot failed on it the same morning (unit "Deactivated successfully", no error in journal — the serve script exits 0 on the wait). Fixed withR9V_PLE_READY_TIMEOUT=1800infrank-primary.env(forwarded by launch.sh2de0357).
Prefix caching (E8/E9, R9V_ENABLE_PREFIX_CACHING=1, same port): zero
VRAM cost (hits reuse freed KV blocks; load VRAM identical to E0),
cold path unchanged (first request ≈ cold bench), reuse hits large — 32k
prompt TTFT 44.2 s → 3.6–4.3 s (9x), 131k 439 s → 6.9 s (63x).
Hit pattern: rounds 1–2 miss, round 3+ hit (identical-prompt and
conversation-growth probes both). For the harness's resend-every-turn
traffic that is a ~40–440 s TTFT win per steady-state turn; the one-turn
miss costs nothing extra. DEPLOYED 2026-09-28 (Frank's go-ahead): the
repo env sets R9V_ENABLE_PREFIX_CACHING=1 (commit 41e9189), verified
on :7770 (--enable-prefix-caching in the container argv, health 200,
live completion answering).
Authoring a new profile (the qualification pipeline)
Order matters at every step:
- Package manifest
packages/models/<pkg>/package.json— target shards- aux artifacts with sha256. Verify every target's byte count AND sha256
after fetch (
curl -fL -C -per shard; never trust a fetch without-f).
- aux artifacts with sha256. Verify every target's byte count AND sha256
after fetch (
- Placement bootstrap from GGUF headers: parse all shards' tensor
tables (a KV-only shard1 is normal for split parts; each part carries its
own tensor table). Per-expert full per layer =
(
ffn_gate_exps+ffn_up_exps+ffn_down_exps)/512; rank slices rank0 = full×416//640, rank1 = full−rank0. Hot counts = floor(measured_budget / per-expert total). Anchor budgets to the MEASURED hot_bytes of the qualified sibling profile, never invented. - Trim + validate:
tools/trim_experts.py --hot-counts N,M, thenexpert_budget.validate_manifest(m)+expert_budget.expert_memory(m, slots, {cache_rank}, runtime=<runtime.json>)(runtime/catalog are keyword-only). Declared hot_bytes must match computed or it raises. - PLE derive (containerized
tools/prepare_ple.pywith--expected-bytes/--expected-type/--expected-shape); setup skips re-derivation when the file exists with matching size. - profile.env: set values BEFORE
source dual-r9700/profile.env(set-first-wins; an empty string does NOT survive:=).
The canonical bootstrap.json wrapper schema is exactly
{schema: "r9v.placement.v1", id, model_package, hardware, artifact_path, artifact_sha256, scope} with hardware: "dual-r9700-32gb-asymmetric"; the
wrapper id MUST equal the id in the profile.json placement descriptor
(setup rejects mismatches: "does not match profile placement"). Never
fabricate qualification metadata — use
scope: "Unranked routing bootstrap only; does not certify fit or headroom".
GGUF parsing laws for the bootstrap: expert tensor names are
blk.0.ffn_up_exps.weight — split on "." gives FOUR parts, layer number is
parts[1] (a 3-part assumption crashes int("")); outer-KV value-type sizes
{0:1,1:1,2:2,3:2,4:4,5:4,6:4,7:1,8:str,9:arr,10:8,11:8,12:8}; tensor byte
extents from consecutive OFFSETS, never a type-size table (mis-buckets tails
silently); the PLE Q8_0 tensor on this arch is shape (160, 320001536),
54,400,261,120 B, an entire shard.
Setup/start: ./r9v setup <alias> --state-dir <dir> --model-dir <dir> --image sha256:<digest> --local-image --reuse-from <sibling-model-dir> --accept-model-license (aux artifacts reuse byte-identical files; F16 vs
Q8 mmproj will not reuse). Start requires the SAME --state-dir; fixed
placement via R9V_EXPERT_MANIFEST_PATH skips headroom/catalog/seed raises.
The host-only doctor FAILs when another instance holds the cards (it wants
3 GiB free per rank; production at ~30 GiB/card is a hard wall) — a second
instance CANNOT coexist, so qualification requires a stop+mask window for
the incumbent, then unmask+restart.
Qualification gate (unqualified → qualified): first start runs the
workload battery (PONG / tool_calls / vision / long-ctx), the PLE pinning
proof (VmLck ≈ PLE bytes in /proc/<container-pid>/status when
RESIDENCY_MODE=pinned), and a VRAM headroom snapshot (floor 3.3 GiB). If
headroom is violated, lower hot counts via re-trim and re-derive
MAX_EFFECTIVE. Only then record state ready: true and author the runbook.
Remote-execution gotchas from the same windows: nohup X & inside an ssh
command races ssh teardown — the script can die before writing a byte; run
long remote scripts synchronously, or setsid + disown, or via a system
unit. zsh login shells abort on unquoted === separators; grep globs need
quoting through ssh+zsh layers.
Ops
- Restart:
sudo systemctl restart frank-primary(root unit; the serve script re-gates everything, relaunch through the shim, waits). - First start after boot re-warms ~175 GiB of page cache; expect
minutes to /health;
TimeoutStartSec=1800. - Scratch/probe: NEVER probe against the prod state dir. Copy it, set
R9V_HOST_PORTto a free port andR9V_CONTAINER_NAMEto a scratch name, and stop the probe before any deploy — a probe holding :7770 produces a false-green deploy (the 2026-09-26 lesson, guarded by the MainPID check). - Rollback to uncensored: point
scripts/frank-primary.envR9V_STATE_FILEback at/home/dank/r9v-state/uncensored/setup.json, drop the two q8-only keys (adapter, FULL_MUTABLE_CACHE) and the template keys, redeploy — the uncensored state dir and payload are intact on disk. - Models on the wire: served alias
frank-primary; thinking channel isreasoning(vLLM); tool parserqwen3_coder.
Known costs (accepted at cutover)
- Residency-halved experts — Q8_0 doubles expert bytes vs IQ4_XS, so the tiered cache holds half as many experts in VRAM at the same budget; the probe's 11 tok/s decode was residency-bound, and the production number depends on the re-qualified budgets in the q8 state.
- 128K envelope — down from the uncensored 196,608 (which itself was down from 262,144 on llama.cpp).
- Reference quant — back to the unsloth Q8_0 weights (no abliteration); answer-quality profile differs from the uncensored incumbent by design.
- Docker-pinned fork — updates flow through image bundles + the shim, never a bare container run; the launcher stays the single argv owner.