# Update nostr_core_lib + n_signer Integration

## Context

`n_signer` completed a breaking wire-protocol migration. Our vendored
`nostr_core_lib` (v0.6.10) is incompatible with current `n_signer`. The upstream
lib is now v0.6.15. This plan updates the vendored copy and adapts didactyl to
the new API, plus exposes the new additive features.

## Breaking changes (mandatory)

1. **Verb renames** — legacy names removed: `sign_event`→`nostr_sign_event`,
   `nip04_encrypt`→`nostr_nip44_encrypt`, etc. (handled inside the lib update)
2. **Selector rewrite** — `nostr_index` is **removed** (rejected with error
   `2006 nostr_index_deprecated`). The only accepted selector for `nostr_*`
   verbs is now `{"role":"<name>","role_path":"<full-bip44-path>"}` sent
   **together**. Role-only → `2009 path_required`.

## Our exposure

- `nostr_signer_nsigner_set_nostr_index` in `src/main.c:515`,
  `src/setup_wizard.c:2513`
- `nostr_index` config field in `src/config.h:152`
- `--signer-index` CLI flag in `src/main.c`
- `nostr_index` persisted to genesis

All remote-signer operations fail against current n_signer.

## New APIs (additive, full scope)

- `nostr_signer_last_error()` — human-readable error from last failed call
- `nostr_signer_nsigner_set_role_path()` / `set_derive_index()` — replace
  set_nostr_index
- `nostr_signer_nsigner_from_transport()` / `_from_client()` — flexible
  constructors
- `nostr_signer_mine_event()` — NIP-13 PoW through the signer
- Algorithm verbs: `sign`/`verify`/`encapsulate`/`decapsulate`/
  `derive_shared_secret`/`derive`/`encrypt`/`decrypt` (secp256k1, ed25519,
  x25519, ml-dsa-65, slh-dsa-128s, ml-kem-768, otp)

## Architecture

```mermaid
flowchart TD
    A[Genesis/CLI] --> B[signer_config_t: role + role_path + derive_index]
    B --> C[construct_signer in main.c]
    C --> D[nostr_signer_nsigner_* factory]
    D --> E[nostr_signer_nsigner_set_role_path]
    E --> F[nostr_signer_get_public_key connectivity check]
    F --> G{success?}
    G -->|no| H[print nostr_signer_last_error + signer_health_record_failure]
    G -->|yes| I[Process-lifetime signer handle]
    I --> J[nostr_handler / tools_context / cashu_wallet / block_list]
    I --> K[signer_crypto tool + nostr_post PoW]
    K --> L{local mode?}
    L -->|yes| M[return NOT_SUPPORTED error]
    L -->|no| N[route through remote nsigner]
```

## Design decisions

- **Phase 6:** Extend `nostr_post` with optional PoW args (no new tool). PoW is
  a posting variant; keeps agent tool count unchanged.
- **Phase 7:** Single unified `signer_crypto` tool with `operation` enum
  covering all 10 algorithm verbs. Net +1 tool instead of +10.
- **`role_path` required** for all `nsigner_*` modes (matches n_signer's hard
  requirement).
- **`derive_index` is CLI-only** (like `fds`) — not persisted to genesis.
- **Algorithm tools require remote signer** — local backend returns
  `NOSTR_ERROR_NOT_SUPPORTED`.

## Phases

### Phase 1 — Update vendored nostr_core_lib v0.6.10 → v0.6.15

- Copy upstream `nostr_core_lib/` over `didactyl/nostr_core_lib/`
- Verify VERSION=0.6.15
- Rebuild: `cd nostr_core_lib && ./build.sh --nips=001,004,005,006,011,013,017,019,021,042,044,046,059,060,061`
- Confirm new headers present; `nostr_signer_nsigner_set_nostr_index` gone

### Phase 2 — Replace nostr_index with role_path in config layer

- `src/config.h`: remove `nostr_index`; add `role_path[OW_MAX_URL_LEN]`,
  `derive_index` (-1 = unset)
- `src/config.c`: remove nostr_index parsing; add role_path parsing;
  derive_index is CLI-only
- Update error messages

### Phase 3 — Update main.c signer construction + CLI flags

- Remove `--signer-index`; add `--signer-path <bip44-path>`,
  `--signer-derive-index <n>`
- `apply_signer_overrides()`: require both role + role_path for nsigner_* modes
- `construct_signer()`: `set_role_path` + `set_derive_index` instead of
  `set_nostr_index`
- Surface `nostr_signer_last_error()` in failure paths
- Update `print_usage()`

### Phase 4 — Update setup_wizard.c

- `prompt_signer_transport()`: remove nostr_index prompt; add role_path prompt
  with presets (NIP-06 default, hardened, custom)
- `wizard_construct_ephemeral_signer()`: set_role_path instead of set_nostr_index
- `install_system_service_with_dedicated_user()`: `--signer-path` in key_args;
  shell-escape the path (contains `'`)
- `persist_runtime_config_to_nostr_wizard()`: role_path JSON field instead of
  nostr_index

### Phase 5 — Surface nostr_signer_last_error() everywhere

- `src/main.c`, `src/nostr_handler.c`, `src/nostr_block_list.c`,
  `src/tools/tool_nostr_dm.c`, `tool_memory.c`, `tool_config.c`,
  `tool_task.c`, `cashu_wallet.c`, `src/signer_health.c`

### Phase 6 — Extend nostr_post with optional PoW args

- Add `difficulty` (default 0), `threads` (default 1), `timeout_sec` (default
  600) to nostr_post schema
- difficulty > 0 + remote signer → `nostr_signer_mine_event`
- difficulty > 0 + local mode → clear error (PoW requires remote n_signer)
- difficulty == 0 → unchanged

### Phase 7 — Single unified signer_crypto tool

- `src/tools/tool_signer_crypto.c`: one tool `signer_crypto` with `operation`
  enum: get_info | get_public_key | sign | verify | encapsulate | decapsulate |
  derive_shared_secret | derive_hmac | otp_encrypt | otp_decrypt
- Operation-specific optional args: algorithm, index, scheme, message_hex,
  signature_hex, peer_pubkey_hex, ciphertext_hex, plaintext_b64, encoding, data
- Requires ctx->signer != NULL; clear error otherwise
- Add to tools_internal.h, tools_dispatch.c, tools_schema.c, Makefile SRCS

### Phase 8 — Docs + schema

- `genesis.jsonc.example`, `schemas.json`: role_path instead of nostr_index
- `docs/SIGNER.md`: role+role_path, --signer-path, signer_crypto tool, PoW,
  nostr_index removed (breaking)
- `plans/wizard_full_nsigner_transports.md`: mark superseded

### Phase 9 — Build, test, verify

- `make clean && make deps && make`
- Smoke test with n_signer: `--signer nsigner_unix --signer-socket <name>
  --signer-role main --signer-path "m/44'/1237'/0'/0/0"`
- Test signer_crypto (get_info, sign+verify roundtrip)
- Test nostr_post with difficulty=4
- Run `tests/run_tests.py`
