You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(config): reject unknown config keys and log provider origins (#511)
* fix(config): reject unknown config keys and log provider origins
A misindented `providers:` section parses as a null section plus unknown
top-level keys. yaml.Unmarshal discarded those silently, so the gateway
booted with none of the operator's providers and no error — the fault only
surfaced once an env-provisioned provider was removed and startup failed
with "no providers were successfully registered".
Parse the YAML layer with KnownFields(true) so an unknown key fails startup,
naming the file and every offending line. This also catches ordinary typos
(`prot:` for `port:`, `bass_url:` for `base_url:`).
The env layer declares the same structures as JSON and overrides YAML entry
by entry, so a typo there would silently win over a correct YAML entry.
Decode VIRTUAL_MODELS, SET_RATE_LIMIT_*, and SET_BUDGET_* with
DisallowUnknownFields for the same reason.
Two fixes fall out of that:
- BudgetLimitConfig had no json tags, unlike its RateLimitRuleConfig
neighbour which added them for exactly this env form, so `period_seconds`
in a SET_BUDGET_* JSON array was silently dropped and produced a limit
with no window. Add the tags.
- `failover.overrides` is a removed key that old config files still carry
and that strict parsing would now reject. Model it as an explicitly
ignored field, upgrading it from silently ignored to a logged deprecation
pointing at `disabled_models`.
A config path that exists but cannot be read — a directory bind-mounted
where a file was expected — was indistinguishable from a missing file and
fell back to defaults. Report it instead.
Finally, log one line at boot showing how many providers came from the
config file versus environment discovery, with their names, so an operator
can see at a glance that a config file contributed nothing.
Closes#509
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(config): add CONFIG_STRICT escape hatch for unknown keys
Strict parsing is the right default — a dropped providers, rate_limits,
budgets, or guardrails entry silently changes routing, cost, or security —
but it blocks one legitimate case: rolling a binary back under a config file
written for a newer version, where an unknown key is expected rather than a
typo.
CONFIG_STRICT=false downgrades unknown keys to warnings that name the file,
line, and field, then boots without them. The default stays true.
Unknown keys are always detected: lax mode still decodes with
KnownFields(true) and inspects the resulting TypeError, so it can report each
ignored key instead of dropping it in silence the way yaml.Unmarshal did.
The flag relaxes which keys are accepted, never whether a value makes sense.
A malformed value (`port: [9999, 8080]`) and a YAML syntax error stay fatal
in both modes, and a file mixing an unknown key with a malformed value still
fails. The same rule applies to the JSON env layer, where only
encoding/json's unknown-field error is downgraded.
Entity-level validation is unaffected: a virtual model whose target names a
provider that does not exist still fails startup even when the key that would
have declared that provider was ignored with a warning.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(config): reject trailing data after declarative JSON and YAML values
Swapping json.Unmarshal for a json.Decoder to get DisallowUnknownFields
regressed a check nobody asked for but everybody relied on: Decode stops
after the first JSON value and leaves the rest unread, so
VIRTUAL_MODELS='[{"source":"a"}] {"targts":[]}'
booted happily while ignoring the suffix. json.Unmarshal rejects trailing
data; silently applying half an env var is precisely the failure this path
exists to prevent. Require io.EOF after the decoded value.
The YAML layer had the same hole, pre-dating this branch: both yaml.Unmarshal
and yaml.Decoder read one document, so every key after a `---` separator was
applied nowhere. Reject a second document.
Both are structural faults, not unknown keys, so they stay fatal under
CONFIG_STRICT=false.
Also correct the misindentation gotcha in the docs, whose prose counted keys
the sample did not contain. The sample now carries two providers and the
quoted error is the binary's verbatim output.
Reported by Greptile and CodeRabbit on #511.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* build: bump Go to 1.26.5 for GO-2026-5856
govulncheck reports the Encrypted Client Hello privacy leak in crypto/tls
(GO-2026-5856), fixed in go1.26.5. It reproduces on pristine main at e0eb0ee,
so CI is red independently of this branch; the reachable traces are the
dashboard, redis, and mongo TLS paths, none of them new.
Bump every pin so the shipped binary is patched, not just CI:
.github/workflows/test.yml GO_VERSION 1.26.4 -> 1.26.5
.github/workflows/release.yml go-version 1.26.4 -> 1.26.5
Dockerfile golang:1.26.4-alpine3.23 -> 1.26.5
go.mod go 1.26.4 -> go 1.26.5
Verified: govulncheck reports no vulnerabilities with and without the swagger
tag; unit, contract, e2e, and integration suites pass; golangci-lint is clean
across all build tags; and the release image builds and reports go1.26.5.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0 commit comments