Migrate remote signer calls to prefixed n_signer verbs

This commit is contained in:
Laan Tungir
2026-07-28 06:31:43 -04:00
parent 27da465295
commit 933cf03ce7
6 changed files with 136 additions and 8 deletions
+1 -1
View File
@@ -1 +1 @@
0.6.8
0.6.9
+14
View File
@@ -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.
+2 -2
View File
@@ -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
+106
View File
@@ -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.
+9 -1
View File
@@ -3,6 +3,14 @@
> Status: M1M6 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
+4 -4
View File
@@ -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;
}