Add reveal-secret-key after unlock (CLI + GUI)
- profiles::reveal_secret_key returns a key in hex and nsec1... forms, gated on an unlocked vault (VaultLocked when encrypted + locked) - IPC: reveal_secret_key method; error replies now carry a machine-readable code field (ErrorKind as snake_case, e.g. vault_locked) - CLI: show-secret <npub> prompts for the vault password when locked - GUI: 'Secret key' button per profile card opens a modal showing hex + nsec with copy buttons; locked vaults ask for the password inline before revealing - Tests: Rust (reveal plaintext/encrypted/locked) and Vitest (reveal flow, lock-then-unlock), all green
This commit is contained in:
parent
ec2eb6c092
commit
8eb6685281
17 changed files with 594 additions and 54 deletions
|
|
@ -1,4 +1,4 @@
|
|||
# Checkpoint — Password-encrypted vault (2026-08-03)
|
||||
# Checkpoint — Secret-key reveal after unlock (2026-08-04)
|
||||
|
||||
A stopping point you can return to if this session is closed. Everything below was
|
||||
verified green at the moment this file was written.
|
||||
|
|
@ -6,73 +6,87 @@ verified green at the moment this file was written.
|
|||
## Where things are
|
||||
|
||||
- Project: `/home/avi/Projects/skills/nost-feed-manager`
|
||||
- Git repo: `master` @ `7e3bac3` ("Add Nostr Feed Manager: Rust backend with Electron + React GUI")
|
||||
- The encryption work is **uncommitted** — all changes are in the working tree.
|
||||
- Also relevant: `/home/avi/Projects/nostr_backend/nostr_backendmanager.md` (old-CLI docs, untouched),
|
||||
and `/home/avi/Projects/nostr_backend/vlog-website/` (separate, untouched).
|
||||
- Git repo: `master`
|
||||
- The reveal feature is **uncommitted** — all changes are in the working tree.
|
||||
- `/home/avi/Projects/nostr_backend/nostr_backendmanager.md` (old-CLI docs) has been updated
|
||||
to match reality; it lives outside this repo so it is not part of the commit.
|
||||
- `/home/avi/Projects/nostr_backend/vlog-website/` (separate, untouched).
|
||||
|
||||
## What was completed: password-encrypted vault
|
||||
## What was completed in this session: reveal a secret key after unlock
|
||||
|
||||
- Secret keys are now encrypted at rest with **AES-256-GCM** under a key derived via **Argon2id**
|
||||
from the user's password. Vaults stay plaintext until a password is set (opt-in).
|
||||
- Encryption only covers the secret keys; labels/npubs stay readable so profiles can be browsed
|
||||
while the vault is locked. The derived key lives only in memory for the session.
|
||||
Building on the existing password-encrypted vault (AES-256-GCM + Argon2id), owners can now view a
|
||||
profile's secret key after entering the vault password:
|
||||
|
||||
## Files changed (19 modified, 4 new)
|
||||
- Backend: `profiles::reveal_secret_key` returns the key in both hex and `nsec1...` forms, gated on
|
||||
an unlocked vault (`VaultLocked` when encrypted + locked). New CLI command `show-secret <npub>`,
|
||||
which prompts for the password when locked (via `NFM_PASSWORD` env or hidden prompt).
|
||||
- IPC: new `reveal_secret_key { npub }` method. Error replies now carry a machine-readable `code`
|
||||
field (ErrorKind serialised as snake_case, e.g. `vault_locked`), so the GUI can branch without
|
||||
string-matching on user-facing messages.
|
||||
- GUI: a "Secret key" button on every profile card opens `ShowSecretKeyModal`, which shows hex +
|
||||
nsec with copy buttons and a warning. When the vault is locked the modal asks for the password
|
||||
inline, unlocks, then reveals.
|
||||
- Key is only ever fetched after unlock; never stored in state before reveal.
|
||||
|
||||
## Files changed (16 modified, 2 new)
|
||||
|
||||
Modified:
|
||||
- `Cargo.toml`, `Cargo.lock` — added `argon2`, `aes-gcm`, `base64`, `getrandom`, `rpassword`
|
||||
- `README.md` — documented the new feature + CLI commands
|
||||
- `src/lib.rs`, `src/app.rs`, `src/vault.rs`, `src/profiles.rs`, `src/publish.rs`,
|
||||
`src/ipc.rs`, `src/main.rs`, `src/errors.rs`
|
||||
- `frontend/src/App.tsx`, `frontend/src/lib/api.ts`, `frontend/src/lib/types.ts`,
|
||||
`frontend/src/state/AppProvider.tsx`, `frontend/src/screens/SettingsScreen.tsx`,
|
||||
`frontend/src/styles.css`, `frontend/src/test/apiMock.ts`, `frontend/src/test/fakeBackend.ts`
|
||||
- `README.md` — documented the reveal feature + `show-secret`
|
||||
- `src/errors.rs` — `ErrorKind` now serialises as snake_case for the IPC error code
|
||||
- `src/profiles.rs` — `RevealedKey`, `reveal_secret_key`, `profile_label` + tests
|
||||
- `src/ipc.rs` — `RevealSecretKey` request, `code` on error replies
|
||||
- `src/main.rs` — `show-secret` CLI command
|
||||
- `frontend/src/components/Icon.tsx` — new `key` icon
|
||||
- `frontend/src/lib/api.ts` — `revealSecretKey`, `BackendError.code`
|
||||
- `frontend/src/lib/types.ts` — `RevealedKey`, error `code` in `BackendResponse`
|
||||
- `frontend/src/screens/ProfilesScreen.tsx` — "Secret key" button per profile
|
||||
- `frontend/src/state/AppProvider.tsx` — `revealSecretKey` in context
|
||||
- `frontend/src/test/{App,ProfilesScreen,apiMock,fakeBackend}` — updated for new UI + error codes
|
||||
|
||||
New:
|
||||
- `src/crypto.rs` — Argon2id KDF + AES-256-GCM encrypt/decrypt + password verifier
|
||||
- `frontend/src/components/UnlockModal.tsx`
|
||||
- `frontend/src/components/VaultPasswordModal.tsx`
|
||||
- `frontend/src/test/VaultPassword.test.tsx`
|
||||
- `frontend/src/components/ShowSecretKeyModal.tsx`
|
||||
- `frontend/src/test/ShowSecretKey.test.tsx`
|
||||
|
||||
Also updated (outside repo): `/home/avi/Projects/nostr_backend/nostr_backendmanager.md`.
|
||||
|
||||
## New backend API (IPC + CLI)
|
||||
|
||||
IPC methods: `set_vault_password { current_password?, new_password }`,
|
||||
`unlock_vault { password }`, `lock_vault`, `remove_vault_password { password }`.
|
||||
IPC: `reveal_secret_key { npub }` → `{ hex, nsec }`. Error replies now include
|
||||
`"code": "vault_locked"` (etc.) alongside `message`/`details`.
|
||||
|
||||
CLI: `set-password`, `remove-password`, `unlock`; `create`/`publish` auto-prompt when locked.
|
||||
Passwords come from `NFM_PASSWORD` env var or a hidden terminal prompt — never argv.
|
||||
Min password length: 8 chars.
|
||||
CLI: `show-secret <npub>` prints hex + nsec after unlocking. `create`, `publish`, and
|
||||
`show-secret` all auto-prompt for the vault password when it is encrypted.
|
||||
|
||||
## How it was verified (all green)
|
||||
|
||||
```
|
||||
cargo test # 50 passed
|
||||
cargo test # 55 passed
|
||||
cargo clippy --all-targets # clean
|
||||
cargo fmt --check # clean
|
||||
cargo build --release # builds
|
||||
npm run typecheck # clean (frontend/)
|
||||
npm run lint # clean (pre-existing module warning only)
|
||||
npm run format:check # clean
|
||||
npm test # 52 passed (10 files)
|
||||
npm test # 56 passed (11 files)
|
||||
```
|
||||
|
||||
Plus a manual end-to-end CLI smoke test: create → set-password → vault file shows only base64
|
||||
ciphertext → wrong password rejected → correct password creates encrypted profile. Temp data
|
||||
was cleaned up (`/tmp/nfm-e2e` removed).
|
||||
Plus a manual IPC end-to-end smoke test: reveal on plaintext → OK; set-password → lock →
|
||||
reveal returns `code: "vault_locked"`; unlock → reveal returns the same hex. Temp data cleaned up.
|
||||
|
||||
## How to resume
|
||||
|
||||
1. Open the repo: `cd /home/avi/Projects/skills/nost-feed-manager`
|
||||
2. Inspect the diff: `git diff` (work is still uncommitted)
|
||||
3. To try it: `cargo build --release` then
|
||||
`XDG_DATA_HOME=/tmp/nfm-smoke ./target/release/nostr-manager-backend create "Alice"`,
|
||||
`NFM_PASSWORD=... ./target/release/nostr-manager-backend set-password`
|
||||
3. To try it:
|
||||
- CLI: `cargo build --release`, then
|
||||
`XDG_DATA_HOME=/tmp/nfm-smoke ./target/release/nostr-manager-backend create "Alice"` and
|
||||
`./target/release/nostr-manager-backend show-secret <npub>`
|
||||
- GUI: `cd frontend && npm start`, Profiles → "Secret key" on a card
|
||||
4. Re-run verification with the commands above.
|
||||
|
||||
## Outstanding / next steps (if you continue)
|
||||
|
||||
- Decide whether to **commit** the work (nothing is committed yet).
|
||||
- `nostr_backendmanager.md` still lists "password-based vault encryption" as a future item and was
|
||||
left untouched — it may deserve updating to match reality.
|
||||
- No lock-screen gate: browsing works while locked; only create/publish require unlocking (intended).
|
||||
- Decide whether to **commit** the reveal work (nothing is committed yet).
|
||||
- Consider a small UI hint that profiles with an unencrypted vault can be revealed with no prompt.
|
||||
- Relay defaults are currently `relay.damus.io` (503 upstream) and `relay.nostr.band` (timeout);
|
||||
user's local settings already point at `nos.lol` + `relay.primal.net` instead.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue