Environment variables
The risk ceilings have no environment layer at all, three variables are refused at startup rather than ignored, and every read the workspace performs is declared in a CI-gated registry.
Three rules govern the environment, each because its opposite produced a real defect.
The ceilings are not env-settable, at all
vike_config::Policy — leverage, per-order notional, the slippage band, the halt-file admit mode,
the per-venue arming ceiling — implements neither EnvOverride nor CliOverride, and
crates/vike-config/src/layers.rs seals both traits, so an override is a compile error rather than
a review comment. Everything else resolves default, then file, then environment, then CLI.
Settings carries the split; the files are on the
settings reference.
Removed variables refuse to start
Deleting a read is only half the job. An operator who set VIKE_MAX_ORDER_NOTIONAL=250 believes a
ceiling is active; a build that quietly stopped reading it would trade uncapped while they
believed otherwise. crates/vike-config/src/removed.rs's REMOVED_ENV is the table, and
refuse_removed_env is the first step of startup for every root that loads settings —
vike-desktop, vike-tradehub and vike-cli:
| variable | replaced by | why it went |
|---|---|---|
VIKE_MAX_ORDER_NOTIONAL | policy.toml — max_notional_per_order | an order-size ceiling any exported variable can raise is not a ceiling |
VIKE_TRADEHUB_MAX_ORDER_NOTIONAL | policy.toml — max_notional_per_order | an order-size ceiling any exported variable can raise is not a ceiling |
VIKE_SECRETS_PASSPHRASE | secrets.env | nothing consumes it — credentials are read from the project's own store, in plaintext |
"Set" means present with a non-empty value after trimming: an empty VAR= configured nothing
under the old readers either, so refusing over a leftover blank line would turn a harmless artefact
into an outage. Every offender is reported in one pass, and the message renders the operator's own
value as a paste-ready TOML line — unless the value would itself be a secret, where echo_value is
false and only the name is printed, since echoing one turns a startup error into a credential in
any log capturing stderr.
Two daemons deliberately do not refuse, each stating its reason in its own boot spec rather
than as a bare flag — crates/vike-boot/src/lib.rs's RemovedEnv::Ignore. The recorder and the
data service read none of the ceilings whose variables were removed, and a recorder must not
acquire a new reason to fail to start; on those boxes vike-cli config check, which the shipped
units put in their ExecStartPre=, is the surface that refuses a stale environment.
A removed FILE refuses the same way — and says which of two things it found
A <project>/vike.toml override layer once sat above <project>/settings/, reintroducing the
which file won? question that directory exists to answer. It is refused by name from inside the
loader, and only NotFound counts as absent — anything else means absence was not established,
so it refuses too, with a different message. Under
a service sandbox a stat below the home tree returns EACCES, not ENOENT, and the fail-closed
verdict once named a file that did not exist. Nothing here rewrites the file: it is the operator's.
Every read is declared
vike_ops::settings::SETTINGS is a verbatim registry of every environment variable the workspace
reads: one row per (name, crate) pair, carrying scope, the layer the read happens at, how the
name reaches the code, and the documented default. The table is data — the runtime never consults
it. It exists so crates/vike-ops/tests/settings_registry.rs can gate the source tree and the table
against each other both ways: an undeclared env::var fails CI, and so does a stale row.
Layer encodes the rule the registry exists for — libraries take configuration as parameters; only
binaries read the process environment. Binary and Injected (a pure parser over a caller-supplied
map) are the correct shapes; Library, a direct env::var under src/ outside a binary, is the
work list. That list is a ratchet: LIBRARY_PIN holds it as a fixed-size array, so it may shrink
but never grow. It had to be — the count went from 60 on 2026-07-29 to 77 a week later, across five
changes that each had no reason to notice.
Finding out what a box is actually reading
vike-cli config show --section env prints the registry resolved against this machine, with
--filter (a substring of the name or the reading crate), --changed-only and --json. The store
precedence is process env, then the credential store, then the documented default, and
presence wins rather than non-emptiness: an exported NAME= beats the store with an empty string,
reported as such.
That order is what the stores support, not a promise any reader honours
Most consumers read exactly one store, so a source word alone would assert a provenance the tool
cannot verify. Every row therefore also prints what its reader does: env for a direct
env::var, caller-map for a map the caller supplies, unknown for a computed name. A value found
only in the credential store while its reader calls env::var is warned about under the table.
Redaction happens on construction, inside the resolver rather than in either printer, so --json
cannot leak what the table hides. One disclosure is stated rather than hidden: the registry declares
the whole per-venue, per-tier API-key grid, so the table names those keys and marks each set or
unset — pasted output shows which venues a box holds keys for, never a value.
Three worth knowing before you need them
VIKE_SETTINGS_DIR names the settings directory outright, honoured before any filesystem probe and
skipping the project walk entirely. A blank value is ignored rather than honoured: an empty
Environment=VIKE_SETTINGS_DIR= line would otherwise resolve settings to the working directory.
RUST_LOG (aliased VIKE_LOG) filters the console only. The file layer defaults to trace and
captures the log-to-tracing bridge, so a batch tool driving a chatty dependency writes a JSON
file no console setting can turn down: one hour of a backfill measured 6.4 GB, and a long range
once wrote 341 GB and nearly filled the disk hosting a live trading node. VIKE_LOG_FILE_LEVEL=warn
(or off) is the switch — see log volume. VIKE_LOG_DIR tops
the log-directory precedence, above a config file's log_dir and the project's own
settings/state/logs. The gates behind all of this are on
settings gates.
Reference
One core, several front ends — how the desktop app, the daemon, the CLI and the data service relate, and where each one's flags, binaries and settings live.
Binaries
Every binary target in the Vike Trader workspace — 46 across 17 crates — derived from cargo metadata and classified: its own page, its collector family, or an internal harness.