A pre-flight + readiness command that validates configuration and connectivity without booting the server. Use it as the first diagnostic when something "feels off", and let install scripts gate on its exit code.
breadbox doctor [--json] [--skip-external]
- Exit code
0— all checks pass (or were skipped). - Exit code
1— one or more checks failed. Remediation hints are printed next to each failure.
| Flag | Description |
|---|---|
--json |
Emit a structured {"checks": [...], "ok": bool} document instead of a pretty table. Useful in CI and install scripts. |
--skip-external |
Skip DNS and outbound HTTP checks (DNS lookup + GET /health/ready against PUBLIC_URL/DOMAIN). Use this on air-gapped installs or when the box is still behind a firewall. |
| Name | What it validates |
|---|---|
config load |
.local.env / .docker.env parse cleanly and required env vars decode. |
database |
DATABASE_URL is set, Postgres accepts a connection, and Ping succeeds. |
migrations |
The goose_db_version in the DB matches the highest numeric prefix among embedded migrations. Reports "behind" (needs breadbox migrate) or "ahead" (binary downgrade). |
encryption key |
ENCRYPTION_KEY is set (required if Plaid or Teller is configured), hex-decodes to exactly 32 bytes, and is accepted by AES. |
plaid |
When PLAID_CLIENT_ID is set: PLAID_SECRET is present and PLAID_ENV is one of sandbox/development/production. |
teller |
When TELLER_APP_ID is set: TELLER_ENV is valid, cert/key paths (if any) exist and are readable, or PEM bytes are available via app_config. |
provider credentials |
Walks every non-disconnected bank_connections row and verifies encrypted_credentials decrypts with the current key. Catches silent ENCRYPTION_KEY rotations. |
admin account |
auth_accounts contains at least one row — otherwise the setup wizard hasn't run. |
scheduler |
sync_interval_minutes is positive; any BACKUP_CRON / SYNC_CRON env value parses as a 5-field cron expression. |
public url |
When PUBLIC_URL or DOMAIN is set (and --skip-external isn't): DNS resolves and GET /health/ready returns < 400. |
Encryption-key fingerprint vs stored hash: deferred to issue #688. Once the fingerprint is persisted at setup,
doctorwill compare it against the key in the environment to catch rotations even before any bank connection exists. Until then, the credential-decrypt walk is the proxy.
Check Status
- -------------------- ----------------------------------------
✓ config load environment=local
✓ database connected and reachable
✓ migrations up-to-date (version 20260417032707)
✓ encryption key set and 32 bytes
✓ plaid client/secret set, env=sandbox
✓ teller cert/key present, env=sandbox
✓ provider credentials 3 connection(s) decrypt cleanly
✓ admin account 1 account(s) present
✓ scheduler sync every 720m; cron expressions valid
⊘ public url neither PUBLIC_URL nor DOMAIN set
OK — all checks passed.
breadbox doctor --json --skip-external > doctor.json
jq -e '.ok == true' doctor.jsonA future one-liner installer (issue #686) is expected to run breadbox doctor
after launching the process to surface configuration problems before handing
the operator a URL. This PR does not yet wire doctor into install.sh.
- Does not mutate state. Read-only across the board. Running it repeatedly is safe.
- Does not boot the HTTP server. Use
breadbox servefor that — doctor is designed to diagnose startup failures without needing startup to succeed. - Does not probe provider APIs. A healthy Plaid env var doesn't guarantee the credentials are valid with Plaid itself; the credential-decrypt walk is strictly a local AES check.