checkpoint: document undo-delete secret-preserving fix (2026-09-10)

This commit is contained in:
Avi 2026-09-10 12:50:24 -05:00
commit 8780ef3fbb

View file

@ -1,3 +1,97 @@
# Checkpoint — Undo-delete restores working profiles (2026-09-10)
## Where things are
- Project: `/home/avi/Projects/Keynctr`
- Branch: `master` @ **`d101b8e`** ("fix(undo): restore full profile with secret key on undo-delete").
- Working tree: **clean for tracked files.** Only untracked entries are the pre-existing
hygiene leftovers plus two Rust scratch files (all intentionally untracked — see
"Still untracked").
## What was completed (this session)
**Step 6 (undo history) — the hollow-undo bug is fixed, landed as `d101b8e`.**
Deleting a profile used to keep only a `ProfileSummary` on the undo stack, so
undo re-created the profile with `secret_key = ""` — a dead shell that could never
sign. The undo stack now keeps the full stored record:
- **`src/profiles.rs`** — new `DeletedProfile { summary, stored }` struct;
`delete_profile_record()` returns the real stored secret (plaintext or encrypted
blob, exactly as on disk) plus the safe UI summary; `delete_profile()` stays as
the summary-only wrapper for callers that keep no undo entry.
- **`src/app.rs`** — `App.undo_history` is now `Vec<DeletedProfile>` (secret material
never leaves the backend); `undo_delete()` restores the real `StoredProfile`,
refuses to create a hollow profile (empty secret → entry handed back + error),
and `state_view()` still exposes only `summary` items so the renderer never sees
a secret. New regression test
`undo_delete_restores_working_profile_without_leaking_secret` locks this in.
- **`src/ipc.rs`** — `DeleteProfile` routes through `delete_profile_record` so the
GUI undo entry carries the secret (previously it pushed a summary-only entry,
so GUI undo was still hollow).
- **`src/main.rs`** — CLI `delete-profile`/`undo-delete` use the same record path
(`delete_profile_direct` → `delete_profile_record`, `cli_undo_delete` →
`app.undo_delete()`); also fixes the old "label looked up after removal" bug by
capturing the label before deletion, and drops the now-unused imports.
- Compile fixes included: the inherited work-in-progress did not build (`DeletedProfile`
vs `ProfileSummary` mismatch in `ipc.rs`, partial moves in `undo_delete`); both
resolved, plus `cargo fmt` applied.
Security properties: secret material stays backend-only (`AppStateView.undo_history`
is still `Vec<ProfileSummary>`); undo restores the exact stored blob (no re-derivation,
no logging); empty-secret entries fail closed instead of writing hollow profiles.
## Commits added this session (newest first)
| Hash | Message |
|------|---------|
| `d101b8e` | fix(undo): restore full profile with secret key on undo-delete |
(Parent chain — `1d5940f` display/icons checkpoint, `d580139` icon alpha fix,
`0814a53` Linux display compat, `715c99c`/`510cb65` connection-secrets vault
integration — is unchanged.)
## Verification (run this session, on top of `d101b8e`)
- **Rust**: `cargo test` → **197 passed**, 0 failed (196 pre-existing + 1 new
undo regression test); `cargo clippy --all-targets` → clean (exit 0);
`cargo fmt --check` → clean (exit 0); `cargo build --release` → Finished, exit 0.
- **Frontend** (in `frontend/`, Rust-only change so no frontend files touched):
`npm test` → **116/116 passed**; `npm run typecheck` → exit 0;
`npm run lint` → exit 0; `npm run electron:build` → exit 0; `npm run build` →
exit 0. `npm run format:check` → warns on the same 5 pre-existing files
(`ExportSecretKeyModal.tsx`, `SignerModeScreen.tsx`, `AppProvider.tsx`,
`ExportSecretKey.test.tsx`, `fakeBackend.ts`) documented in earlier checkpoints —
not introduced here, left untouched.
## How to reproduce / exercise
- Backend: `cargo run --release -- serve` (JSON-lines IPC on stdio) or the CLI in
`src/main.rs`.
- GUI: from `frontend/`, `npm run electron:build && electron .` (prod) or `npm run
start:dev` with `NOSTR_GUI_DEV_URL`.
- Exercise undo: Profiles → delete a profile → Undo delete → the restored profile
signs/publishes (previously it came back secret-less). CLI equivalent:
`keynectr delete-profile <npub>` then `keynectr undo-delete`.
## Still untracked (do NOT lose; do NOT commit the hygiene junk)
- **Source JPEG** `KeynectrAppIconPossibility02.jpeg` — intentionally untracked.
- Pre-existing untracked hygiene leftovers: `COSMIC_THEME.md`, `.opencode/`,
`.impeccable/critique/`, `.directory`, `deferred/SignerConnectionPanel.tsx.wip/`.
- Rust scratch files (unreferenced, harmless — neither is wired into the build):
`src/signer/nip46_external.rs` (dead stub, not declared in `src/signer/mod.rs`),
`src/publish.rs.bak` (backup copy). Left alone this session; delete or wire up
in a later pass.
- Build artifacts `release/` and `dist/` are gitignored and not committed.
- `profiles_vault.json*` and `target/` remain correctly untracked and uncommitted.
## Deferred / next steps (unchanged, minus the undo item)
- Step 3 sub-step 2 (IPC reroute) remains the next signer milestone; external
(remote) signing in the publish path still returns "not yet supported".
- External-signer permissions (Step 4), deferred security (Step 5: KDF upgrade,
`--allow-env-secret`, gate deprecated `RevealSecretKey`), hygiene (Step 7:
Keynctr rename incl. `package.json` → `homepage`, legacy Python removal, vault
relocation, Prettier pass over the 5 known files).
- Open question carried forward: `migrate_vault_signer_modes` reports a change on
every load (always `changed = true`), so `App::load` re-saves each start.
---
# Checkpoint — NIP-46 Connection Secrets in the Vault (2026-09-04) # Checkpoint — NIP-46 Connection Secrets in the Vault (2026-09-04)
## Where things are ## Where things are