docs(backend): pin get_public_key as an intentional, load-bearing ACL exception (#26) #53

Merged
padreug merged 1 commit from fix/26-document-get-public-key-ungated into dev 2026-07-04 13:59:31 +00:00
Owner

Re: #26 — resolves the open question with option A (accept + document). Issue to be closed manually after merge, per repo convention (no auto-close keyword).

What

get_public_key returns the signer pubkey without routing through pubkeyAllowed(), unlike every other NIP-46 method (connect, ping, sign_event, nip04/44_*). #26 flagged this as an unaudited/ungated disclosure through the ACL seam and asked us to decide deliberately between two options: (A) accept + document, or (B) override the strategy to gate it.

The decision: option A — because option B breaks production

I verified against the live clients before touching the gate. Gating get_public_key would reintroduce the exact "signer unavailable" outage class we just spent the #41 saga eliminating:

  • The client calls it as a mandated post-connect session step. nip46_bunker_client.py::connect() flips _connected = True and immediately calls get_public_key to verify the target pubkey — NIP-46 spec 46.md overview step 5. It runs before any policy-bearing method, as part of session establishment.
  • The clients carry no grant for it. lnbits' _ensure_policy (remote_bunker.py DEFAULT_POLICY_RULES + DEFAULT_POLICY_METHODS_NO_KIND) plants rules for sign_event(whitelisted kinds) + the four nip04/44 crypto methods only. There is no get_public_key rule (and none for connect/ping either — connect is granted structurally by any live token, not by a policy rule).
  • ⇒ Gating it would return undefined from checkIfPubkeyAllowed, dropping the client's post-connect call onto the admin-approval path where it stalls until timeout → session establishment fails → "signer unavailable".

The pubkey isn't secret — it's the identity the bunker openly signs as; a relay observer derives it from published events anyway. So option A is correct on the merits and mandatory for compatibility.

The change

Pure documentation — a load-bearing comment at the get_public_key case explaining why it must stay ungated, so a future refactor doesn't "helpfully" gate it and cause the outage. No behavior change.

  • tsc: clean (the 3 pre-existing errors in authorize.ts/web/authorize.ts are unchanged and unrelated).
  • npm run test:nip46: green (serves NIP-46 + survives a flap).

If we ever want per-app gating/audit of identity disclosure, it must be co-designed with a policy rule the clients actually plant — tracked in the #26 discussion (NDK vs rust-nostr prior-art survey).

🤖 Generated with Claude Code

Re: #26 — resolves the open question with option A (accept + document). Issue to be closed manually after merge, per repo convention (no auto-close keyword). ## What `get_public_key` returns the signer pubkey **without** routing through `pubkeyAllowed()`, unlike every other NIP-46 method (`connect`, `ping`, `sign_event`, `nip04/44_*`). #26 flagged this as an unaudited/ungated disclosure through the ACL seam and asked us to decide **deliberately** between two options: (A) accept + document, or (B) override the strategy to gate it. ## The decision: option A — because option B breaks production I verified against the live clients before touching the gate. Gating `get_public_key` would reintroduce the exact "signer unavailable" outage class we just spent the #41 saga eliminating: - **The client calls it as a mandated post-connect session step.** `nip46_bunker_client.py::connect()` flips `_connected = True` and immediately calls `get_public_key` to verify the target pubkey — NIP-46 spec `46.md` overview step 5. It runs *before* any policy-bearing method, as part of session establishment. - **The clients carry no grant for it.** lnbits' `_ensure_policy` (`remote_bunker.py` `DEFAULT_POLICY_RULES` + `DEFAULT_POLICY_METHODS_NO_KIND`) plants rules for `sign_event`(whitelisted kinds) + the four `nip04/44` crypto methods only. There is **no `get_public_key` rule** (and none for `connect`/`ping` either — `connect` is granted structurally by any live token, not by a policy rule). - **⇒ Gating it would return `undefined`** from `checkIfPubkeyAllowed`, dropping the client's post-connect call onto the admin-approval path where it stalls until timeout → session establishment fails → "signer unavailable". The pubkey isn't secret — it's the identity the bunker openly signs as; a relay observer derives it from published events anyway. So option A is correct on the merits *and* mandatory for compatibility. ## The change Pure documentation — a load-bearing comment at the `get_public_key` case explaining **why** it must stay ungated, so a future refactor doesn't "helpfully" gate it and cause the outage. **No behavior change.** - `tsc`: clean (the 3 pre-existing errors in `authorize.ts`/`web/authorize.ts` are unchanged and unrelated). - `npm run test:nip46`: green (serves NIP-46 + survives a flap). If we ever want per-app gating/audit of identity disclosure, it must be co-designed with a policy rule the clients actually plant — tracked in the #26 discussion (NDK vs rust-nostr prior-art survey). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
docs(backend): pin get_public_key as an intentional, load-bearing ACL exception (#26)
Some checks failed
Docker image / build-and-push-image (push) Has been cancelled
d35b98a8c9
get_public_key returns the signer pubkey without routing through
pubkeyAllowed(), unlike every other NIP-46 method. #26 flagged this as an
unaudited/ungated disclosure through the ACL seam and asked us to decide
deliberately between gating it and documenting the exception.

Verified against the live clients: gating it would BREAK production.
lnbits' _ensure_policy (remote_bunker.py DEFAULT_POLICY_RULES +
DEFAULT_POLICY_METHODS_NO_KIND) grants only sign_event(kinds) + the four
nip04/44 crypto methods — no get_public_key rule — and the client calls
get_public_key as a spec-mandated, hardcoded post-connect session step
(nip46_bunker_client.py connect()). Routing it through checkIfPubkeyAllowed
would return `undefined`, dropping that call onto the admin-approval path
and stalling session establishment → "signer unavailable" (the #41 outage
class).

So the correct resolution is #26's option A (accept + document): the pubkey
isn't secret, NIP-46 mandates it ungated during session setup, and the
clients carry no grant for it. Make the exception explicit and load-bearing
in the code so a future refactor doesn't "helpfully" gate it and reintroduce
the outage. No behavior change; tsc-clean; test:nip46 green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
padreug deleted branch fix/26-document-get-public-key-ungated 2026-07-04 13:59:31 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/nsecbunkerd!53
No description provided.