feat(signer): nostr publish via resolve_for_wallet + door-scanner stats endpoint #24

Merged
padreug merged 3 commits from signer-abstraction into main 2026-06-07 17:11:44 +00:00

3 commits

Author SHA1 Message Date
1fb96bfe3c chore: bump config.json version to 1.6.1-aio.5
Some checks failed
lint.yml / chore: bump config.json version to 1.6.1-aio.5 (pull_request) Failing after 0s
Releases the door-scanner stats endpoint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-03 19:48:18 +02:00
4238b41f10 feat: GET /tickets/event/{event_id}/stats for door-scanner roster
Mirrors the events_list_event_tickets nostr-transport RPC for callers
that don't hold a raw user prvkey (the webapp post-#9, in particular —
useTicketScanner.refreshStats now has a working HTTP path). Auth:
wallet admin_key + the event's wallet must be in the caller's wallet
set, matching the register endpoint's owner check.

Without this endpoint the activities scanner page loaded its initial
counts (via no-op fallbacks) but every post-scan refreshStats returned
404, leaving the Scanned counter stuck at 0 even though registrations
landed correctly. Surfaced by aio-demo manual test on 2026-06-03.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-03 19:47:49 +02:00
66076d6ca7 feat(signer): migrate Nostr publishing off account.prvkey → resolve_for_wallet (#23)
Closes aiolabs/events#23. Pre-cascade prerequisite for aiolabs/lnbits#17
(signer abstraction phase 1), which lands an m002 startup job that
NULLs the legacy `accounts.prvkey` column. After this migration, the
events extension reads no plaintext nsec and works with any
NostrSigner backend (LocalSigner / RemoteBunkerSigner / ClientSideOnlySigner).

## What changed

### nostr_hooks.py — publish_or_delete_nostr_event

Was: pulled `(account.pubkey, account.prvkey)` from the wallet owner,
passed both to `publish_event_to_nostr`. Hard-skipped publish when
`account.prvkey` was None.

Now: calls `await resolve_for_wallet(event.wallet)` (the DRY helper
from aiolabs/lnbits#23 — wallet → account → signer → can_sign-check
in one call, returns None on any soft-fail). Passes the resolved
`NostrSigner` to the publisher. Soft-skip on None (wallet missing,
account unclassified, or ClientSideOnlySigner where the server has
no signing authority) — matching previous "no prvkey" behavior.

### nostr_publisher.py — publish_event_to_nostr

Was: accepted `(account_pubkey, account_prvkey)` and signed via a
local `sign_nostr_event` helper that called `coincurve.PrivateKey
.sign_schnorr` directly on the plaintext nsec.

Now: accepts `signer: NostrSigner`. Builds the unsigned event dict
(`kind`/`created_at`/`tags`/`content`), hands it to
`await signer.sign_event(...)`, reconstructs the local `NostrEvent`
model from the signed dict (`id`/`pubkey`/`sig` fields). The signer
backend (LocalSigner / RemoteBunkerSigner) is transparent.

Removed the `sign_nostr_event` helper entirely — the signer abstraction
handles all signing now.

Dropped the `coincurve` import; no direct crypto in this extension.

## Acceptance

- [x] keypair helper replaced (nostr_hooks no longer touches account.prvkey)
- [x] publish_event_to_nostr accepts NostrSigner instead of (pubkey, prvkey)
- [x] extension-local Schnorr code removed (sign_nostr_event gone)
- [x] re-grep `events/`: zero `account.prvkey` references
- [x] version bumped: 1.6.1-aio.3 → 1.6.1-aio.4

Manual smoke testing + tag + catalog entry follow the migration
landing; will run against the regtest stack with lnbits on
`issue-18-phase-2.3` (which validates both LocalSigner and
RemoteBunkerSigner signing paths end-to-end).

## Cross-references

- aiolabs/events#23 — issue this commit closes
- aiolabs/lnbits#17 — the cascading signer-abstraction PR
- aiolabs/lnbits#23 — the resolve_for_wallet helper this uses
- aiolabs/lnbits#26 — phase 2.3 (sign_event over bunker, validated against
  aiolabs/nsecbunkerd@fb1c239)
- aiolabs/lnbits#21 — umbrella audit identifying 5 affected extensions

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 21:55:56 +02:00