diff --git a/VERSION b/VERSION index fae59cac..1a5ac0d4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.6.8 +0.6.9 diff --git a/nostr_core/NSIGNER_INTEGRATION.md b/nostr_core/NSIGNER_INTEGRATION.md index 4808b1d3..50176ec8 100644 --- a/nostr_core/NSIGNER_INTEGRATION.md +++ b/nostr_core/NSIGNER_INTEGRATION.md @@ -120,6 +120,20 @@ int nsigner_client_compute_body_hash_hex(const cJSON* params, char out_hash_hex[ #endif ``` +### n_signer wire methods + +The remote signer backend uses n_signer's role-based Nostr methods: + +- `nostr_get_public_key` +- `nostr_sign_event` +- `nostr_nip04_encrypt` / `nostr_nip04_decrypt` +- `nostr_nip44_encrypt` / `nostr_nip44_decrypt` + +These wire names are distinct from both the public `nostr_signer_*` C API and the +unprefixed NIP-46 method names. In particular, n_signer's unprefixed `get_public_key` +is algorithm-based; this library uses `nostr_get_public_key` because remote signer +factories select keys by `role` or `nostr_index`. + ### Auth and framing notes - Framing is 4-byte big-endian payload length + JSON payload. diff --git a/nostr_core/nostr_core.h b/nostr_core/nostr_core.h index 3d655016..d486fcdd 100644 --- a/nostr_core/nostr_core.h +++ b/nostr_core/nostr_core.h @@ -2,10 +2,10 @@ #define NOSTR_CORE_H // Version information (auto-updated by increment_and_push.sh) -#define VERSION "v0.6.8" +#define VERSION "v0.6.9" #define VERSION_MAJOR 0 #define VERSION_MINOR 6 -#define VERSION_PATCH 8 +#define VERSION_PATCH 9 /* * NOSTR Core Library - Complete API Reference diff --git a/plans/n_signer_verb_migration.md b/plans/n_signer_verb_migration.md new file mode 100644 index 00000000..66163f3e --- /dev/null +++ b/plans/n_signer_verb_migration.md @@ -0,0 +1,106 @@ +# Plan: n_signer Verb Name Migration for nostr_core_lib + +## Context + +`n_signer` has completed a verb-name migration (see [`n_signer/plans/legacy_verb_aliases.md`](../../n_signer/plans/legacy_verb_aliases.md)). The legacy verb names are gone from the n_signer wire protocol. This document covers the fixes needed in `nostr_core_lib` — the shared C client library that many other projects vendor. + +**Reference:** the new n_signer API is documented in [`n_signer/README.md`](../../n_signer/README.md) §4 (API). + +## What changed in n_signer + +The Nostr protocol verbs gained a `nostr_` prefix. The algorithm-based verbs (`sign`, `verify`, `encapsulate`, `decapsulate`, `derive_shared_secret`, `get_public_key`) are unchanged. The old alias verbs (`sign_data`, `ssh_sign`, `verify_signature`, `kem_encapsulate`, `kem_decapsulate`, `otp_encrypt`, `otp_decrypt`) are gone. + +| Legacy n_signer verb | New n_signer verb | +|----------------------|-------------------| +| `sign_event` | `nostr_sign_event` | +| `mine_event` | `nostr_mine_event` | +| `nip04_encrypt` | `nostr_nip04_encrypt` | +| `nip04_decrypt` | `nostr_nip04_decrypt` | +| `nip44_encrypt` | `nostr_nip44_encrypt` | +| `nip44_decrypt` | `nostr_nip44_decrypt` | +| `get_public_key` (role-based) | `nostr_get_public_key` | + +**Important distinction:** `get_public_key` is now **algorithm-based** (takes `algorithm`+`index`). The role-based Nostr pubkey verb is `nostr_get_public_key` (takes `nostr_index`/`role`). Since `nostr_core_lib`'s remote signer always uses role-based selectors (`nostr_index`/`role`), the correct replacement is `nostr_get_public_key`. + +## What does NOT change + +- **NIP-46 standard method names** in [`nostr_core/nip046.c`](../nostr_core/nip046.c) — the strings `"sign_event"`, `"get_public_key"`, `"nip04_encrypt"`, `"nip44_encrypt"`, etc. at lines 111-131 are the **NIP-46 wire protocol** (the standard that relays and other bunkers speak). These must stay as-is. They are NOT n_signer verbs. +- **High-level C API function names** in [`nostr_core/nostr_signer.h`](../nostr_core/nostr_signer.h) — `nostr_signer_get_public_key()`, `nostr_signer_sign_event()`, `nostr_signer_nip04_encrypt()`, etc. are the nostr_core_lib's own API surface. These names are fine — they're C function names, not wire verbs. Renaming them would break every consumer for no protocol reason. +- **The `nostr_signer_local` backend** — local signing doesn't call n_signer at all; it uses libsecp256k1 directly. No changes needed. + +## Files to change + +### 1. `nostr_core/nostr_signer.c` — the remote signer backend + +This is the only file that calls n_signer verbs via `nsigner_client_call()`. All calls use role-based selectors (`nostr_index`/`role`), so all map to the `nostr_*` verbs. + +| Line | Current verb string | New verb string | +|------|---------------------|-----------------| +| 343 | `"get_public_key"` | `"nostr_get_public_key"` | +| 395 | `"sign_event"` | `"nostr_sign_event"` | +| 484 | `"nip04_encrypt"` | `"nostr_nip04_encrypt"` | +| 491 | `"nip04_decrypt"` | `"nostr_nip04_decrypt"` | +| 498 | `"nip44_encrypt"` | `"nostr_nip44_encrypt"` | +| 505 | `"nip44_decrypt"` | `"nostr_nip44_decrypt"` | + +These are simple string-literal replacements. The `signer_remote_params_with_selector()` function (line 289) builds `{"nostr_index": N}` or `{"role": "..."}` selectors — these are still valid for the `nostr_*` verbs, so no selector changes needed. + +**Response format note:** `nostr_get_public_key` returns a plain 64-hex-char string by default (same as the old role-based `get_public_key`). The existing parsing at line 348 (`strlen(result->valuestring) != 64`) continues to work. If the caller ever passes `{"format":"structured"}`, the response would be a JSON object string — but the current code doesn't request structured format, so no change needed. + +### 2. `tests/nsigner_client_test.c` — the mock client test + +| Line | Current verb string | New verb string | +|------|---------------------|-----------------| +| 296 | `"get_public_key"` | `"nostr_get_public_key"` | + +This test uses a mock transport (fds pair) that returns a canned response — it doesn't actually call n_signer. The verb string just needs to match what the real client would send. Update it for consistency. + +### 3. `nostr_core/NSIGNER_INTEGRATION.md` — integration doc + +This doc references the high-level C API function names (`nostr_signer_get_public_key`, `nostr_signer_sign_event`, etc.) which are NOT changing. However, if it documents the underlying n_signer wire verbs anywhere, those references should be updated to the `nostr_*` names. Scan for any wire-verb documentation and update. + +### 4. `plans/nsigner_integration_plan.md` — historical plan + +This is a historical planning doc. Update any wire-verb references for accuracy, or add a note pointing to this migration plan. + +## Files that do NOT need changes + +- `nostr_core/nip046.c` — NIP-46 standard method names (stay as-is, see above). +- `nostr_core/nsigner_client.c` — generic transport/client, doesn't hardcode verb names. +- `nostr_core/nsigner_transport.c` — transport layer, no verb names. +- `nostr_core/nostr_signer.h` — high-level API declarations (function names stay). +- `tests/signer_modules_test.c` — tests the local signer, not n_signer verbs. +- `tests/nip01_test.c` — NIP-01 tests, no n_signer verb calls. + +## Verification + +1. `grep -rn '"sign_event"\|"nip04_encrypt"\|"nip04_decrypt"\|"nip44_encrypt"\|"nip44_decrypt"\|"mine_event"\|"sign_data"\|"ssh_sign"\|"verify_signature"\|"kem_encapsulate"\|"kem_decapsulate"\|"otp_encrypt"\|"otp_decrypt"' nostr_core/nostr_signer.c` returns no matches. +2. `grep -rn '"get_public_key"' nostr_core/nostr_signer.c` returns no matches (replaced by `nostr_get_public_key`). +3. `grep -rn '"sign_event"\|"nip04_encrypt"\|"nip44_encrypt"' nostr_core/nip046.c` still returns matches (NIP-46 standard names — these stay). +4. Build clean: `bash build.sh` or the project's build target. +5. Tests pass: `./build/nsigner_client_test` (or equivalent). + +## Downstream consumers (inherit the fix) + +Once `nostr_core_lib` is updated, these projects that vendor it will pick up the fix when they update their vendored copy: + +- `sovereign_browser/nostr_core_lib/` +- `didactyl/nostr_core_lib/` +- `fips/nostr_core_lib/` +- `fips_access_point/` (if it vendors nostr_core_lib) +- `screen_capture/nostr_core_lib/` +- `n_os_tr/includes/nostr_core_lib/` and `n_os_tr/includes/didactyl/nostr_core_lib/` + +Each downstream project should update its vendored `nostr_core/nostr_signer.c` with the same 6 string-literal changes. The `nip046.c` file in each vendored copy should NOT be changed (NIP-46 standard names). + +## Projects with their own n_signer clients (separate fixes) + +These projects have their own n_signer client code (not vendored from nostr_core_lib) and need separate fixes: + +- **`nostr_terminal/src/nsigner_client.c`** — many call sites (lines 1054-1452): `"get_public_key"`→`"nostr_get_public_key"`, `"sign_event"`→`"nostr_sign_event"`, `"nip04_encrypt"`→`"nostr_nip04_encrypt"`, `"nip44_encrypt"`→`"nostr_nip44_encrypt"`. Also vendored into `n_os_tr/includes/nostr_terminal/`. +- **`codium_nostr_extension/src/signer/nsignerBackend.ts`** — `"get_public_key"` (line 65)→`"nostr_get_public_key"`, `"sign_event"` (line 76)→`"nostr_sign_event"`. +- **`nostr_push/nostr_push`** (bash script) — `"sign_event"` (line 394)→`"nostr_sign_event"`, `"get_public_key"`→`"nostr_get_public_key"`. +- **`laantungir_website/scripts/get_nsigner_pubkey.js`** — `"get_public_key"` (line 68)→`"nostr_get_public_key"`. +- **`laantungir_website/scripts/publish_nostr.js`** — `"get_public_key"` (line 102)→`"nostr_get_public_key"`, `"sign_event"` (line 112)→`"nostr_sign_event"`. +- **`sovereign_browser/src/nostr_bridge.c`** — `"nip04_encrypt"` (line 4525)→`"nostr_nip04_encrypt"`, `"nip44_encrypt"` (line 4529)→`"nostr_nip44_encrypt"`. +- **`nostr_login_lite`** — check `src/signers/nsigner-webserial.js`, `nsigner-webusb.js`, and the built `www/` bundle for verb calls. diff --git a/plans/nsigner_integration_plan.md b/plans/nsigner_integration_plan.md index 67a841fc..df4edae8 100644 --- a/plans/nsigner_integration_plan.md +++ b/plans/nsigner_integration_plan.md @@ -3,6 +3,14 @@ > Status: M1–M6 implemented. This plan remains as architecture history; for current usage/integration contract see: > - `README.md` → "Signer abstraction & nsigner integration" > - `nostr_core/NSIGNER_INTEGRATION.md` +> - `plans/n_signer_verb_migration.md` for the completed Nostr wire-method prefix migration +> +> **Wire-method update:** n_signer's role-based Nostr methods are now +> `nostr_get_public_key`, `nostr_sign_event`, `nostr_nip04_encrypt`, +> `nostr_nip04_decrypt`, `nostr_nip44_encrypt`, and `nostr_nip44_decrypt`. +> The unprefixed `get_public_key` method is algorithm-based and is not used by the +> role/`nostr_index` selectors in this library. Public `nostr_signer_*` C API names +> and standard NIP-46 method names are unchanged. > > This plan originally described pulling the **caller-side** signer-integration glue out of per-project implementations and into `nostr_core_lib`, > so any project that already links the library can sign **locally**, via a **running @@ -15,7 +23,7 @@ re-implement the *same* caller-side integration with n_signer: - 4-byte big-endian length-prefixed framing - kind-27235 auth envelope (`nsigner_rpc` / `nsigner_method` / `nsigner_body_hash`) -- the JSON-RPC verbs (`get_public_key`, `sign_event`, `nip04_*`, `nip44_*`) +- the JSON-RPC methods (`nostr_get_public_key`, `nostr_sign_event`, `nostr_nip04_*`, `nostr_nip44_*`) - a per-project `signer.h` abstraction wrapping local-vs-remote signing Meanwhile, `nostr_core_lib` exposes ~30+ functions that take a raw diff --git a/tests/nsigner_client_test.c b/tests/nsigner_client_test.c index 4a69aa02..8ee6394c 100644 --- a/tests/nsigner_client_test.c +++ b/tests/nsigner_client_test.c @@ -44,7 +44,7 @@ static int write_full_fd(int fd, const unsigned char* buf, size_t len) { static int test_framing_round_trip(void) { int fds[2] = {-1, -1}; - const char* msg = "{\"id\":\"1\",\"method\":\"get_public_key\",\"params\":[]}"; + const char* msg = "{\"id\":\"1\",\"method\":\"nostr_get_public_key\",\"params\":[]}"; char* out = NULL; size_t out_len = 0; int ok = 0; @@ -80,7 +80,7 @@ cleanup: static int test_framing_fragmented_read_reassembly(void) { int fds[2] = {-1, -1}; - const char* msg = "{\"id\":\"2\",\"method\":\"sign_event\",\"params\":[\"abc\"]}"; + const char* msg = "{\"id\":\"2\",\"method\":\"nostr_sign_event\",\"params\":[\"abc\"]}"; unsigned int len = (unsigned int)strlen(msg); unsigned char hdr[4]; char* out = NULL; @@ -250,7 +250,7 @@ static int test_fds_transport_round_trip_via_client(void) { } req[req_len] = '\0'; - if (strstr(req, "\"method\":\"get_public_key\"") == NULL) { + if (strstr(req, "\"method\":\"nostr_get_public_key\"") == NULL) { free(req); _exit(6); } @@ -293,7 +293,7 @@ static int test_fds_transport_round_trip_via_client(void) { goto cleanup; } - if (nsigner_client_call(client, "get_public_key", params, &result) != NOSTR_SUCCESS) { + if (nsigner_client_call(client, "nostr_get_public_key", params, &result) != NOSTR_SUCCESS) { params = NULL; goto cleanup; }