Settings gates
The environment-variable registry and its two ratchets, plus the two gates that refuse a settings key nothing reads.
Two separate questions get gated here, and they fail for opposite reasons. The registry asks "is every environment read declared, and does a library do it?" The consumption gates ask "does anything actually read the key this file accepts?" A settings key that nothing reads is worse than an unimplemented feature, because the CLI reports it as the origin of an effective value and hands the operator positive confirmation of something false.
The registry
vike_ops::settings::SETTINGS is the verbatim registry of every environment variable this workspace
reads. It is data, not behaviour — nothing in the runtime consults it. It exists so
crates/vike-ops/tests/settings_registry.rs can walk the real source tree and assert that the table
and the tree agree in both directions: an undeclared env::var fails CI, and a stale row fails CI
too. Rows are keyed on the pair (name, krate), not on name alone, so one variable read from several
crates gets one row per crate, each carrying its own default and evidence.
Genuinely dynamic call sites — a name the program assembles at runtime — must be named in
DYNAMIC_ALLOWLIST with a written reason. A blind spot has to be declared to exist, and that
principle recurs: MAP_LOOKUP_PROVEN pins exactly which map-lookup rows rest on a resolved call site
rather than on the loose literal sweep, and it fails on a removal and on an addition, so the
remaining blind spot is measured rather than assumed smaller.
The rule the registry exists to enforce
Libraries take configuration as parameters; only binaries read the process environment or the
credential store. A Layer records which shape each row has — Library is the work-list (a
library reading global state its caller can neither see nor override), Injected is the target
state (a pure parser over a caller-supplied map), and Binary is the third correct shape.
The Library work-list is a ratchet: it may shrink, never grow
LIBRARY_PIN pins the set of offending (krate, name) pairs as a fixed-size array, so its declared
length is the only machine-true count and no prose is allowed to be the authority for it. A ratchet
was needed because a flat ban is unavailable — dozens of legitimate rows exist and four families are
deliberately exempt — and because the count grew silently across five pull requests that each had no
reason to notice, while every prose statement of it went stale. library_rows_do_not_grow names the
offending pair and the two legitimate responses; library_pin_has_no_stale_rows fails on a removal
with "delete the line", a one-line cleanup rather than a blocker.
The credential store is a second ratchet, on a class the first cannot see
Everything above keys on an env::var call, and the credential store is not read that way: it is
a filesystem read of a path a runtime walk produces, with no env::var anywhere near it. A library
loading it was therefore structurally invisible to LIBRARY_PIN, not merely unpinned.
CREDENTIAL_STORE_PIN closes that — same walk, same comment stripper, same Layer rule, keyed on
the name of the function that performs the read. It is a separate pin because there is no
variable name to key on and because the fix is a different instruction: "take the map as a
parameter", not "move the read to a binary".
Keying on a name left its own hole, and a real defect walked into it: a hand-rolled filesystem read
of a working-directory-relative .env — a second credential store outside the project settings
directory — had no name to key on and was invisible to both gates. So the ratchet takes a second kind
of evidence, keyed on the shape: a filesystem opener whose path argument names the store. The
residual is declared and executable — a path bound to a local first, produced by a helper, or reached
through a loop variable still escapes, and a test asserts each miss so the blind spot cannot be
quietly widened or quietly closed.
The key that appears as no literal at all
Credential keys have neither a variable name nor a reader name: the loader composed each one from a
{VENUE}_{TIER} prefix and handed it to a map lookup, so the key existed only at runtime. One
venue's live API secret was read on every credential probe with neither a row nor a sighting, while
its demo twin had one only because a test fixture happened to spell it.
The fix was not a fourth scanner: it was making the key set data. vike_model::credential_keys
folds its suffix and tier tables over VENUES into the whole enumerable grid, the loader composes
its names from those same tables, and every_generated_key_is_declared demands a registry row for
each. Adding a venue, a tier or a suffix now reddens CI until the rows exist — exactly what the
roster contract promises for every other per-venue table.
The two consumption gates
crates/vike-config/tests/policy_is_consumed.rs exists because a risk ceiling shipped as a field,
was validated on load, was accepted by the strict unknown-key check, and was read by nothing —
while a module doc stated in as many words that it was consumed at the order surfaces. The field was
removed. Its rows now name a file and a needle, and every_claimed_consumer_really_reads_it opens
that file and looks, so claiming a ceiling is enforced when it is not turns the gate red, and so does
deleting the consumer later.
crates/vike-config/tests/settings_are_consumed.rs is the twin for the three types that had the
identical defect and no gate at all. A clean-install validation found five keys displayed as
effective while nothing read any of them: no control server, nothing listening, and the trace log in
the wrong directory at trace level — a configuration that once wrote 341 GB onto the disk hosting a
live trading node.
Both gates run four directions: every field or key has a row; every claimed consumer really reads it; a claimed consumer must live outside the settings crate, since that crate parses, validates, clamps and serializes every field and would otherwise let every row claim consumption while asserting nothing; and a row admitting no consumer must be honest in the other direction.
settings_are_consumed.rs checks pub data rather than a fixture, because a test alone would have
left vike-cli config show still lying — the command reads the same rows and names the unread keys.
What each file governs is on Settings and under
Settings reference.
Adding a venue
The scaffold whose work-list is derived from the tree rather than remembered, the gate that keeps it derived, and the shared homes a new venue consumes without modifying.
Docs gates
Six gates over prose — citations, documented defaults, documented commands, duplicated facts, and the two hand-maintained indexes.