Commit graph

13 commits

Author SHA1 Message Date
Avi
41d4a60336 chore(deps): lockfile refresh from update_apply (yoke-derive 0.8.4, npm tree refresh) 2026-10-01 20:07:35 -05:00
Avi
15fa331f46 chore(deps): apply dependency updates from the in-app updater run
Applying npm audit fix + npm update + cargo update via the (fixed) updater: clears the high-severity js-yaml advisory (maxTotalMergeKeys CPU use on empty merge sources). Full suites verified green after the bump: 219 Rust unit + 6 e2e, 135 frontend, vite build clean.
2026-09-28 08:30:48 -05:00
Avi
38499d4506 feat(signer): QR pairing — client-initiated nostrconnect:// flow for Amber
Keynctr is the NIP-46 client; Amber is the scanner. Amber hands out no
link — it scans one — so the signer screen now mints a pairing token:

- start_pairing(): ephemeral key + secret, nostrconnect:// token via
  NostrConnectUri::client_with_secret, status().pairing_uri for the GUI
- run_pairing_task(): listens for the signer's connect request, echoes
  the secret (anti-spoofing), persists the connection row + secret,
  then adopts identity via get_public_key and hands to the demux loop
- pairing subscription is closed at handoff so the demux loop owns the
  conversation (relay could otherwise deliver signer replies under the
  stale pairing sub id where nobody routes them)
- IPC: nip46_pair_start; status carries pairing_uri
- SignerModeScreen: 'Show QR' button, QR render (qrcode) of the token,
  copy-link fallback, cancel; paste-link flow unchanged
- e2e: fake QR scanner consumes the real pairing token end-to-end
  (scan -> secret echo -> identity -> sign -> vault persistence)
2026-09-12 04:47:20 -05:00
Avi
85756df081 feat(signer): accept bunker:// URIs, async signer permissions, NIP-46 e2e test
- SignerManager/SignerModeScreen parse both nostrconnect:// and bunker://
  (Amber presents bunker://; signer pubkey extracted before '@')
- Signer permission surface made async (permissions, can_*, is_connection_valid)
- tests/nip46_e2e.rs: full client handshake against fake Amber over a local
  relay — NIP-44 round-trip, get_public_key identity, signed-event verification,
  vault persistence asserting no secret material for remote profiles
- prettier formatting of touched frontend files
2026-09-12 02:40:40 -05:00
Avi
caed722b06 feat: add hash-chained, append-only audit log
Introduce src/audit.rs: a SHA-256 hash-chained audit log for
security-sensitive operations (key export, NIP-46 connect/revoke,
signing approvals, vault lock/unlock, permission denials).

- AuditEntry carries timestamp, profile npub, action, reason,
  success flag, error, and prev/this hash forming a tamper-evident
  chain from a genesis hash.
- record() holds a single mutex across the entire read-compute-write-
  update cycle so concurrent writers cannot interleave and silently
  overwrite entries; appends are atomic (write-all + fsync + rename).
- verify_chain() re-hashes every entry end to end.
- Log lives in the app data dir with 0600 permissions.

Also adds the sha2 dependency. Verified in isolation on top of HEAD:
cargo test --release -> 124 passed (119 prior + 5 audit).
2026-09-03 09:21:53 -05:00
Avi
b484bdeb08 feat: add embedded signer and NIP-46 client signer modes
- Add Signer trait with common interface for both signing modes
- Implement EmbeddedSigner: keys stored in encrypted vault (Argon2id + AES-256-GCM)
- Implement Nip46ClientSigner: connects to remote signer via nostrconnect:// URI
- Support both local and remote NIP-46 signers
- Add signer mode selection UI (SignerModeScreen)
- Add IPC endpoints for signer mode management, embedded signer, and NIP-46 client
- Update frontend types, API, and AppProvider
- All tests pass (119 Rust + 110 frontend)
2026-09-01 20:16:14 -05:00
Avi
fa5ba08578 Security: upgrade nostr stack 0.40->0.45 clearing 11 RustSec advisories
- nostr 0.40.0 -> 0.45.3, nostr-sdk 0.40.0 -> 0.45.2 (secp256k1 0.30)
- fixes RUSTSEC-2026-0216/0219/0224/0225/0226/0227/0228/0229/0230/0231/0232
  incl. forged-event signature-bypass and NIP-46 credential Debug leak
- NIP-42 auth now explicit: SignerAuthenticator on every client path
- EventBuilder::sign -> finalize_async; nip44 encrypt supplies random nonce
- feed/signer receive loops moved to stream_events (receiver opens before
  the signer announces, preserving the ordering fix)
- relay-pool replaced by in-SDK relay/gossip; try_connect().timeout()
- tests: malformed NIP-44 payload rejection + secret-leak regression
- suite: 115 backend tests green (113 preserved + 2 new); frontend untouched
2026-08-25 14:23:26 -05:00
Avi
c11ab9f735 Security: major dependency upgrades clearing all npm advisories; show per-advisory fix paths 2026-08-24 11:00:18 -05:00
Avi
7c6a085bf1 Rename the app to Keynectr
- Crate/binary: nostr-manager-backend -> keynectr
- Data directory: nost-feed-manager -> keynectr, migrated automatically
  on first data_dir() call (existing vaults, settings and backups move)
- Electron extraResources/spawn path, executableName, productName,
  window title and CLI usage strings updated to match
- Deliberately unchanged: crypto.rs KDF verifier string, so previously
  encrypted vault backups remain decryptable

Verified live: existing vault with two profiles migrated to
~/.local/share/keynectr and loads correctly.
2026-08-23 10:22:25 -05:00
Avi
130d7e29bc Zeroize transient secret key material in memory 2026-08-21 15:29:44 -05:00
Avi
73cb08d856 Add NIP-46 remote signer (external signing)
Add a remote-signer (bunker) role so other Nostr apps can delegate
signing to this app's active profile keys via nostrconnect:// links.

Backend: new src/signer.rs implementing the NIP-46 protocol (kind 24133
events encrypted with NIP-44 v2 conversation keys). It parses
nostrconnect:// connect URIs, spawns an async task in the serve process
that reads relay requests, auto-approves once the handshake completes,
signs delegate events, and publishes responses. Exposes status, connect,
and disconnect via IPC and CLI (signer status / signer connect).

Other: ipc.rs serve/handle now share Arc<Mutex<App>>; main.rs adds the
signer CLI commands; Cargo.toml enables nostr nip46 feature.

Frontend: new Signer screen (nav item + sidebar entry with key icon)
to paste a nostrconnect:// link, connect/disconnect, and show the
connected peer and relays; wires signer_connect/_disconnect/_status
through api.ts and AppProvider; adds tests and test mocks.
2026-08-05 19:28:26 -05:00
Avi
7ca1d14dcb 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
2026-08-03 19:00:41 -05:00
Avi
7e3bac345c Add Nostr Feed Manager: Rust backend with Electron + React GUI
- Rust library (nostr-manager-backend) with CLI and JSON-lines IPC serve mode:
  profiles, publishing with per-relay reports, relays, settings, vault storage
  and legacy-vault migration
- Electron + React + TypeScript desktop GUI using the same backend over stdio IPC
- Vitest suite with a fake backend speaking the real protocol
- electron-builder linux packaging; README with build and usage instructions
2026-08-03 16:05:59 -05:00