Migrate Nostr publishing off account.prvkey → use resolve_signer (pre-cascade prerequisite for lnbits#17) #11
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
views_api.py:_resolve_signing_keypairreadsaccount.prvkeydirectly to sign restaurant + menu-item events:aiolabs/lnbits#17(signer abstraction phase 1, PR open) ships anm002classify job that fail-closed NULLs theaccounts.prvkeycolumn for every row at startup. When that PR cascades to a host running this extension, allaccount.prvkeyreads returnNone,_resolve_signing_keypairreturnsNone, and Nostr publishing silently stops working.Umbrella audit at
aiolabs/lnbits#21identifies this extension among 5 affected.Sites to migrate
views_api.py:111-135_resolve_signing_keypair— replace with_resolve_signerviews_api.py:142-150_publish_restaurant— uses keypair to callpublish_event(client, event, prvkey)views_api.py:191-202_publish_menu_item— same shapeviews_api.py:216-224_publish_menu_item_delete— same shapenostr_publisher.py:205-209sign_nostr_event(event, private_key_hex)— replace internalsnostr_publisher.py:212-232publish_event(client, event, private_key_hex)— accept a signerMigration pattern
Replace the keypair-returning helper with a signer-returning one:
Refactor
publish_eventto accept a signer and callsigner.sign_event(event_dict). The currentNostrEventmodel carriesid/sigfields;NostrSigner.sign_event(dict)returns the dict withid+pubkey+sigpopulated. Adapt the publisher to round-trip through dict ↔ NostrEvent at the signer boundary:Builders no longer need to compute
nostr_event.idthemselves (signer does it). Optional cleanup: drop thenostr_event.id = nostr_event.event_idlines frombuild_*functions.Acceptance
_resolve_signing_keypairremoved;_resolve_signerreturns aNostrSigner | Nonesign_nostr_eventhelper removed (signer handles it internally)publish_eventaccepts aNostrSignerinstead ofprivate_key_hex_publish_restaurant,_publish_menu_item,_publish_menu_item_delete) updated to pass the signerrestaurant/post-migration: zeroaccount.prvkeyreferencespyproject.tomlversion to nextvX.Y.Z-aio.Nper the fork-versioning conventionaiolabs/lnbits-extensions/extensions.json(new entry alongside the previous, don't overwrite — operators on older versions need the previous to still resolve)Timing
This blocks
aiolabs/lnbits#17's cascade to any host that runs the restaurant extension. Currently no production host runs restaurant; landing this before PR #17 cascades to dev-tier hosts (cfaun/four84/aio-demo) is the conservative call.Cross-references
aiolabs/lnbits#21— umbrella audit (this is one of 5 affected extensions)aiolabs/lnbits#17— the cascading PR whose m002 NULLsaccounts.prvkeyaiolabs/lnbits#9— parent: operator-IdP framing, signer abstractionaiolabs/nostrmarket#5— sister migration for the merchant signing path~/dev/coordination/log.md2026-05-26T19:30Z — cross-session coordination entry surfacing this auditUpdate 2026-05-27 — async sign_event API confirmed via
aiolabs/lnbits#24NostrSigner.sign_eventis migrating toasync definaiolabs/lnbits#24(open, stacked on PR #19). When that lands (along withaiolabs/lnbits#17,#19,#23), the canonical migration pattern in this issue's body becomes:Two notes:
resolve_for_wallet(wallet_id)(lands inaiolabs/lnbits#23) collapses theget_wallet → get_account → resolve_signer → check can_sign()block into one call. Use it instead of hand-rolling that lookup.await signer.sign_event(event)works for all three concrete signers (LocalSigner,ClientSideOnlySigner,RemoteBunkerSigner) — the ABC is async and the sync impls expose an async signature for consistency. No runtime cost onLocalSigner(Python returns immediately when awaiting a sync-bodyasync def).sign_nostr_eventextension-local helper can be removed entirely — the signer handlesid+pubkey+sigpopulation. Builders no longer need to computenostr_event.idthemselves either (drop thenostr_event.id = nostr_event.event_idlines).Acceptance items remain the same; just code the await form from day one.