fix(transport): swap the relay layer NDK → nostr-tools so subscriptions survive reconnects (#42, fixes #41) #43
Merged
padreug
merged 3 commits from 2026-06-27 00:14:46 +00:00
fix/42-nostr-tools-transport into dev
3 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| a676d4fa98 |
feat(transport): port the admin RPC off NDK onto the relay pool (#42)
Some checks failed
Docker image / build-and-push-image (push) Has been cancelled
Third increment of the NDK -> nostr-tools transport swap. The admin interface's runtime RPC now runs on the RelayPool transport, so the admin channel — like the signer channel — re-subscribes on every relay reconnect and can't go silently deaf after a flap (#41). - nip46/transport.ts is now a full RPC: besides serving inbound requests it routes inbound RESPONSES to one-shot handlers (the pending map) and can sendRequest() — needed for the interactive approval flow (bunker -> operator "acl" request). start() takes the kinds to listen on; sendResponse() takes the response kind. The signer backend is unaffected (still one kind, response-only). - admin/index.ts: rebuilt on RelayPool + Nip46Transport instead of NDK + NDKNostrRpc + attachIndefiniteReconnect. `rpc` is a small adapter the command handlers keep calling; it resolves each request's envelope scheme (nip04/nip44) by id and publishes on the admin channel (24134). requestPermission/Response use transport.sendRequest + nip19 instead of NDKNostrRpc + NDKUser. The connectedRelays()-only watchdog is replaced by a session-liveness one on pool.healthy() (connected AND subscribed) — the check the old one couldn't make (#20/#41). - admin/types.ts: AdminRpcRequest / AdminRpc replace NDKRpcRequest / NDKNostrRpc. The ~16 command + validation handlers swap the import only (they use req.{id,pubkey,method,params,event.kind}); no logic change. - admin/kinds.ts: plain numeric kinds (24133/24134), no NDKKind type dep. - relay-reconnect.ts deleted — its job (reconnect) now lives in the pool, and its blind spot (no resubscribe) is exactly what #41 was. Still on NDK (not transport, addressed separately): the one-shot boot DM (notifyAdminsOnBoot, throwaway NDK over public relays), key generation in create_new_key/create_account, the getKeys npub helper, and the standalone CLI client (src/client.ts). Tests (tests/admin-transport.test.ts): a request on 24133 is answered on 24134 and survives a relay flap; the sendRequest + response-routing approval flow round- trips. lifecycle 7 / relay 2 / nip46 1 / admin 2 all green; daemon bundles clean; zero new type errors. Refs: #42, #41, #20, #7 |
|||
| ea923b472d |
feat(transport): port the NIP-46 backend off NDK onto the relay pool (#42)
Some checks failed
Docker image / build-and-push-image (push) Has been cancelled
Second increment of the NDK -> nostr-tools transport swap. The daemon's backend
signing path no longer uses NDK at all.
- nip46/transport.ts: the NIP-46 RPC wire layer over the RelayPool, replacing
NDKNostrRpc. Same crypto + framing so existing clients (lnbits, the spire) are
unaffected byte-for-byte: adaptive nip04/nip44 envelope (nip04 iff content has
`?iv=`, fallback to the other), verify the kind:24133 signature, JSON
`{id,method,params}` in / `{id,result,error}` out, signed as the held key and
`#p`-tagged to the client.
- backend/index.ts: the Backend is rebuilt on that transport instead of
`extends NDKNip46Backend`. The dispatch + response strings match NDK's
strategies exactly (connect->ack, ping->pong, get_public_key, sign_event->
signed event JSON, nip04/44 encrypt/decrypt, reject->error/"Not authorized").
The ACL hook (pubkeyAllowed -> permitCallback) is unchanged; the ACL only
reads `.kind` off the sign_event payload, so a plain parsed event suffices.
- backend/token-store.ts: the prisma-backed connection-token redemption
(validateToken/applyToken) split out of the Backend and injected, so the
protocol layer has no database dependency and is unit-testable. Logic
unchanged (#24/#25 live-lifecycle semantics preserved).
- run.ts: the daemon now drives a RelayPool (heartbeat on) for the backend
transport instead of an NDK instance + attachIndefiniteReconnect; startKey
wires the prisma applyToken.
- relay-pool.ts: publish() now retries across a reconnect window — a publish
that lands mid-flap rejects ("relay connection errored"), so we wait for the
pool to recover and retry (relays dedupe by id; clients match by request id).
Test (tests/nip46-backend.test.ts): a real nostr-tools NIP-46 client drives
connect/ping/get_public_key/sign_event/nip44_encrypt through a mock relay,
asserts each response, FLAPS the relay, and asserts the backend still answers —
then checks the deny path returns "Not authorized". Green. lifecycle + relay
suites unchanged.
The admin interface (NDKRpc) + the getKeys listing still use NDK; that's the
next increment. NDK remains a dependency until then.
Refs: #42, #41, #25, #24, #21, #9
|
|||
| 1409941d11 |
feat(transport): nostr-tools relay pool that re-subscribes on reconnect (#42)
Some checks failed
Docker image / build-and-push-image (push) Has been cancelled
First increment of the NDK -> nostr-tools transport swap (#42), the root fix for #41 (bunker goes silently deaf after a relay flap). NDK does not replay subscriptions on reconnect: a NDKRelaySubscription registers `relay.once("ready", execute)` and never re-arms, so after a flap the socket reconnects but the kind:24133 REQ is never re-sent. We chased that through #4/#7/#20/#21 without closing it because it is structural in NDK. `RelayPool` (src/daemon/lib/relay-pool.ts) owns the connect loop, modelled on lightning.pub's RelayConnection and signet's relay-pool (both nostr-tools, both bind resubscribe to reconnect). Every (re)connect re-subscribes the whole registry, so subscription liveness can't drift from socket liveness. It also exposes `healthy()` (connected AND registry subscribed on the wire) — the session-liveness signal the old connectedRelays()-only watchdog couldn't make, which is what let #20's reconnect mask the deaf state. We disable nostr-tools' own `enableReconnect`: its auto-resubscribe is version-fragile right now (regressed in 2.23.0 fb7de7f; the 455124e fix is unreleased as of 2026-06-26), so the resubscribe is OUR code, not a function of which nostr-tools version is installed. Regression test (tests/relay-pool.test.ts + tests/helpers/mock-relay.ts): an in-process mock relay flaps mid-session (down + back up on the same port) and we assert a subsequent inbound kind:24133 is still delivered — the exact #41 scenario, and the test that was missing every prior round. Green; existing lifecycle suite unchanged. Next increments on this branch: port Backend (NIP-46) + AdminInterface (RPC) onto the pool, wire run.ts, retire relay-reconnect.ts + the connection-only watchdog. Refs: #42, #41, #21, #20, #9 |