Compare commits
2 commits
c553dd5e17
...
33ab4fe113
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33ab4fe113 | ||
|
|
7891ccce1a |
4 changed files with 72 additions and 25 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
||||||
46
README.md
46
README.md
|
|
@ -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`:
|
||||||
[]([YOUR_FORGEJO_INSTANCE_URL]/<OWNER>/<REPO>/actions/workflows/ci.yml)
|
[](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
|
||||||
|
|
|
||||||
|
|
@ -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": {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue