From db1a8345870dd543cf6c3c9bdf47f7dc5b5641e1 Mon Sep 17 00:00:00 2001 From: Padreug Date: Sun, 31 May 2026 12:15:57 +0200 Subject: [PATCH] refactor(acl): align IMethod with NIP-46 wire-name vocabulary (#14) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NDK 3.x's `NDKNip46Backend` passes the wire method name verbatim to `pubkeyAllowed` — `nip04_encrypt`, `nip04_decrypt`, `nip44_encrypt`, `nip44_decrypt`, etc. NDK 2.8.1 normalized these to `encrypt`/`decrypt` before calling the permit callback; that normalization was the root of why our encrypt/decrypt path had never worked end-to-end against lnbits's bunker-backed signer (lnbits stores `PolicyRule.method` using wire names, our auth lookup looked for the normalized name → no match → request fell through to the never-resolved admin prompt and timed out at 15s). Source `IMethod` directly from NDK's exported `NIP46Method` union so it can't drift across future bumps. If NDK adds a new method (e.g. `nip60_*`) we pick it up for free. Drop the `method as IMethod` cast at the `signingAuthorizationCallback` call site — both sides now share the same vocabulary by construction. This is the substantive win that aiolabs/nsecbunkerd#14 is filed for. With this commit: - `sign_event` policy rules with kinds continue to match exactly as before (kind stringification path unchanged). - `nip04_encrypt` / `nip04_decrypt` / `nip44_encrypt` / `nip44_decrypt` policy rules — kind-less — now match the live-policy join (step 4 of `checkIfPubkeyAllowed`) by their method-name alone. lnbits's bunker-mediated `signer.nip44_decrypt` and `signer.nip44_encrypt` calls (per `aiolabs/lnbits` PR #38 phase 2.4) start succeeding end-to-end against any operator account whose Policy carries those rules — which `_ensure_policy`'s self-heal already ensures for every newly-bound operator (per coord log 2026-05-30T22:00Z). - `switch_relays` (new in NDK 3) flows through the auth check the same way as any other method. `requestToSigningConditionQuery` needs no further change — the existing `sign_event` switch case covers the only method that discriminates on kind; all other methods use the default `{ method }` query against the override layer, which is correct for the kind-less wire names too. Refs aiolabs/nsecbunkerd#14, aiolabs/nsecbunkerd#11 (whose live-policy join this finally puts to use). --- src/daemon/lib/acl/index.ts | 23 +++++++++++++++++++++-- src/daemon/run.ts | 7 ++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/daemon/lib/acl/index.ts b/src/daemon/lib/acl/index.ts index 2e3559a..f621c98 100644 --- a/src/daemon/lib/acl/index.ts +++ b/src/daemon/lib/acl/index.ts @@ -1,4 +1,4 @@ -import { NDKEvent, NostrEvent } from '@nostr-dev-kit/ndk'; +import { NDKEvent, NostrEvent, NIP46Method } from '@nostr-dev-kit/ndk'; import prisma from '../../../db.js'; /** @@ -114,7 +114,26 @@ export async function checkIfPubkeyAllowed( return undefined; } -export type IMethod = "connect" | "sign_event" | "encrypt" | "decrypt" | "ping"; +/** + * Sign-time auth method names follow the NIP-46 wire convention as + * NDK 3.x's `NDKNip46Backend` passes them through to `pubkeyAllowed` + * verbatim (it stopped normalizing `nip04_encrypt`/`nip04_decrypt` + * to `encrypt`/`decrypt` somewhere between 2.8.1 and current + * upstream). + * + * lnbits's `_ensure_policy` writes `PolicyRule.method` using the same + * wire-name vocabulary (`nip04_encrypt`, `nip04_decrypt`, + * `nip44_encrypt`, `nip44_decrypt`, `sign_event`, `get_public_key`, + * `connect`, `ping`). With the wire-name vocabulary on both sides, + * the post-#11 live-policy join (step 4 of `checkIfPubkeyAllowed`) + * naturally matches lnbits's stored rules — no `encrypt → nip04_encrypt` + * adapter layer needed. + * + * Source the type from NDK itself so it can't drift across future + * NDK bumps; if NDK adds a new method (e.g. `nip60_*`) we pick it up + * for free. + */ +export type IMethod = NIP46Method; export type IAllowScope = { kind?: number | 'all'; diff --git a/src/daemon/run.ts b/src/daemon/run.ts index f664712..9497677 100644 --- a/src/daemon/run.ts +++ b/src/daemon/run.ts @@ -1,10 +1,7 @@ import NDK, { NDKPrivateKeySigner, Nip46PermitCallback, Nip46PermitCallbackParams } from '@nostr-dev-kit/ndk'; import { nip19, utils as nostrUtils } from 'nostr-tools'; import { Backend } from './backend/index.js'; -import { - IMethod, - checkIfPubkeyAllowed, -} from './lib/acl/index.js'; +import { checkIfPubkeyAllowed } from './lib/acl/index.js'; import AdminInterface from './admin/index.js'; import { IConfig } from '../config/index.js'; import { NDKRpcRequest } from '@nostr-dev-kit/ndk'; @@ -107,7 +104,7 @@ function signingAuthorizationCallback(keyName: string, adminInterface: AdminInte } try { - const keyAllowed = await checkIfPubkeyAllowed(keyName, remotePubkey, method as IMethod, payload); + const keyAllowed = await checkIfPubkeyAllowed(keyName, remotePubkey, method, payload); if (keyAllowed === true || keyAllowed === false) { console.log(`🔎 ${nip19.npubEncode(remotePubkey)} is ${keyAllowed ? 'allowed' : 'denied'} to ${method} with key ${keyName}`);