- Define missing dict_factory (orphaned fragment left app unable to
return DB rows; every endpoint crashed on first query)
- Skip backup when PINVAULT_NAS_BACKUP_DIR is empty (os.makedirs('')
raised in bootstrap and killed gunicorn at import; compose default)
- Generate PINs with secrets.randbelow instead of random.randint
- Per-row random HMAC salts for PINs and recovery codes (single shared
hardcoded salt defeated the precomputation protection); constant-time
comparisons; legacy rows keep the historic salt
- Atomic backup restore (temp file + os.replace) to avoid torn DB
- Validate lock_days type, guard backup loop, move re import to module
level
- Replace weak unittest stubs with 34-test pytest suite (sqlcipher
shimmed with sqlite3 so tests run without the native lib)
- Add README, LICENSE, pyproject, .env.example; ruff clean
15 lines
525 B
Plaintext
15 lines
525 B
Plaintext
# bcrypt hash of the master password (required).
|
|
# Generate with:
|
|
# python3 -c "import bcrypt; print(bcrypt.hashpw(b'YOUR_PASSWORD', bcrypt.gensalt()).decode())"
|
|
PINVAULT_MASTER_HASH=
|
|
|
|
# Optional overrides (defaults shown):
|
|
#PINVAULT_DB=/data/pinvault.db
|
|
#PINVAULT_LOCAL_BACKUP_DIR=/data/backups
|
|
#PINVAULT_NAS_BACKUP_DIR=
|
|
#PINVAULT_BACKUP_INTERVAL=3600
|
|
#PINVAULT_MAX_BACKUPS=168
|
|
# If unset, a random API key is generated per process (printed nowhere;
|
|
# set this explicitly for stable automation access):
|
|
#PINVAULT_API_KEY=
|