P0: validate configuration on startup, fail fast on bad config #52
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
pixadcurrently starts with whatever the config file contains; bad values surface later as runtime errors. Production P0 fromTODO.mdFuture Steps (queued after #51): validate the full configuration at startup and exit nonzero with a clear error before binding the listener.Definition of done
signing_keyunless a documented keyless mode is intended — decide and document), values in range (ports, timeouts, sizes positive and sane),allowlist_hostsentries well-formed, paths creatable/writable, etc.source_host_whitelist/whitelist_hostsREADME mismatch).make checkgreen;TODO.mdWorkflow bookkeeping in the finishing commit; finishing commit title ends with(closes #N)for this issue.Process
Standard workflow: feature branch from
main, direction via comments on this issue, discussion on the PR, adversarial review,merge-ready+ assign sneak on pass.Picking this up. Definition of done and implementation plan follow; PR to come on a feature branch from current
main.Definition of done
internal/config/config.go(getString,getInt,getBool) swallow every conversion error and silently return the default, andloadConfigFilelogs a warning and CONTINUES when a config file at a standard location exists but fails to parse. Both become hard errors.whitelist_hostsvsallowlist_hostsmismatch). Theenvsection stays permitted (smartconfig consumes it for environment injection), and nestedmetricssubkeys are validated too (username/passwordonly).portan integer in 1-65535 (no float truncation:8080.5is an error, not 8080);upstream_connections_per_hostan integer of at least 1;signing_keyrequired and at least 32 characters (existing rule, kept — no keyless mode; documented in the config error message);allowlist_hostsentries must be bare hostnames (non-empty strings, no scheme, no slash, no whitespace);state_dirnon-empty, must be creatable and writable (verified at startup with a probe file);sentry_dsnif set must parse as a URL with scheme and host;metrics.usernameandmetrics.passwordmust be set together or not at all;debug/maintenance_mode/allow_httpmust be booleans or ParseBool-able strings, not arbitrary numbers.config.Newreturns the error, fx aborts before binding the listener, process exits nonzero.Implementation plan
internal/config/config.go: extract anewFromSmartConfig(sc)construction/validation function thatNewwraps (keeps fx wiring untouched, makes the logic testable without fx). Replace the silent-default getters with strict variants that distinguish key-absent (default) from key-present-but-invalid (error). AddvalidateKnownKeysagainst the canonical key set (debug,maintenance_mode,port,state_dir,sentry_dsn,db_url,metrics,signing_key,allowlist_hosts,allow_http,upstream_connections_per_host,env). Extendvalidate()with the range/wellformedness rules above. Make a parse failure of an existing config file fatal inloadConfigFileinstead of warn-and-continue.internal/config/config_test.go: TDD — failing tests committed first, covering (a) omitted keys use defaults (minimal config with only a validsigning_key), (b) a table of set-but-invalid values each asserting startup fails with an error naming the key (invalid port string, port 0, port 70000, float port, non-booldebug, zero/negativeupstream_connections_per_host, allowlist entry with scheme, non-string allowlist entry, shortsigning_key, missingsigning_key,metrics.usernamewithout password, invalidsentry_dsn, explicitly emptystate_dir), (c) unknown top-level key aborts naming the key, (d)envsection is not flagged as unknown.main, tests committed first, then the implementation,make fmton touched markdown,make checkgreen,TODO.mdbookkeeping in the finishing commit, PR title ending with (closes #52), labelneeds-review.Implementation is up as PR #53 (branch
feature/config-validation, labeledneeds-review), executed per the plan above: TDD (failing enforcement tests committed first, then the implementation),make checkgreen, end-to-end verified (pixadexits 1 withconfig key "port": value "banana" is not an integeron a bad value andunknown config keys: whitelist_hostson a typo'd key). One deviation from the plan, documented in the PR: the lenientgetStringSlicehelper is retained because existing tests exercise it and test modification needs explicit approval — strictness forallowlist_hostsis enforced on the raw value byvalidateAllowlistHostsValuebefore extraction, so the no-silent-fallback guarantee holds regardless. Awaiting adversarial review per the standard workflow.