From ee88171e45dd3305992810d80ff89c153832dad1 Mon Sep 17 00:00:00 2001 From: Avi Date: Thu, 3 Sep 2026 13:14:44 -0500 Subject: [PATCH] checkpoint: document packaging icon fix (post-Step 2) HEAD moved to 114b343 (icon restored); the previously-deleted frontend/build/icon.png is now a committed asset, no longer a leftover. Records the verified npm run dist + AppImage/deb icon proof, the pre-existing format:check failure (5 files, Step-7), and notes the homepage rename is on the record for Step 7 (not fixed). Step 3 signer API is proposed and awaiting sign-off, not implemented. --- CHECKPOINT-encryption.md | 147 +++++++++++++++++++++++---------------- 1 file changed, 88 insertions(+), 59 deletions(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 19294a0..b509a8c 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,20 +1,22 @@ -# Checkpoint — Signer Modes + Fail-Closed Key Export (2026-09-03) +# Checkpoint — Signer Modes + Fail-Closed Key Export + Packaging Icon (2026-09-03) ## Where things are - Project: `/home/avi/Projects/Keynctr` -- Branch: `master` @ **`6eff510`** ("feat: fail-closed ExportSecretKey with fresh auth and audit"). -- Working tree: **not clean** — see "Still uncommitted" below. The three feature - commits of this session are committed; only the packaging icon, the old - checkpoint, and pre-existing hygiene leftovers remain uncommitted. +- Branch: `master` @ **`114b343`** ("fix(packaging): restore Linux app icon so dist builds ship an icon"). +- Working tree: **effectively clean for tracked files.** No tracked file is modified or + staged. The only untracked entries are the pre-existing hygiene leftovers and the + source JPEG (all intentionally untracked — see "Still untracked"). Build artifacts + (`release/`, `dist/`) are gitignored. ## What was completed (this session) The previously-uncommitted working tree (27 modified + 10 untracked files, ~1852/692) -was triaged into **three logical, independently-verifiable commits**. Order is -`a → c → b`: `ExportSecretKey` (b) reads the per-profile `signer_mode` field and the -`external_signer_not_connected` error that the signer-mode commit (c) introduces, so -(c) had to land first. The only uncommitted *tests* were the export tests and the -signer-mode/permission tests, so "(d) tests" is not a separate commit — each feature's -tests travel with it. +was triaged into **three logical, independently-verifiable commits** in a prior session, +and this session **landed the Step-2 packaging fix** on top. Order is +`a → c → b → (packaging)`: `ExportSecretKey` (b) reads the per-profile `signer_mode` +field and the `external_signer_not_connected` error that the signer-mode commit (c) +introduces, so (c) had to land first. The only uncommitted *tests* were the export +tests and the signer-mode/permission tests, so "(d) tests" is not a separate commit — +each feature's tests travel with it. 1. **Per-profile signer modes + persisted NIP-46 connections (c)** — three coexisting signing modes (Embedded / Nip46Client / Nip46Bunker), a `signer_mode` field on every @@ -28,6 +30,17 @@ tests travel with it. replaces the old reveal modal. 3. **Hash-chained audit log (a)** — SHA-256 hash-chained append-only log with atomic append and end-to-end `verify_chain()`; the `sha2` dependency. +4. **Packaging icon restored (this session, `114b343`)** — `frontend/build/icon.png` + was deleted from the working tree, so electron-builder had no Linux icon and every + AppImage/deb it emitted carried the generic Electron placeholder. The icon was + **regenerated from `KeynectrAppIconPossibility02.jpeg`** (not resized): the white + (254) JPEG background was cut to transparent (alpha derived from luma), the art was + flattened to a square canvas with symmetric padding, ink kept pure black (RGB 0,0,0), + and exported as **512x512 RGBA**. The single declared config path + (`linux.icon: build/icon.png`) was kept single — no `build/linux/` fan-out — because + the 512 master satisfies both targets: AppImage downscales to 256 internally (≥256 + required) and the deb installs `usr/share/icons/hicolor/512x512/apps/keynectr.png`, + matching the generated `.desktop` `Icon=keynectr`. ### Security properties confirmed - No secret material is logged or returned except the single, authenticated, audited @@ -45,67 +58,81 @@ tests travel with it. ## Commits added this session (newest first) | Hash | Message | |------|---------| -| `6eff510` | feat: fail-closed ExportSecretKey with fresh auth and audit | -| `2c61830` | feat: add per-profile signer modes with persisted NIP-46 connections | -| `caed722` | feat: add hash-chained, append-only audit log | +| `114b343` | fix(packaging): restore Linux app icon so dist builds ship an icon | -Parent of this session: `4038e2d` ("checkpoint: document embedded signer and NIP-46 -client signer modes"). +(The prior session's three feature commits and their checkpoint `d92371f` remain the +parent chain: `6eff510` → `2c61830` → `caed722` → … → `d92371f` → `114b343`.) -### Files touched by the three commits (30 files, +3921 / -611) -``` -Cargo.lock Cargo.toml frontend/electron/main.ts frontend/package.json -frontend/package-lock.json frontend/src/components/ExportSecretKeyModal.tsx (new) -frontend/src/components/ShowSecretKeyModal.tsx (deleted) -frontend/src/lib/api.ts frontend/src/lib/signer/SignerManager.ts (new) -frontend/src/lib/types.ts frontend/src/screens/ProfilesScreen.tsx -frontend/src/screens/SignerModeScreen.tsx frontend/src/state/AppProvider.tsx -frontend/src/styles.css frontend/src/test/apiMock.ts -frontend/src/test/ExportSecretKey.test.tsx (new) frontend/src/test/fakeBackend.ts -frontend/src/test/ShowSecretKey.test.tsx (deleted) -src/app.rs src/audit.rs (new) src/errors.rs src/ipc.rs src/lib.rs src/main.rs -src/profiles.rs src/signer/mod.rs src/signer/nip46_client.rs -src/signer/permissions.rs (new) src/signer/types.rs src/vault.rs -``` - -## Verification (run this session, per commit) -Each commit was verified **in isolation** (checked out on top of its parent), not just -as the final tree: -- `caed722` (audit): `cargo test --release` → **124 passed** (119 prior + 5 audit). -- `2c61830` (signer modes): `cargo test --release` → **186 passed**; `cargo clippy - --all-targets` clean; `cargo fmt --check` clean; frontend `tsc --noEmit` clean; - `npx vitest run` → **110 passed**. -- `6eff510` (export): `cargo test --release` → **186 passed**; frontend `tsc --noEmit` - clean; `npx vitest run` → **116 passed** (110 + 6 export tests). +## Verification (run this session) +Full suite per AGENTS.md, run on top of `114b343` (icon is a binary asset, no code +change, so the suite is expected to hold): +- **Rust**: `cargo test --release` → **186 passed**, 0 failed; `cargo clippy + --all-targets` → clean (exit 0); `cargo fmt --check` → clean (exit 0); `cargo build + --release` → Finished, exit 0. +- **Frontend**: `npm test` → **116 passed** (16 files); `npm run typecheck` → clean + (exit 0); `npm run lint` → clean (exit 0). + - `npm run format:check` → **exit 1** on 5 files (`src/components/ExportSecretKeyModal.tsx`, + `src/screens/SignerModeScreen.tsx`, `src/state/AppProvider.tsx`, + `src/test/ExportSecretKey.test.tsx`, `src/test/fakeBackend.ts`). **Pre-existing** — + those files are committed as-is at `6eff510` (prior session) and are clean in the + working tree; this icon-only change touched none of them. Left for the Step-7 + hygiene/format pass; not silently auto-fixed here. +- **Packaging (the point of this fix)**: `npm run dist` ran end-to-end. Note the script + is `electron-builder --linux dir`, which builds only the unpacked dir; the declared + `linux.target` (AppImage + deb) was also built directly to prove the icon lands: + - `release/Keynctr-0.1.0.AppImage` — 135,749,547 bytes. + - `release/keynectr_0.1.0_amd64.deb` — 105,810,972 bytes. + - **Icon proven inside both artifacts**: the embedded icon is **byte-identical + (md5 `b3e372f7`)** to the generated `build/icon.png` in all four locations checked — + the deb's `usr/share/icons/hicolor/512x512/apps/keynectr.png`, the AppImage's hicolor + png, the AppImage's `.DirIcon`, and `build/icon.png` itself — all 512x512 RGBA with + real transparency (32,626 opaque px, 226,582 transparent, corners alpha 0), ink + pure black. The deb `.desktop` reads `Icon=keynectr`, matching the hicolor name. ## 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`. +- Packaging: from `frontend/`, `npm run dist` (unpacked dir) or `npx electron-builder + --linux AppImage deb` (declared targets) → artifacts in `release/`. - Exercise export: Profiles → a profile → Export secret key → enter the vault password and a reason. An external (NIP-46 client) profile shows it cannot export. - Exercise modes: Signer Mode screen → pick Embedded / NIP-46 client; connect a `nostrconnect://` URI with a `perms=` parameter to grant scoped permissions. -## Still uncommitted (do NOT lose; do NOT commit the hygiene junk) -- **`frontend/build/icon.png` is deleted** (working tree) — the electron-builder Linux - icon. This is the Step-2 packaging fix: regenerate it from - `KeynectrAppIconPossibility02.jpeg` (or point electron-builder at the new asset), - verify `npm run dist`, then commit. **Not done in this session.** -- **`CHECKPOINT-encryption.md`** — this file, committed to reference the post-triage - HEAD (`6eff510`). It must be re-updated to reference the new HEAD once Step 2 - (packaging) lands its own commit. -- Pre-existing untracked hygiene leftovers (out of scope, address in the hygiene pass): - `COSMIC_THEME.md`, `KeynectrAppIconPossibility02.jpeg`, `.opencode/`, `.impeccable/`, - `.directory`. **`profiles_vault.json*` and `target/` remain correctly untracked and - uncommitted** (vault is gitignored). +## Still untracked (do NOT lose; do NOT commit the hygiene junk) +- **Source JPEG** `KeynectrAppIconPossibility02.jpeg` — intentionally **untracked** + (the icon is now derived from it; the JPEG itself is not a build input and stays out + of git, per instruction). +- Pre-existing untracked hygiene leftovers (out of scope, address in the Step-7 hygiene + pass): `COSMIC_THEME.md`, `.opencode/`, `.impeccable/`, `.directory`. +- **`frontend/build/icon.png` is no longer a leftover** — it was regenerated and + committed in `114b343` this session. The prior "deleted icon" item is closed. +- Build artifacts `release/` and `dist/` are gitignored and not committed. +- `profiles_vault.json*` and `target/` remain correctly untracked and uncommitted + (vault is gitignored). + +## On the record (not fixed, per instruction) +- **`package.json` → `homepage` still reads `https://github.com/avi/Keynctr`.** This is + the Step-7 rename/hygiene item and was **left untouched** in this session; noted here + so it is on the record rather than silently fixed. ## Deferred / next steps (unchanged, plus new) -- **Step 2 (packaging)**: restore `frontend/build/icon.png`, verify `npm run dist`. -- Signer abstraction (Step 3): promote `src/signer` to the single `Signer` source of - truth; introduce `SigningBackend { Internal{..}, Remote{..} }` per profile and route - every IPC handler through the trait (no inline mode branching). +- **Step 2 (packaging): DONE.** Icon restored, `npm run dist` + declared targets green, + icon proven inside both artifacts, committed as `114b343`. +- **Step 3 (signer abstraction)** — proposed for sign-off, NOT yet implemented. Current + state that motivates the API: `src/signer/mod.rs` already defines a `Signer` trait and + a `Signing` enum (`Local(Keys)` / `External{signer, profile_pubkey}`); `SignerMode` + (`Embedded`/`Nip46Bunker`/`Nip46Client`) lives on `StoredProfile` (per-profile) *and* + is duplicated on `App` (app-level), and `App` holds three separate signer handles + (`embedded_signer`, `nip46_signer`, `nip46_bunker_signer`). The IPC dispatcher + (`src/ipc.rs`) branches on `signer_mode`/handle presence in ~12 sites (see the + grep list pasted to the user this session). The proposal promotes `src/signer` to the + single `Signer` source of truth, introduces a `SigningBackend { Internal{..}, + Remote{..} }` per profile, and routes every IPC handler through the trait so no inline + mode branching remains. **Awaiting the user's sign-off on the API before any + implementation.** - External-signer permissions (Step 4): wire `src/signer/permissions.rs` into the approval modal so grants (kinds, relays, expiry, rate) are persisted AND enforced in the UI, not just parsed. @@ -113,8 +140,10 @@ as the final tree: + backward-compatible migration; `--allow-env-secret` flag; remove or gate deprecated `RevealSecretKey` IPC behind the same fail-closed path. - Undo history (Step 6): resolve the `ProfileSummary`-loses-the-secret question. -- Hygiene (Step 7): Keynctr rename pass, delete legacy Python, migrate root - `profiles_vault.json*` into `~/.local/share/keynectr`. +- Hygiene (Step 7): Keynctr rename pass (includes the `homepage` → correct repo fix noted + above), delete legacy Python, migrate root `profiles_vault.json*` into + `~/.local/share/keynectr`, and a Prettier format pass to clear the 5 pre-existing + `format:check` failures. - Open question carried forward: `migrate_vault_signer_modes` currently reports a change on every load (always `changed = true`), so `App::load` re-saves the vault each start. Harmless (idempotent) but wasteful; tighten to only report real changes.