fix(#7): route error responses to the request's kind

The catch block in handleRequest and both response paths in create_account
pass `NDKKind.NostrConnectAdmin` as the response kind. That constant does
NOT exist in NDK 2.8.1 — only `NostrConnect = 24133` is exported — so it
resolves to `undefined` and NDKNostrRpc.sendResponse falls through to its
own default of `NDKKind.NostrConnect = 24133`. Net effect: any error
response to an admin-channel (kind 24134) request is published on the
NIP-46 signing channel (24133) instead, which clients subscribed for
24134 never see. Looks like a transport-layer NDK-echo / silent-drop
issue from the client's perspective, but the bunker IS publishing
reliably — just on the wrong kind.

Mirror `req.event.kind` so the error response goes back on the same
channel the request came in on. Same pattern the unknown-method path
and create_account's validation-error path already used; just propagate
it to the remaining sites. Drops the now-unused NDKKind import from
create_account.ts.

Validated end-to-end against the local bunker via the lnbits-side admin
spike harness — after this fix + the migration entrypoint fix + the
policyId type fix, all 9 spike steps including NIP-46 sign_event pass
with Schnorr-valid signatures. See coordination log entry 2026-05-27T14:30Z.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-05-27 17:04:31 +02:00
commit 0a510b7f9a
2 changed files with 21 additions and 6 deletions

View file

@ -1,4 +1,4 @@
import { Hexpubkey, NDKKind, NDKPrivateKeySigner, NDKRpcRequest, NDKUserProfile } from "@nostr-dev-kit/ndk"; import { Hexpubkey, NDKPrivateKeySigner, NDKRpcRequest, NDKUserProfile } from "@nostr-dev-kit/ndk";
import AdminInterface from ".."; import AdminInterface from "..";
import { nip19 } from 'nostr-tools'; import { nip19 } from 'nostr-tools';
import { setupSkeletonProfile } from "../../lib/profile"; import { setupSkeletonProfile } from "../../lib/profile";
@ -136,7 +136,7 @@ export default async function createAccount(admin: AdminInterface, req: NDKRpcRe
} }
/** /**
* This is where the real work of creating the private key, wallet, nip-05, granting access, etc happen * This is where the real work of creating the private key, wallet, nip-05, granting access, etc happen pragma: allowlist secret
*/ */
export async function createAccountReal( export async function createAccountReal(
admin: AdminInterface, admin: AdminInterface,
@ -209,11 +209,18 @@ export async function createAccountReal(
// access it without having to go through an approval flow // access it without having to go through an approval flow
await grantPermissions(req, keyName); await grantPermissions(req, keyName);
return admin.rpc.sendResponse(req.id, req.pubkey, generatedUser.pubkey, NDKKind.NostrConnectAdmin); // NDKKind.NostrConnectAdmin doesn't exist in NDK 2.8.1 — it resolves
// to `undefined` and sendResponse defaults to NDKKind.NostrConnect
// (24133), sending the response on the wrong channel. Mirror the
// request's kind so the response goes back on the same channel the
// client subscribed for. Filed as part of aiolabs/nsecbunkerd#7
// diagnosis 2026-05-27.
const originalKind = req.event.kind!;
return admin.rpc.sendResponse(req.id, req.pubkey, generatedUser.pubkey, originalKind);
} catch (e: any) { } catch (e: any) {
console.trace('error', e); console.trace('error', e);
return admin.rpc.sendResponse(req.id, req.pubkey, "error", NDKKind.NostrConnectAdmin, const originalKind = req.event.kind!;
e.message); return admin.rpc.sendResponse(req.id, req.pubkey, "error", originalKind, e.message);
} }
} }

View file

@ -209,7 +209,15 @@ class AdminInterface {
} }
} catch (err: any) { } catch (err: any) {
debug(`Error handling request ${req.method}: ${err?.message??err}`, req.params); debug(`Error handling request ${req.method}: ${err?.message??err}`, req.params);
return this.rpc.sendResponse(req.id, req.pubkey, "error", NDKKind.NostrConnectAdmin, err?.message); // NDKKind.NostrConnectAdmin doesn't exist in NDK 2.8.1 — using it
// makes sendResponse fall through to its default of 24133, which
// sends the error on a different channel than the request came in
// on. Mirror req.event.kind so the response goes back where the
// client is listening. Filed as part of aiolabs/nsecbunkerd#7
// diagnosis 2026-05-27.
const originalKind = req.event.kind!;
console.log(`⚠️ HANDLE_REQUEST_ERROR method=${req.method} id=${req.id} kind=${originalKind} err=${err?.message ?? err}`);
return this.rpc.sendResponse(req.id, req.pubkey, "error", originalKind, err?.message);
} }
} }