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.
This commit is contained in:
parent
114b34319e
commit
ee88171e45
1 changed files with 88 additions and 59 deletions
|
|
@ -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
|
## Where things are
|
||||||
- Project: `/home/avi/Projects/Keynctr`
|
- Project: `/home/avi/Projects/Keynctr`
|
||||||
- Branch: `master` @ **`6eff510`** ("feat: fail-closed ExportSecretKey with fresh auth and audit").
|
- Branch: `master` @ **`114b343`** ("fix(packaging): restore Linux app icon so dist builds ship an icon").
|
||||||
- Working tree: **not clean** — see "Still uncommitted" below. The three feature
|
- Working tree: **effectively clean for tracked files.** No tracked file is modified or
|
||||||
commits of this session are committed; only the packaging icon, the old
|
staged. The only untracked entries are the pre-existing hygiene leftovers and the
|
||||||
checkpoint, and pre-existing hygiene leftovers remain uncommitted.
|
source JPEG (all intentionally untracked — see "Still untracked"). Build artifacts
|
||||||
|
(`release/`, `dist/`) are gitignored.
|
||||||
|
|
||||||
## What was completed (this session)
|
## What was completed (this session)
|
||||||
The previously-uncommitted working tree (27 modified + 10 untracked files, ~1852/692)
|
The previously-uncommitted working tree (27 modified + 10 untracked files, ~1852/692)
|
||||||
was triaged into **three logical, independently-verifiable commits**. Order is
|
was triaged into **three logical, independently-verifiable commits** in a prior session,
|
||||||
`a → c → b`: `ExportSecretKey` (b) reads the per-profile `signer_mode` field and the
|
and this session **landed the Step-2 packaging fix** on top. Order is
|
||||||
`external_signer_not_connected` error that the signer-mode commit (c) introduces, so
|
`a → c → b → (packaging)`: `ExportSecretKey` (b) reads the per-profile `signer_mode`
|
||||||
(c) had to land first. The only uncommitted *tests* were the export tests and the
|
field and the `external_signer_not_connected` error that the signer-mode commit (c)
|
||||||
signer-mode/permission tests, so "(d) tests" is not a separate commit — each feature's
|
introduces, so (c) had to land first. The only uncommitted *tests* were the export
|
||||||
tests travel with it.
|
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
|
1. **Per-profile signer modes + persisted NIP-46 connections (c)** — three coexisting
|
||||||
signing modes (Embedded / Nip46Client / Nip46Bunker), a `signer_mode` field on every
|
signing modes (Embedded / Nip46Client / Nip46Bunker), a `signer_mode` field on every
|
||||||
|
|
@ -28,6 +30,17 @@ tests travel with it.
|
||||||
replaces the old reveal modal.
|
replaces the old reveal modal.
|
||||||
3. **Hash-chained audit log (a)** — SHA-256 hash-chained append-only log with atomic
|
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.
|
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
|
### Security properties confirmed
|
||||||
- No secret material is logged or returned except the single, authenticated, audited
|
- 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)
|
## Commits added this session (newest first)
|
||||||
| Hash | Message |
|
| Hash | Message |
|
||||||
|------|---------|
|
|------|---------|
|
||||||
| `6eff510` | feat: fail-closed ExportSecretKey with fresh auth and audit |
|
| `114b343` | fix(packaging): restore Linux app icon so dist builds ship an icon |
|
||||||
| `2c61830` | feat: add per-profile signer modes with persisted NIP-46 connections |
|
|
||||||
| `caed722` | feat: add hash-chained, append-only audit log |
|
|
||||||
|
|
||||||
Parent of this session: `4038e2d` ("checkpoint: document embedded signer and NIP-46
|
(The prior session's three feature commits and their checkpoint `d92371f` remain the
|
||||||
client signer modes").
|
parent chain: `6eff510` → `2c61830` → `caed722` → … → `d92371f` → `114b343`.)
|
||||||
|
|
||||||
### Files touched by the three commits (30 files, +3921 / -611)
|
## Verification (run this session)
|
||||||
```
|
Full suite per AGENTS.md, run on top of `114b343` (icon is a binary asset, no code
|
||||||
Cargo.lock Cargo.toml frontend/electron/main.ts frontend/package.json
|
change, so the suite is expected to hold):
|
||||||
frontend/package-lock.json frontend/src/components/ExportSecretKeyModal.tsx (new)
|
- **Rust**: `cargo test --release` → **186 passed**, 0 failed; `cargo clippy
|
||||||
frontend/src/components/ShowSecretKeyModal.tsx (deleted)
|
--all-targets` → clean (exit 0); `cargo fmt --check` → clean (exit 0); `cargo build
|
||||||
frontend/src/lib/api.ts frontend/src/lib/signer/SignerManager.ts (new)
|
--release` → Finished, exit 0.
|
||||||
frontend/src/lib/types.ts frontend/src/screens/ProfilesScreen.tsx
|
- **Frontend**: `npm test` → **116 passed** (16 files); `npm run typecheck` → clean
|
||||||
frontend/src/screens/SignerModeScreen.tsx frontend/src/state/AppProvider.tsx
|
(exit 0); `npm run lint` → clean (exit 0).
|
||||||
frontend/src/styles.css frontend/src/test/apiMock.ts
|
- `npm run format:check` → **exit 1** on 5 files (`src/components/ExportSecretKeyModal.tsx`,
|
||||||
frontend/src/test/ExportSecretKey.test.tsx (new) frontend/src/test/fakeBackend.ts
|
`src/screens/SignerModeScreen.tsx`, `src/state/AppProvider.tsx`,
|
||||||
frontend/src/test/ShowSecretKey.test.tsx (deleted)
|
`src/test/ExportSecretKey.test.tsx`, `src/test/fakeBackend.ts`). **Pre-existing** —
|
||||||
src/app.rs src/audit.rs (new) src/errors.rs src/ipc.rs src/lib.rs src/main.rs
|
those files are committed as-is at `6eff510` (prior session) and are clean in the
|
||||||
src/profiles.rs src/signer/mod.rs src/signer/nip46_client.rs
|
working tree; this icon-only change touched none of them. Left for the Step-7
|
||||||
src/signer/permissions.rs (new) src/signer/types.rs src/vault.rs
|
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
|
||||||
## Verification (run this session, per commit)
|
`linux.target` (AppImage + deb) was also built directly to prove the icon lands:
|
||||||
Each commit was verified **in isolation** (checked out on top of its parent), not just
|
- `release/Keynctr-0.1.0.AppImage` — 135,749,547 bytes.
|
||||||
as the final tree:
|
- `release/keynectr_0.1.0_amd64.deb` — 105,810,972 bytes.
|
||||||
- `caed722` (audit): `cargo test --release` → **124 passed** (119 prior + 5 audit).
|
- **Icon proven inside both artifacts**: the embedded icon is **byte-identical
|
||||||
- `2c61830` (signer modes): `cargo test --release` → **186 passed**; `cargo clippy
|
(md5 `b3e372f7`)** to the generated `build/icon.png` in all four locations checked —
|
||||||
--all-targets` clean; `cargo fmt --check` clean; frontend `tsc --noEmit` clean;
|
the deb's `usr/share/icons/hicolor/512x512/apps/keynectr.png`, the AppImage's hicolor
|
||||||
`npx vitest run` → **110 passed**.
|
png, the AppImage's `.DirIcon`, and `build/icon.png` itself — all 512x512 RGBA with
|
||||||
- `6eff510` (export): `cargo test --release` → **186 passed**; frontend `tsc --noEmit`
|
real transparency (32,626 opaque px, 226,582 transparent, corners alpha 0), ink
|
||||||
clean; `npx vitest run` → **116 passed** (110 + 6 export tests).
|
pure black. The deb `.desktop` reads `Icon=keynectr`, matching the hicolor name.
|
||||||
|
|
||||||
## How to reproduce / exercise
|
## How to reproduce / exercise
|
||||||
- Backend: `cargo run --release -- serve` (JSON-lines IPC on stdio) or the CLI in
|
- Backend: `cargo run --release -- serve` (JSON-lines IPC on stdio) or the CLI in
|
||||||
`src/main.rs`.
|
`src/main.rs`.
|
||||||
- GUI: from `frontend/`, `npm run electron:build && electron .` (prod) or `npm run
|
- GUI: from `frontend/`, `npm run electron:build && electron .` (prod) or `npm run
|
||||||
start:dev` with `NOSTR_GUI_DEV_URL`.
|
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
|
- 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.
|
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
|
- Exercise modes: Signer Mode screen → pick Embedded / NIP-46 client; connect a
|
||||||
`nostrconnect://` URI with a `perms=` parameter to grant scoped permissions.
|
`nostrconnect://` URI with a `perms=` parameter to grant scoped permissions.
|
||||||
|
|
||||||
## Still uncommitted (do NOT lose; do NOT commit the hygiene junk)
|
## Still untracked (do NOT lose; do NOT commit the hygiene junk)
|
||||||
- **`frontend/build/icon.png` is deleted** (working tree) — the electron-builder Linux
|
- **Source JPEG** `KeynectrAppIconPossibility02.jpeg` — intentionally **untracked**
|
||||||
icon. This is the Step-2 packaging fix: regenerate it from
|
(the icon is now derived from it; the JPEG itself is not a build input and stays out
|
||||||
`KeynectrAppIconPossibility02.jpeg` (or point electron-builder at the new asset),
|
of git, per instruction).
|
||||||
verify `npm run dist`, then commit. **Not done in this session.**
|
- Pre-existing untracked hygiene leftovers (out of scope, address in the Step-7 hygiene
|
||||||
- **`CHECKPOINT-encryption.md`** — this file, committed to reference the post-triage
|
pass): `COSMIC_THEME.md`, `.opencode/`, `.impeccable/`, `.directory`.
|
||||||
HEAD (`6eff510`). It must be re-updated to reference the new HEAD once Step 2
|
- **`frontend/build/icon.png` is no longer a leftover** — it was regenerated and
|
||||||
(packaging) lands its own commit.
|
committed in `114b343` this session. The prior "deleted icon" item is closed.
|
||||||
- Pre-existing untracked hygiene leftovers (out of scope, address in the hygiene pass):
|
- Build artifacts `release/` and `dist/` are gitignored and not committed.
|
||||||
`COSMIC_THEME.md`, `KeynectrAppIconPossibility02.jpeg`, `.opencode/`, `.impeccable/`,
|
- `profiles_vault.json*` and `target/` remain correctly untracked and uncommitted
|
||||||
`.directory`. **`profiles_vault.json*` and `target/` remain correctly untracked and
|
(vault is gitignored).
|
||||||
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)
|
## Deferred / next steps (unchanged, plus new)
|
||||||
- **Step 2 (packaging)**: restore `frontend/build/icon.png`, verify `npm run dist`.
|
- **Step 2 (packaging): DONE.** Icon restored, `npm run dist` + declared targets green,
|
||||||
- Signer abstraction (Step 3): promote `src/signer` to the single `Signer` source of
|
icon proven inside both artifacts, committed as `114b343`.
|
||||||
truth; introduce `SigningBackend { Internal{..}, Remote{..} }` per profile and route
|
- **Step 3 (signer abstraction)** — proposed for sign-off, NOT yet implemented. Current
|
||||||
every IPC handler through the trait (no inline mode branching).
|
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
|
- 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
|
approval modal so grants (kinds, relays, expiry, rate) are persisted AND enforced in
|
||||||
the UI, not just parsed.
|
the UI, not just parsed.
|
||||||
|
|
@ -113,8 +140,10 @@ as the final tree:
|
||||||
+ backward-compatible migration; `--allow-env-secret` flag; remove or gate deprecated
|
+ backward-compatible migration; `--allow-env-secret` flag; remove or gate deprecated
|
||||||
`RevealSecretKey` IPC behind the same fail-closed path.
|
`RevealSecretKey` IPC behind the same fail-closed path.
|
||||||
- Undo history (Step 6): resolve the `ProfileSummary`-loses-the-secret question.
|
- Undo history (Step 6): resolve the `ProfileSummary`-loses-the-secret question.
|
||||||
- Hygiene (Step 7): Keynctr rename pass, delete legacy Python, migrate root
|
- Hygiene (Step 7): Keynctr rename pass (includes the `homepage` → correct repo fix noted
|
||||||
`profiles_vault.json*` into `~/.local/share/keynectr`.
|
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
|
- 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.
|
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.
|
Harmless (idempotent) but wasteful; tighten to only report real changes.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue