Compare commits

...

2 commits

Author SHA1 Message Date
Avi
33ab4fe113 docs(checkpoint): Step 7 done, Forgejo push restored @ 7891ccc 2026-09-30 11:58:56 -05:00
Avi
7891ccce1a docs: Step 7 rename/hygiene pass
- package.json homepage: github.com/avi/Keynctr -> git.atitlan.io/avi/Keynctr
- README: title 'Nostr Feed Manager' -> 'Keynctr', resolve all
  [YOUR_FORGEJO_INSTANCE_URL]/<OWNER>/<REPO> placeholders with the real
  Forgejo URL, fix clone dir and packaged-binary name (nost-feed-manager
  -> keynectr), data dir default ~/.local/share/keynectr, refresh test
  counts (cargo 225+6 e2e, npm 139/19 files) and project layout
  (audit/bunker/feed/updates modules, signer/ dir, Feed+SignerMode screens)
- PRODUCT.md: data dir corrected with migration note
2026-09-30 11:57:49 -05:00
4 changed files with 72 additions and 25 deletions

View file

@ -69,12 +69,53 @@
- Live confirmation of the backfill rename: pair/keep a fresh Amber account - Live confirmation of the backfill rename: pair/keep a fresh Amber account
with no kind-0, publish kind-0 from Amber, confirm the Keynctr row renames with no kind-0, publish kind-0 from Amber, confirm the Keynctr row renames
itself within ~5 min without restarting the app. itself within ~5 min without restarting the app.
- Step 4 permissions UI follow-ups, Step 5 KDF, Step 7 rename/hygiene - Step 4 permissions UI follow-ups (interactive grant editing in the approval
(unchanged from previous checkpoint). modal) — the only buildable step left.
- `package.json` `homepage` still points at github.com/avi/Keynctr (Step 7). - Live pass: KDF migration on a real unlock + second-Amber account switching.
--- ---
# Checkpoint — Step 7 rename/hygiene + Forgejo push restored (2026-09-30)
## Where things are
- Project: `/home/avi/Projects/Keynctr`, branch `master` @ **`7891ccc`**
("docs: Step 7 rename/hygiene pass"). Previous: `c553dd5` (KDF upgrade
m=64MiB/t=3 + transparent migration, Sep 30), `5e91914` (launcher), theme
commits, identity backfill.
- **Forgejo push unblocked**: token supplied per-use via
`git -c http.extraHeader="Authorization: Bearer <token>" push origin master`
(token supplied in chat, NOT stored anywhere — revoke when convenient).
`a6182e3..c553dd5` pushed; remote master verified == local. Token is
single-use-per-invocation; re-ask for it next push unless the user stores one.
- Step 7 done in `7891ccc`: `frontend/package.json` homepage ->
`https://git.atitlan.io/avi/Keynctr`; README retitled Keynctr, all
`[YOUR_FORGEJO_INSTANCE_URL]`/`<OWNER>/<REPO>` placeholders resolved, clone
dir + packaged binary name fixed (`keynectr`), data-dir default
`~/.local/share/keynectr` (code auto-migrates the legacy dir), stale test
counts refreshed (cargo 225 unit + 6 e2e; npm 139 tests / 19 files), project
layout updated (audit/bunker/feed/updates, signer/ dir, Feed + SignerMode
screens). PRODUCT.md data-dir corrected.
- `tests/tmp_kdf_probe.rs` (throwaway probe) deleted; not part of the suite.
- Release binary rebuilt from c553dd5 (Sep 30 11:23).
## Verified
- `cargo test`: 225 unit + 6 e2e, 0 failed. `cargo clippy --all-targets`: 0.
`cargo fmt --check`: clean. Frontend: 139/19 vitest green, typecheck, lint,
format:check clean (docs-only commit; no code touched).
- Push verified by `git ls-remote` readback (remote master == c553dd5 at push).
## How to resume
- GUI: `cd ~/Projects/Keynctr/frontend && npx vite --port 5173` then
`NOSTR_GUI_DEV_URL=http://localhost:5173 KEYNCTR_ENABLE_GPU=1 npx electron .`
- Push: token needed per use (`http.extraHeader` form above), or user stores
one / registers the SSH key.
## Outstanding / next steps
- Live pass (user): KDF migration on a real unlock; second-Amber switch +
backfill rename confirmation; publish kind-0 to primal/damus.
- Step 4 remainder: interactive grant editing in the approval modal.
- No CI workflow yet — `.forgejo/workflows/ci.yml` badge stays commented.
# Checkpoint — permissions UI + updater fix + Workshop theme (2026-09-27 night) # Checkpoint — permissions UI + updater fix + Workshop theme (2026-09-27 night)
## Where things are ## Where things are

View file

@ -19,7 +19,7 @@ Nostr Keynctr (Nostr Feed Manager) pairs a hardened Rust core with an Electron +
## Operating Context ## Operating Context
Workflows: create/switch/delete/undo profiles, compose (Write/Preview, character count, up to 3 link previews, image pick → nostr.build upload with NIP-92 imeta), publish with per-relay receipts, feed aggregation (all vs. My contacts, 24h window), relay add/remove/enable/disable/test, NIP-05 assignment, secret reveal after unlock, vault backup, lock/unlock. Workflows: create/switch/delete/undo profiles, compose (Write/Preview, character count, up to 3 link previews, image pick → nostr.build upload with NIP-92 imeta), publish with per-relay receipts, feed aggregation (all vs. My contacts, 24h window), relay add/remove/enable/disable/test, NIP-05 assignment, secret reveal after unlock, vault backup, lock/unlock.
Environments: Linux desktop (Electron 33), Rust release backend (`target/release/keynectr serve` via stdio), Vite dev server optionally via `NOSTR_GUI_DEV_URL`. Data directory `~/.local/share/nost-feed-manager` (override `XDG_DATA_HOME`), vault `profiles_vault.json` (migrated from legacy Python CLI with timestamped backup), settings file alongside. Environments: Linux desktop (Electron 33), Rust release backend (`target/release/keynectr serve` via stdio), Vite dev server optionally via `NOSTR_GUI_DEV_URL`. Data directory `~/.local/share/keynectr` (auto-migrated from the legacy `nost-feed-manager` dir; override `XDG_DATA_HOME`), vault `profiles_vault.json` (migrated from legacy Python CLI with timestamped backup), settings file alongside.
Rituals: single-click Approve/Reject for NIP-46 requests; explicit `window.confirm` for delete; 5s themed undo bar; publish confirmation toggle; per-relay status dots. Rituals: single-click Approve/Reject for NIP-46 requests; explicit `window.confirm` for delete; 5s themed undo bar; publish confirmation toggle; per-relay status dots.

View file

@ -1,4 +1,4 @@
# Nostr Feed Manager # Keynctr
> A friendly Linux desktop app for managing Nostr profiles, publishing notes, and acting as a > A friendly Linux desktop app for managing Nostr profiles, publishing notes, and acting as a
> **NIP-46 remote signer** — your private keys stay under your control: encrypted in a local > **NIP-46 remote signer** — your private keys stay under your control: encrypted in a local
@ -13,18 +13,17 @@
<!-- <!--
Forgejo CI badge — enable once a workflow exists in `.forgejo/workflows/ci.yml`: Forgejo CI badge — enable once a workflow exists in `.forgejo/workflows/ci.yml`:
[![CI]([YOUR_FORGEJO_INSTANCE_URL]/<OWNER>/<REPO>/actions/workflows/ci.yml/badge.svg)]([YOUR_FORGEJO_INSTANCE_URL]/<OWNER>/<REPO>/actions/workflows/ci.yml) [![CI](https://git.atitlan.io/avi/Keynctr/actions/workflows/ci.yml/badge.svg)](https://git.atitlan.io/avi/Keynctr/actions/workflows/ci.yml)
--> -->
> **Hosting note:** this project is hosted on **Forgejo**. Throughout this document, > **Hosting note:** this project is hosted on **Forgejo** at
> replace `[YOUR_FORGEJO_INSTANCE_URL]` with your instance's base URL (e.g. `https://codeberg.org`) > [`https://git.atitlan.io/avi/Keynctr`](https://git.atitlan.io/avi/Keynctr).
> and `<OWNER>/<REPO>` with the actual repository path.
--- ---
## 📖 Introduction ## 📖 Introduction
Nostr Feed Manager pairs a hardened **Rust core** (the same engine behind the original CLI) with a Keynctr pairs a hardened **Rust core** (the same engine behind the original CLI) with a
polished **Electron + React** desktop interface. All Nostr work — key generation, event signing, polished **Electron + React** desktop interface. All Nostr work — key generation, event signing,
relay communication, encryption — happens inside the Rust backend. The GUI talks to it over a relay communication, encryption — happens inside the Rust backend. The GUI talks to it over a
secure JSON-lines IPC channel and **never sees your secret keys**. secure JSON-lines IPC channel and **never sees your secret keys**.
@ -135,8 +134,8 @@ No Docker, database, or account setup is required.
### 1. Clone and build ### 1. Clone and build
```sh ```sh
git clone [YOUR_FORGEJO_INSTANCE_URL]/<OWNER>/<REPO>.git git clone https://git.atitlan.io/avi/Keynctr.git
cd nost-feed-manager cd Keynctr
# Build the Rust backend (release) # Build the Rust backend (release)
cargo build --release cargo build --release
@ -167,7 +166,7 @@ directly, but you can export these from your shell or source an `.env` before la
| --- | --- | --- | | --- | --- | --- |
| `NFM_PASSWORD` | Vault password for non-interactive CLI use (create / publish / show-secret) | _prompt_ | | `NFM_PASSWORD` | Vault password for non-interactive CLI use (create / publish / show-secret) | _prompt_ |
| `NOSTR_GUI_DEV_URL` | Renderer dev-server URL for hot reload (development only) | — | | `NOSTR_GUI_DEV_URL` | Renderer dev-server URL for hot reload (development only) | — |
| `XDG_DATA_HOME` | Override the data directory | `~/.local/share/nost-feed-manager` | | `XDG_DATA_HOME` | Override the data directory | `~/.local/share/keynectr` |
**`.env.example`** — copy to `.env` and adjust: **`.env.example`** — copy to `.env` and adjust:
@ -180,7 +179,7 @@ NFM_PASSWORD=
# Renderer dev-server URL for hot reload (development only) # Renderer dev-server URL for hot reload (development only)
NOSTR_GUI_DEV_URL=http://localhost:5173 NOSTR_GUI_DEV_URL=http://localhost:5173
# Override the data directory (defaults to ~/.local/share/nost-feed-manager) # Override the data directory (defaults to ~/.local/share/keynectr)
XDG_DATA_HOME= XDG_DATA_HOME=
``` ```
@ -194,13 +193,13 @@ cd frontend
npm run dist # builds renderer + backend and runs electron-builder npm run dist # builds renderer + backend and runs electron-builder
``` ```
The unpacked app lands in `frontend/release/linux-unpacked/`; run it with `./nost-feed-manager`. The unpacked app lands in `frontend/release/linux-unpacked/`; run it with `./keynectr`.
## 🧑‍💻 Usage guide ## 🧑‍💻 Usage guide
### Desktop app ### Desktop app
Tabs cover **Compose**, **Home**, **Profiles**, **Relays**, **Settings**, and **Signer**. Connect Tabs cover **Compose**, **Home**, **Feed**, **Profiles**, **Relays**, **Settings**, and **Signer**. Connect
a relay by entering its `wss://` URL, then compose a note, attach an image, and publish to see a a relay by entering its `wss://` URL, then compose a note, attach an image, and publish to see a
per-relay report. per-relay report.
@ -288,11 +287,11 @@ npm run dev
NOSTR_GUI_DEV_URL=http://localhost:5173 npm start NOSTR_GUI_DEV_URL=http://localhost:5173 npm start
``` ```
### Verify (all green — `cargo 75`, `npm 71`, clippy/fmt/typecheck/lint/format clean) ### Verify (all green — `cargo 225 + 6 e2e`, `npm 139`, clippy/fmt/typecheck/lint/format clean)
```sh ```sh
# Rust # Rust
cargo test # 75 unit tests cargo test # 225 unit tests (+ 6 e2e in tests/)
cargo fmt --check # rustfmt cargo fmt --check # rustfmt
cargo clippy --all-targets cargo clippy --all-targets
@ -301,7 +300,7 @@ cd frontend
npm run typecheck # TypeScript (renderer + electron) npm run typecheck # TypeScript (renderer + electron)
npm run lint # ESLint npm run lint # ESLint
npm run format:check # Prettier npm run format:check # Prettier
npm test # Vitest (jsdom) — 71 tests across 13 files npm test # Vitest (jsdom) — 139 tests across 19 files
``` ```
The test suites exercise the real IPC protocol against a fake backend, so features are tested at The test suites exercise the real IPC protocol against a fake backend, so features are tested at
@ -312,22 +311,29 @@ the protocol boundary rather than in isolation.
``` ```
src/ Rust library + CLI + IPC server src/ Rust library + CLI + IPC server
app.rs app state, vault password/unlock lifecycle app.rs app state, vault password/unlock lifecycle
audit.rs key-export audit log (fail-closed)
bunker.rs NIP-46 bunker (signer server) mode
crypto.rs Argon2id derivation + AES-256-GCM encryption crypto.rs Argon2id derivation + AES-256-GCM encryption
errors.rs structured AppError errors.rs structured AppError
feed.rs feed aggregation (all / contacts, 24h window)
ipc.rs JSON-lines serve() loop and request/reply envelope ipc.rs JSON-lines serve() loop and request/reply envelope
main.rs CLI entry point main.rs CLI entry point
profiles.rs profile create/list/select, secret recovery profiles.rs profile create/list/select, secret recovery, auto-naming
publish.rs note publishing with receipts, images and imeta tags publish.rs note publishing with receipts, images and imeta tags
relays.rs default relays, validation, connection tests relays.rs default relays, validation, connection tests
settings.rs theme and preferences settings.rs theme and preferences
signer.rs NIP-46 remote signer + approval gate updates.rs no-restart self-updater
uploads.rs NIP-98 auth for image-host uploads uploads.rs NIP-98 auth for image-host uploads
vault.rs vault storage (plaintext or password-encrypted) vault.rs vault storage (plaintext or password-encrypted)
signer/ external NIP-46 signer client
backend.rs signer backend dispatch (embedded / external)
nip46_client.rs NIP-46 client: pairing, sessions, park/switch
permissions.rs per-remote per-kind permission grants
frontend/ frontend/
electron/ Electron main + preload (backend spawn, dialogs, clipboard) electron/ Electron main + preload (backend spawn, dialogs, clipboard)
src/ src/
screens/ Compose, Home, Profiles, Relays, Settings, Signer screens/ Compose, Home, Feed, Profiles, Relays, Settings, Signer, SignerMode
lib/ types, api bridge, media helpers lib/ types, api bridge, media helpers, theme tokens
state/ React context (AppProvider) state/ React context (AppProvider)
test/ Vitest suite over a fake backend test/ Vitest suite over a fake backend
package.json scripts + electron-builder config package.json scripts + electron-builder config
@ -341,7 +347,7 @@ respectful and constructive; everyone is expected to follow the
### Reporting bugs ### Reporting bugs
Open an issue on the [issue tracker]([YOUR_FORGEJO_INSTANCE_URL]/<OWNER>/<REPO>/issues) and include: Open an issue on the [issue tracker](https://git.atitlan.io/avi/Keynctr/issues) and include:
- A clear title and description of expected vs. actual behavior - A clear title and description of expected vs. actual behavior
- Steps to reproduce - Steps to reproduce

View file

@ -7,7 +7,7 @@
"name": "Keynectr", "name": "Keynectr",
"email": "maintainers@keynectr.local" "email": "maintainers@keynectr.local"
}, },
"homepage": "https://github.com/avi/Keynctr", "homepage": "https://git.atitlan.io/avi/Keynctr",
"private": true, "private": true,
"main": "dist-electron/main.js", "main": "dist-electron/main.js",
"scripts": { "scripts": {