Add password-encrypted vault
- Encrypt stored secret keys with AES-256-GCM under an Argon2id-derived key; the vault stays plaintext until a password is set (Settings -> Storage or the CLI set-password command) - Only secret keys are encrypted; labels and npubs stay readable so profiles can be browsed while the vault is locked - Backend: crypto module, Vault.crypto metadata, unlock/lock/set/remove password on App, VaultLocked/WrongPassword errors, secret resolution on the publish path - IPC: set_vault_password, unlock_vault, lock_vault, remove_vault_password - CLI: set-password, remove-password, unlock; create/publish prompt when the vault is locked (NFM_PASSWORD env or hidden prompt, never argv) - GUI: unlock banner + modal on locked vaults, protect/change/remove password in Settings, password field styling - Tests: Rust (argon2/AES round-trips, vault lifecycle) and Vitest (unlock flow, set/change/remove password), all green
This commit is contained in:
parent
7e3bac345c
commit
7ca1d14dcb
23 changed files with 1540 additions and 43 deletions
28
README.md
28
README.md
|
|
@ -14,8 +14,9 @@ runs in the Rust backend, which the GUI talks to over a JSON-lines IPC channel.
|
|||
- Add, remove, enable/disable, and test relays
|
||||
- Light / dark / system theme, configurable publish confirmation and key shortening
|
||||
- Back up your vault from the UI
|
||||
- Honest about security: the vault is stored in plaintext (as in the original CLI), readable
|
||||
only by your user account; this is clearly disclosed in the app
|
||||
- Password-protected vault: secret keys are encrypted at rest with AES-256-GCM under an
|
||||
Argon2id-derived key. Without a password set, the vault is stored in plaintext (readable only by
|
||||
your user account) and this is disclosed in the app
|
||||
|
||||
## Architecture
|
||||
|
||||
|
|
@ -88,10 +89,17 @@ cargo run --release -- switch <npub> # select the active profile
|
|||
cargo run --release -- publish <npub> "Hello" # publish a text note
|
||||
cargo run --release -- relays list|add|remove|enable|disable|test
|
||||
cargo run --release -- settings get|set theme|confirm|shorten
|
||||
cargo run --release -- info # show storage locations
|
||||
cargo run --release -- set-password # encrypt the vault (or change its password)
|
||||
cargo run --release -- remove-password # remove vault encryption
|
||||
cargo run --release -- unlock # verify the vault password for this process
|
||||
cargo run --release -- info # show storage locations and version
|
||||
cargo run --release -- serve # JSON-lines IPC server (used by the GUI)
|
||||
```
|
||||
|
||||
Passwords are read from the `NFM_PASSWORD` environment variable when set, otherwise you are
|
||||
prompted interactively. They are never accepted as command-line arguments. `create` and `publish`
|
||||
prompt for the vault password automatically when the vault is encrypted.
|
||||
|
||||
## Storage and migration
|
||||
|
||||
- The vault (`profiles_vault.json`) and settings live in
|
||||
|
|
@ -100,8 +108,13 @@ cargo run --release -- serve # JSON-lines IPC server (used b
|
|||
- The original CLI saved `profiles_vault.json` in its working directory. On first launch this
|
||||
app finds that file, copies it to a timestamped `*.backup-<ts>` next to it, and imports your
|
||||
profiles into the new location. The original file is left untouched.
|
||||
- Private keys are stored in the vault in plaintext. Anyone with access to your user account
|
||||
can read them; a password-encrypted vault is planned for a future version.
|
||||
- The vault is stored in plaintext until you set a password (Settings → Storage, or
|
||||
`set-password` in the CLI). Once protected, every secret key is encrypted at rest with
|
||||
AES-256-GCM under a key derived from your password with Argon2id. Labels and public keys stay
|
||||
readable so profiles can be browsed while the vault is locked. You unlock once per session;
|
||||
the derived key lives only in memory and is never written to disk. Anyone with access to your
|
||||
user account can still read the vault file, so the password is a defence-in-depth layer, not a
|
||||
replacement for keeping your account secure.
|
||||
|
||||
## Development
|
||||
|
||||
|
|
@ -122,7 +135,8 @@ cargo clippy --all-targets
|
|||
|
||||
```
|
||||
src/ Rust library + CLI + IPC server
|
||||
app.rs application state loading/persistence
|
||||
app.rs application state, vault password/unlock lifecycle
|
||||
crypto.rs Argon2id key derivation + AES-256-GCM encryption
|
||||
errors.rs structured AppError
|
||||
ipc.rs JSON-lines serve() loop and request/reply envelope
|
||||
main.rs CLI entry point
|
||||
|
|
@ -130,7 +144,7 @@ src/ Rust library + CLI + IPC server
|
|||
publish.rs note publishing with per-relay reports
|
||||
relays.rs default relays, validation, connection tests
|
||||
settings.rs theme and user preferences
|
||||
vault.rs encrypted-vault-ready storage (currently plaintext)
|
||||
vault.rs vault storage (plaintext or password-encrypted) and migration
|
||||
frontend/
|
||||
electron/ Electron main + preload (backend spawn, IPC, clipboard)
|
||||
src/ React app (components, screens, state, styles)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue