726bc970

By: Michael Lynch <git@mtlynch.io>

Reject invalid startup configuration before side effects

Application and importer configuration was spread across global flags, raw environment lookups, build-tagged helpers, and late startup code. Invalid ports, URLs, SMTP settings, or proxy values could be discovered only after directories were created, clients initialized, or SQLite opened; any nonempty BEHIND_PROXY value also enabled trusted forwarding behavior, including false-like strings.

Litestream constructs a typed Config from defaults, overlays external values, validates field and cross-field constraints, and only then starts operational components in reference/litestream/cmd/litestream/main.go. Adopt that parse-then-validate boundary while keeping this application's configuration much smaller and immutable.

Add startupconfig plans for the app and importer, inject environment lookup for deterministic tests, and parse all command inputs before resource initialization. Strictly validate ports, booleans, time zones, media mode, S3 and public URLs, SMTP settings, sender addresses, and production HTTPS requirements. Build-tagged login policies retain the existing development and production behavior, and Fly now supplies the canonical BEHIND_PROXY=true value. S3 configuration owns typed URL validation without exposing credentials in errors.

The implementation had to preserve build-tag symmetry and secret bytes while eliminating process-global reads. Development must not require production SMTP values, production must validate them before opening SQLite, and passwords or keys must not be trimmed, formatted, or logged. Litestream-specific credentials remain Litestream's responsibility; this app validates only the LITESTREAM_BUCKET presence signal that selects SQLite pragmas rather than duplicating litestream.yml policy.

No external blockers were encountered. Production and development focused tests, the full Go checks, and every flake target passed before the amendment.

Suite timing

Time to Start Worker time Duration Time to finish Idle
Config 10s 2s 2s 13s 10s
Eval 50s 17s 17s 1m07s 36s
Build 1m06s 5m07s 7m35s 8m42s 5m01s
Suite 10s 5m27s 8m32s 8m42s 5m48s

Timeline

0s1m6m20s6m40s7m7m20s7m40s8m8m20s8m40s