Skip to main content

CLIENT_AUTHORITATIVE_KEYS

Constant CLIENT_AUTHORITATIVE_KEYS 

Source
pub const CLIENT_AUTHORITATIVE_KEYS: &[&str];
Expand description

The client’s own fields that survive an untrusted request.

§Budgets, not tastes — and the reasoning pair splits along that line

max_tokens has always been here: it is a budget on the client’s own turn, and dropping it would silently truncate answers the client sized deliberately. UNMODELLED_SAMPLER_KEYS’ own scope note draws the rule — “Budgets (max_tokens), stops, constraint machinery and observation … stay client-authoritative — they say what the request is, not how it should sample.”

reasoning_budget_tokens is that category by name, so it joins. It caps how many tokens this turn may spend thinking, it is enforced by llama.cpp’s own sampler-side budget rather than by a template, and — the load-bearing half — upstream governs it: -2 comes back as an HTTP 400 naming the range (ADR 0007 finding 7c). A client sending it is asking for a shape of turn, within bounds a second system already enforces.

reasoning_effort does not join, and the asymmetry runs the opposite way to what its name suggests. It is taste: it steers what the model is shown, its level vocabulary is per-template folklore, and upstream validates it not at all"banana" is accepted and rendered into the prompt verbatim. So it is precisely the field where an untrusted client’s value would reach the model unexamined by anyone, which is what the trust gate exists to stop. When untrusted it is dropped from the client layer, removed from the body by the cleanup in resolve_sampling, and named in client_fields_discarded.

The gate only reaches a level gglib could read, so it is half the story: an unreadable one ("banana", "none") never becomes a layer value to discard. That half is the same cleanup’s issues arm, and it applies on both sides of the gate — trusting a client is not trusting a typo.

Neither control is observable afterwards (ADR 0007 finding 7a), so the discard record is the only place the decision is ever visible.

§Public because the operator-facing surfaces describe it

gglib model explain and the GUI’s sampling inspector both print a caveat naming what survives an untrusted request, because the client rung is a real rung neither table can show. That sentence read “except max_tokens” for as long as this list was one key long, and nothing would have failed had it stayed that way after reasoning_budget_tokens joined — a user-facing description of the trust boundary, silently false. Exporting the list lets caveats_name_every_client_authoritative_key in gglib-cli’s explain_display assert the sentence against it, so the next key added here fails a test instead of shipping a wrong caveat. The TypeScript half cannot read a Rust constant; it carries its own copy, named and pinned, with a pointer back here.