- sync-env.mjs: add hasEncryptedCredentials() guard before generating
STORAGE_ENCRYPTION_KEY, matching the existing guard in bootstrap-env.mjs
- bootstrap-env.mjs: add decrypt-probe diagnostic on startup to detect
key mismatch and log actionable recovery instructions
- bin/omniroute.mjs: add 'reset-encrypted-columns' CLI recovery command
that nulls encrypted credential columns while preserving provider config
- tests/unit/sync-env.test.ts: isolate tests with DATA_DIR override
Root cause: postinstall → syncEnv() generated fresh crypto secrets into
the package-local .env on every 'npm install -g' upgrade, since the
package directory is wiped and recreated. The bootstrap-env guard never
triggered because sync-env already filled in the new keys. The DB still
contained credentials encrypted under the previous key, making them
permanently unrecoverable (AES-GCM auth-tag mismatch → silent 401s).