# arcint

> Intel Arc inference server (Arc A770 16 GiB, Arc Pro B60 24 GB, xe
> driver), with OpenAI-compatible HTTP APIs: language models through
> libllama, and since 1.1.0 text-to-image (Z-Image Turbo; since 1.1.1
> Qwen-Image, FLUX.2 klein with editing, ESRGAN upscaling) through
> stable-diffusion.cpp's sd-server built against the same patched ggml tree
> (package arcint-imggen, contrib/sdcpp, docs/imggen.md), and since 1.2.0
> voice and vision (docs/speech-service.md): image input on
> /v1/chat/completions (Qwen2.5-VL, --mmproj, libmtmd), German text to
> speech with enrolled-voice cloning on /v1/audio/speech (Qwen3-TTS,
> --tts), and speech to text on /v1/audio/transcriptions (whisper.cpp's
> server on the same ggml, package arcint-stt, contrib/whisper). Serves the
> hybrid GatedDeltaNet/attention Qwen family from GGUF files: Qwen3.8-27B
> (dense, the agent), Qwen3.6-27B-A3B-Coder (MoE, the coder) and
> Qwen3.8-Flash-Next (512-expert MoE on one card plus host RAM), plus
> Cydonia 24B (llama architecture) for writing. One engine: libllama
> (llama.cpp at pinned commit 3e4dfa8) with ggml's OpenCL backend and
> arcint's Intel kernel series (contrib/llama.cpp/patches, 0001-0063
> without 0031, 0055 and 0061).
> arcint owns the HTTP surface, chat templates, tool calls, the sampler,
> named lanes, MTP speculative decoding, context checkpoints and the expert
> cache's policy. The OpenVINO engine of 0.1-0.7.12 was retired in 0.8.0;
> its record is at git tag openvino-history (docs/history.md).

## Status

- Flash-Next's production quant is ISTA-DASLab's GSQ-RCO IQ3_S since
  2026-10-20 (operator): 33.3 t/s decode on six answers, KL 0.127 against
  Q8_0 (IQ3_XXS 38.0 t/s, 0.217, the rollback).
- Version 1.5.0 (2026-11-03): MTP K/V-only prompt pass and on-card KV rotations on by default (coder
  +2 % decode, +6.6 % prefill); GDN rollback by replay per unit (LLAMA_GDN_REPLAY=1; the agent at
  262,144 tokens); GET /slots; --llama-mtp-depth-max; patches 0080-0094.
- Version 1.4.2 (2026-11-02): X-Request-Id on every response (a caller's id kept when it is 1..128 of
  [A-Za-z0-9._:-], otherwise generated) and rid=<id> on the request's log lines.
- Version 1.4.1 (2026-11-02): patch 0078 (the staging bound) dropped; it cost Flash-Next 2.2 %
  decode in 1.4.0. Nothing else changes.
- Version 1.4.0 (2026-11-01): token mode for gateways (`GET /v1/responses/renderer`,
  `arcint:token`/`arcint:span` events, `/props` `token_mode`), `preserve_thinking`, `POST /tokenize` and
  `/detokenize`, `--metrics`, `--api-key`, TLS (`ARCINT_TLS`), the strict `arcint` object on Chat
  Completions, patches 0074-0079; the feyntalk chain with llm-proxy 1.3.0 and feyntalk-render 1.0.0
  (`docs/feyntalk.md`).
- Version 1.3.0 (2026-10-31): the Open Responses API (`POST /v1/responses`, streaming, store,
  cancel, reasoning replay, `--think-ctx-reserve`, `truncation: "auto"`), the MTP width policy
  (`--llama-mtp-policy cost`), the prefill slot loan (`--llama-slot-loan`), patches 0068-0072.
- Version 1.2.7 (2026-10-30): faster decode on every unit and faster coder
  prefill (patches 0064-0067, `--prefill-ubatch`), request cancellation on a
  client that leaves or a stop signal, twin-based KL instruments.
- Version 1.2.6 (2026-10-25): the loop guard's stretch budget point judged
  on a token repeat share; the release A/B reads decode on the majority
  answer.
- Version 1.2.5 (2026-10-25): fixes and clean-up: the door's budget lifts a
  recovered turn's masks and nudges/slaps a looping one; the stream drops the
  line breaks after the closer; /v1/completions starts inside an open block
  like chat; a clean-room speaker head for the voice-similarity tool.
- Version 1.2.4 (2026-10-25): the loop guard's door rung (multi-newline
  tokens masked in the open think block after a detection; ladder includes
  it; ladder-nodoor is 1.2.3's ladder); the coder unit runs the ladder.
- Version 1.2.3 (2026-10-24): a loop guard for small models
  (--loop-guard, README "New: loop guard for small models"), a non-thinking
  sampling row (--nothink-*) and an answer row (--answer-*), all off by
  default; the coder unit runs the ladder and the non-thinking row.
- Version 1.2.2 (2026-10-24): two opt-in guards for agent turns, off by
  default: --think-eog-mask and --tool-grammar; the think block decided by
  the closer's token id; the reasoning stream's UTF-8 hold-back;
  reasoning_closed on the stream. Speed equal to 1.2.1 on all three services.
- Version 1.2.1 (2026-10-20): patch 0063, the expert cache's CPU misses
  with Strata's multi-token rows for IQ2_XS, IQ3_XXS, IQ3_S and IQ4_NL and
  every miss of a verify window on the CPU (GGML_OPENCL_CPU_MISS_MAX 64):
  Flash-Next IQ3_XXS 33.9 -> 37.9 t/s decode, KL unchanged.
- Version 1.2.0 (2026-10-19): voice and vision (docs/milestone-1.2.0.md,
  docs/speech-service.md), three on-demand units on the B60: arcint-vlm
  :8083 (Qwen2.5-VL-7B, image_url data: URIs, --mmproj), arcint-tts :8084
  (Qwen3-TTS 1.7B, German, enrolled voices only, --tts, the projector on the
  CPU), arcint-stt :8091 (whisper.cpp's server on the same ggml, package
  arcint-stt, /props). Patch 0062 (libmtmd's TTS decoder, valid frames).
  Gates: VLM 9/9, TTS RTF 1.1167 and round-trip WER 0.0264, STT WER 0.0547;
  the language services byte-identical to 1.1.1 on both cards.
- Version 1.1.1 (2026-10-18): more image models (docs/milestone-1.1.1.md,
  docs/imggen-service.md): Qwen-Image (German lettering) on :8081, FLUX.2
  klein 4B with /v1/images/edits on :8082, ESRGAN 4x through
  /sdcpp/v1/upscale; patches 0059 (CONV_3D, the Wan VAE) and 0060
  (LEAKY_RELU); the image gate's per-model tolerances (D6).
- Version 1.1.0 (2026-10-18): image generation (docs/milestone-1.1.0.md):
  Z-Image Turbo on the B60 on demand through the arcint-imggen package
  (sd.cpp f0aac52's sd-server, /v1/images/generations, /health, /props);
  patches 0057 (fused ROPE_APPLY, OpenCL) and 0058 (q8_0 GEMM on XMX, the
  B60 only). 1024², 8 steps: 2.14 s a step, ~23.5 s a request steady.
- Version 1.0.0 (2026-10-16): the 1.0 charter met (docs/milestone-1.0.0.md);
  DFlash2 drafting opt-in. 0.8.1 removed the series' opt-in alternatives
  (54 patches); 0.8.0 removed the OpenVINO engine (charter A6).
- Production: the agent on the B60 (245,760 ctx, q8_0:q4_0 KV, MTP 5), the
  coder on the A770 (98,304 ctx, q8_0:q4_0 KV, MTP 4), Flash-Next on the
  B60 on demand (IQ3_XXS since 2026-10-15, IQ2_XS the faster option;
  131,072 ctx, q8_0 KV, expert cache 12,000 MiB, MTP 3), the writer on the B60 on demand.
- 1.0 charter: docs/milestone-1.0.0.md (feature complete, almost no gains
  by obvious changes).

## Current numbers (measured-here, 1.1.0 pre-tag, the units' flags, A/B against 1.0.0)

- agent, B60: prefill 708.7 t/s at 54,895 tokens, 498.5 at 138,028 (1.0.0
  695.7 / 491.9: 0058 runs the MTP head's Q8_0 weight in the prompt pass);
  the needle at 138k; acceptance task 10/10.
- coder, A770: prefill 724.3 t/s at 40,841 tokens; decode 51.1 / 47.5 /
  44.0 t/s on three 300-token answers; the needle; acceptance task 9/10.
- Flash-Next IQ3_XXS (the default), B60 + 28.72 GiB host bank: prefill
  617.2 t/s at 20,045 tokens right after the capital (1.0.0 595.2); decode
  35.2-35.4 t/s; the needle. The IQ2_XS option (0.8.0): 665.6 t/s and 42.3 t/s.
- images, B60: Z-Image Turbo 1024², 8 steps: 2.14 s a step, VAE 6.3 s, peak
  13.4 GiB, the image gate PASS (42.6 dB against a CPU reference); 512²
  0.48 s a step; sd.cpp's Vulkan on the same card 7.64 s a 1024² step.
- Each equal to or faster than 1.0.0's binary in the same window (the release gate). Strata's SYCL port on
  the same card (IQ2_XS): 620 t/s and 37.8 t/s (2026-10-14).
- Quality (KL vs Q8_0 or CPU reference, 16 x 512): agent q8_0:q4_0 KV
  0.0057 nats; coder's searched quant 0.0237; Flash-Next IQ3_XXS 0.218 nats,
  top-1 89.7 % (the default; IQ2_XS 0.294 and UD-Q3_K_XL 0.100 are on-demand
  options; IQ2_XS and IQ3_XXS outside the bar, accepted).

## Correctness rule (operator, 2026-10-01; CLAUDE.md, DESIGN 3.4)

- Correctness is judged at the answer: the answers stay right (facts,
  needle, task battery); the candidate's mean KL against the reference is at
  most 0.03 nats worse than the baseline arm's on the same card and window;
  argmax agreement drops by at most 1 point.
- Bit-identical output across configurations, placements, timing or request
  history is not required. Integrity checks on copied data (expert slots,
  bank entries, restored checkpoints) stay exact.
- Byte-equality cells in the suites are tripwires: a red one is reported,
  not a veto.
- Gates are per phase: a change that improves prefill or decode and does not
  regress the other beyond the run-to-run spread is adopted if it passes the
  answer-level bar.
- Release gates (operator, 2026-10-15): acceptance task >= 9/10 through the
  deployed package (10/10 the target); speed: the candidate against the last
  release's binary, alternated in one window, not slower beyond its spread
  (operator, 2026-10-16; the absolute floors are a report); the
  equivalence and concurrency suites on --engine llama.

## Contracts

- HTTP: POST /v1/chat/completions, POST /v1/completions (OpenAI-compatible,
  SSE streaming; /v1/completions also takes token-id prompts), GET /health,
  GET /props, GET /v1/models; with --slots, GET /slots (slot state only — id,
  lane, n_ctx, is_processing, n_cache, and a busy slot's request id; no prompt,
  completion, token ids or sampling parameters). No UI.
- Token surface (llama-server-compatible, behind the same key as every route):
  POST /tokenize takes {content, add_special?, with_pieces?, model?} and
  returns {tokens}; with_pieces returns {id, piece} objects, a piece that is
  not valid UTF-8 on its own being the array of its byte values. content must
  be a string (llama-server's mixed array of strings and ids is a 400 here).
  POST /tokenize also takes a chat body (messages, tools) or a Responses body
  (input, instructions, tools) and returns {tokens, count}: the prompt the
  request would decode, counted through the request's own prepare path (one
  tokenisation, never a second). A prompt the lane would refuse is refused by
  /tokenize too, the count in the error body's prompt_tokens; a count over a
  continuation does not evict the referenced stored response (it is a lookup,
  so it refreshes that entry's LRU rank like any other read); an image part is
  refused (the projector counts tokens /tokenize does not run). POST /detokenize
  takes {tokens} and returns {content}, special tokens rendered as text
  (unparse_special=true); an id outside the vocabulary is a 400 naming tokens.
  parse_special is accepted and ignored (specials are always parsed). The
  model field picks the lane as the generation routes do.
- --served-model-name NAME sets the id /v1/models and /props report. The
  model object carries id, n_ctx (running), n_ctx_train, quant, lanes.
- Named lanes: --served-model-name A,B --lane-ctx CA,CB serves lanes of
  different context from one process and one KV pool of their sum
  (kv_unified, a cell window per lane). The model field picks the lane
  (empty or the canonical id: the first; unknown: 404); the 400 quotes that
  lane's context; a busy lane is a 503 for itself after --queue-timeout
  (DESIGN 4.2, amended 2026-10-07).
- Context overflow is HTTP 400 with prompt_tokens/n_ctx/overflow. No
  truncation, no context shift (GDN state cannot un-see tokens; DESIGN 3.8,
  an operator invariant).
- Client disconnect aborts at the next step boundary.
- Tool calls parsed server-side into OpenAI tool_calls (JSON body and the
  coder's <function=>/<parameter=> XML with schema-driven coercion);
  deterministic call ids; never executed; no tools declared = raw text.
- Reasoning: text before the bare think closer goes to reasoning_content;
  enable_thinking and reasoning_effort honoured.
- preserve_thinking: chat_template_kwargs.preserve_thinking (Chat) or
  arcint.preserve_thinking (Responses) keeps every assistant turn's thinking,
  not only the turns after the last user query, so a steering message does not
  drop a running tool loop's reasoning; absent renders exactly as before.
  chat_template_kwargs is a passthrough to the template (llama-server's
  dialect): enable_thinking and preserve_thinking are read and type-checked by
  arcint; a key naming what the template context sets itself (messages, tools,
  add_generation_prompt, bos_token, eos_token, strftime_now) or one of minja's
  31 builtins is a 400; every other key reaches the template unchanged.
  /props' chat_template_caps carries supports_preserve_reasoning and
  supports_preserve_thinking.
- Sampling defaults: request > operator flags > the GGUF's defaults; /props
  names the provenance.
- Console: one-line state/timing prints on stderr (load, expert cache,
  per-slot prefill/decode rates, draft acceptance).
- Security: no authentication by default; --api-key/--api-key-file add a
  bearer token on every route but /health (that includes /props, /metrics and
  /slots; the CORS preflight is answered without the key). TLS only in a build
  configured with -DARCINT_TLS=ON (--tls-cert/--tls-key); the default build
  links no OpenSSL. LAN or a reverse proxy only.

## Model input

- A GGUF as published (--gguf FILE; the first shard of a split file). The
  engine admits general.architecture qwen35, qwen35moe, qwen4exp, and llama
  at the Mistral Small 24B geometry; anything else is refused at load. No
  allowlist (retired with the OpenVINO engine, operator 2026-10-15).
- Flash-Next's MTP layer: an MTP-only GGUF built from the checkpoint's 31
  mtp.* tensors by tools/flash_next_mtp_gguf.sh (--llama-mtp-gguf); its
  expert ranking: Strata's decode profile (--llama-expert-profile).

## Mechanisms

- Kernels (contrib/llama.cpp/patches, README per patch): K-quant integer-dot
  matvec and fp16/int8 DPAS GEMMs with 2D block loads; IQ2/IQ3/IQ4/Q2_0 in
  planes; the gated delta-net with rollback snapshots in the recurrent cache;
  split decode attention, XMX prompt attention, quantized KV attention, GQA
  verify attention; fused decode launches; inputs staged in a USM ring.
- KV: llama.cpp's cache, --llama-kv f16 | q8_0 | q8_0:q4_0 (others refused);
  --llama-reserve-cells N reserves compute buffers for N cells.
- Speculative decoding: the model's own MTP head (--llama-mtp N), draft 0
  from the catch-up batch, --llama-mtp-min-p stop, --llama-mtp-depth-max
  cutoff, verify walk in the sampler's order (greedy and seeded sampled);
  rollback by recurrent snapshot. Both lanes busy: one draft loop and one
  verify for both.
  Or a DFlash2 drafter (--llama-dflash-gguf FILE, --llama-dflash N up to
  7, --llama-dflash-min-p P; llama.cpp's draft-dflash on libllama): a block
  a drafter pass from the target's layer inputs, the selector's lattice
  walked greedily; not with --llama-mtp, not under --llama-layer-split.
  Opt-in: against MTP 5 on the agent +27 % on code, -5 to -8 % on prose
  and at 4k, -33 to -36 % at 130-200k (the reference likewise past ~60k);
  the acceptance task 0/10 at 212,992 tokens (a different greedy path of
  the same target, MTP 5 and MTP 7 10/10, not attributed; DFlash2 10/10 at
  32,768); /props dflash.drafts is the drafter's own count; the agent
  unit keeps MTP 5 (docs/campaigns/dflash2-agent.md on the qfndev branch).
- Context checkpoints (--llama-checkpoints N, --llama-checkpoint-step T):
  per lane, the recurrent state at user messages and near the prompt's end,
  in host memory; a follow-up or an edited message resumes from the newest
  at or below the shared prefix. No prefix cache across lanes or restarts.
- Expert cache (Flash-Next; --llama-cpu-moe 48 --llama-expert-cache MIB
  --llama-expert-profile F): hot experts in VRAM slots, the rest in a pinned
  USM host bank read by the card over the link; swaps by Strata's adapt rule
  on their own queue;
  up to 16 decode misses a layer computed on the CPU with Strata's AVX2 rows
  (LLAMA_EXPERT_CACHE_CPU_MISSES=1); prefill streams bank tensors through a
  VRAM ring (GGML_OPENCL_BANK_STREAM=1). The 27 GiB n-gram table is read
  lazily.
- Two cards by layers: --device GPU.0,GPU.1 --llama-layer-split K, an expert
  cache per card (--llama-expert-cache A,B), --llama-mtp-card N.
- Environment switches: docs/switches.md (operational, tuning, off, red
  case, diagnostic; the opt-in alternatives were removed in 0.8.1).

## Divergence policy (DESIGN 1.1)

Upstream PR first; else a numbered, PR-shaped patch against the pinned
llama.cpp in contrib/llama.cpp/patches, documented with its measurement,
KL and red case; a fork only with a stated reason and exit condition (none
exists). Moving the pin: contrib/llama.cpp/README.md "Moving the pin".

## Command-line options (arcint --help)

- --engine llama --gguf FILE: the engine (llama is its only value) and the
  file.
- --device GPU.N | NAME-SUBSTRING | GPU.0,GPU.1: the card(s), in OpenCL
  enumeration order (GPU.0 = B60, GPU.1 = A770 on the dev host).
- --host ADDR (127.0.0.1), --port N (8090), --http-threads N.
- --metrics (off): serve GET /metrics in the Prometheus text format with the
  request/status, token, prefill/decode, cache-hit, draft and lane gauges. A
  request field `arcint.timings: true` adds the per-response timing object
  (llama-server's prompt_n/prompt_ms, predicted_n/predicted_ms, cache_n,
  draft_n, draft_accepted) on Chat Completions and Responses.
- --slots (off): serve GET /slots — one entry per slot with id, lane, n_ctx,
  is_processing, n_cache (the KV cells that slot's sequence holds) and, while
  the slot serves a request, id_task: that request's X-Request-Id. State only:
  no prompt, no generated text, no token ids, no sampling parameters. The prompt
  (detokenized), the generated text and the sampling parameters are what
  llama-server's /slots carries, and slots.sh asserts the absence with a
  marker in the busy request's prompt. Behind --api-key like every route;
  /props lists it only when it is served.
- --api-key KEY | --api-key-file PATH: require `Authorization: Bearer KEY` on
  every route but /health (GET/HEAD only; a CORS preflight -- an OPTIONS with
  Access-Control-Request-Method -- is exempt). The check runs before the
  request body is read, so an unknown path is a 401 too (no route probing),
  and a 401 names the scheme (WWW-Authenticate: Bearer). The file is read once
  (exactly one trailing newline stripped); an empty or newline-only file, a
  key with a control character or DEL, or one that starts or ends with a space
  is a boot error; a percent sign warns (cpp-httplib URL-decodes header
  values) and a group/world-readable file warns with its mode. The server
  keeps only the key's sha256 digest in its long-lived state; the key is never
  logged and never on
  /props; --api-key is visible in ps and /proc/<pid>/cmdline (test use;
  deployments use the file). /health stays open for probes and reveals the
  served model id.
- X-Request-Id: every response carries one request id. A caller may supply it;
  the value is taken over only when it is 1..128 characters of [A-Za-z0-9._:-].
  Absent, empty or anything else is replaced by a generated req- plus 16
  lowercase hex characters (random, not a counter) -- a replacement is not an
  error and the bytes sent are never echoed or logged, which is what the narrow
  grammar buys (CR, LF, SP, HTAB, non-ASCII and every log-field separator are
  out). The id rides as rid=<id> on the request's own log lines: the http line,
  the refusals, the per-request prefill/decode summary and its abort. It is on
  successes, on 400/401/404/409/500, on the pre-routing auth refusal, on a CORS
  preflight and on SSE responses before the first chunk. JSON bodies, the
  Responses objects and /metrics are unchanged (no id label: cardinality); with
  --api-key the header is allowed and exposed so a browser client can read it.
- --tls-cert FILE --tls-key FILE: HTTPS through httplib's OpenSSL server;
  needs a build configured with -DARCINT_TLS=ON (the default build refuses the
  flags and does not link OpenSSL).
- --served-model-name NAME[,NAME2] with --lane-ctx CA,CB; --parallel N;
  --n-ctx N (default the smaller of 32,768 and the trained context);
  --queue-timeout S; --prefill-chunk N; --prefill-ubatch N (n_ubatch, default 512).
- Sampler defaults: --temp, --top-p, --top-k, --min-p,
  --repetition-penalty, --presence-penalty,
  --chat-template-kwarg enable_thinking=BOOL.
- --llama-kv K[:V]; --llama-threads N; --llama-reserve-cells N.
- --llama-mtp N, --llama-mtp-gguf FILE, --llama-mtp-vocab FILE,
  --llama-mtp-min-p P, --llama-mtp-card N, --llama-mtp-depth-max N (0, off:
  past N tokens of a slot's context it decodes one token a round without
  drafting; /props mtp.depth_max).
- --llama-dflash-gguf FILE, --llama-dflash N (7), --llama-dflash-min-p P,
  --llama-dflash-ubatch N (64).
- --llama-checkpoints N (32), --llama-checkpoint-step T (8192).
- --think-eog-mask (off; request field think_eog_mask): while a think block
  is open, the tokens that end the turn are not offered.
- --tool-grammar (off; request field tool_grammar): a tool call is
  constrained to the declared schemas by a GBNF.
- --loop-guard off|detect|nudge|slap|door|ladder|ladder-nodoor (off; request
  field loop_guard), --loop-guard-door-grace N (20),
  --loop-guard-window N (200), --loop-guard-threshold T (0.8),
  --loop-guard-grace K (40), --loop-guard-match exact|near (near).
- --nothink-temp, --nothink-top-p, --nothink-top-k, --nothink-min-p,
  --nothink-presence-penalty: the row a thinking-off request starts from.
- --answer-temp, --answer-top-p, --answer-top-k, --answer-min-p,
  --answer-presence-penalty, --answer-frequency-penalty: the row after the
  think block closes, in a thinking turn.
- --llama-cpu-moe N, --llama-expert-cache MIB[,MIB],
  --llama-expert-profile FILE, --llama-layer-split K.
- --stub, --stub-delay-ms N (the device-free test backend); -v, -vv;
  --version.

## Layout

- README.md: what arcint is, models, numbers, building, running, scope.
- DESIGN.md: architecture (3), reuse and the correctness rule (3.4), MTP
  (3.5), the overflow invariant (3.8), the expert cache (3.9), lanes and two
  cards (3.10), serving (4), test gates (5), state of play (7), open items (8).
- docs/llama-engine.md: the engine's record, mechanism by mechanism.
- contrib/llama.cpp/: the pin, the patch series and its README.
- docs/switches.md: every environment switch, classified.
- docs/campaigns/: one document per open defect or lever, plus research-*.md
  surveys of prior art (research-reference-audit.md first).
- docs/sop-card-window.md (the card window), docs/release-checklist.md
  (generated from tests/acceptance/cells.json), docs/docker.md,
  docs/milestone-1.0.0.md, docs/history.md (the retired OpenVINO record).
- CHANGELOG.md: per-release changes and dependencies.
- src/: config, util/, core/ (sampling, stop, toolcall, chat), exec/
  (backend interface, stub, backend_llama, llama_spec, verify_walk,
  llama_layer_split), api/, http/.
- tools/: the Flash-Next MTP GGUF builder, KL and reference tooling, kernel
  tuning, expert-cache trace replay.
- tests/: `unit` label (bare ctest, device-free) and the acceptance cells in
  tests/acceptance/cells.json run by tests/acceptance/run.py on a card.
- contrib/packaging/arcint/ (the Debian recipe), contrib/systemd/ (units),
  docker/ (the image).
- third_party/: cpp-httplib, nlohmann/json, minja.

## Build

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build
ctest --test-dir build --output-on-failure -L unit
./build/arcint --stub --port 8090 -v

Serving build: -DARCINT_LLAMA=ON -DARCINT_LLAMA_DIR=<llama.cpp at 3e4dfa8
with contrib/llama.cpp/patches applied> -DGGML_OPENCL_USE_ADRENO_KERNELS=OFF;
needs OpenCL headers and the ICD loader, and Intel's compute runtime to run.
-DARCINT_WERROR=ON is warning-clean; ASan+UBSan clean on x86_64.

TLS is an extra arm: -DARCINT_TLS=ON (finds and links OpenSSL) accepts
--tls-cert/--tls-key. The default build refuses the flags and stays free of
libssl, so the package's Depends do not change.
