refactor(v2): extract kind-30078 publish primitives to nostr_publish.py (#39 1/3)
Layer 2 prep: a second consumer (fee_transport.py for #39) is about to land that uses the same operator-signer + NIP-44 v2 + nostrclient publish flow as cassette_transport.py. Extracting shared primitives now rather than duplicating ~100 lines. New `nostr_publish.py` module: - Error hierarchy: NostrPublishError base + OperatorIdentityMissing, SignerUnavailable, RelayUnavailable subclasses (all transport-layer failures, domain-agnostic). - `resolve_operator_signer(operator_user_id)` — fetch account + resolve to NostrSigner, with the can-sign + has-pubkey checks. - `sign_as_operator(operator_user_id, event)` — wrap signer.sign_event, set created_at before signing. - `nip44_encrypt_via_signer` + `nip44_decrypt_via_signer` — transitional LocalSigner → RemoteBunkerSigner cascade (bunker handles natively; LocalSigner falls back to hand-rolled NIP-44 v2 against the stored prvkey). - `publish_signed_event(signed)` — nostrclient relay-manager publish with lazy import + RelayUnavailable on missing extension. - High-level `publish_encrypted_kind_30078(operator_user_id, recipient_pubkey_hex, d_tag, payload)` — builds event, encrypts via signer, signs, publishes. The whole flow in one call; callers (cassette_transport, soon fee_transport) just specify domain. `cassette_transport.py`: - Imports from nostr_publish; CassetteTransportError becomes a subclass of NostrPublishError so existing catches still work. - `publish_to_atm` reduces to a thin wrapper that builds the cassette-specific payload + d-tag and delegates to `publish_encrypted_kind_30078`. - Consumer path (`decrypt_and_parse_state_event`) still owns cassette-specific decode/transient distinctions; uses imported `nip44_decrypt_via_signer`. - Re-exports OperatorIdentityMissing / SignerUnavailable / RelayUnavailable so views_api can keep importing from cassette_transport without change. `tasks.py` — cassette bootstrap consumer imports `resolve_operator_signer` from nostr_publish directly instead of the cassette_transport underscore-prefixed name. 164/164 tests green; behavior unchanged. Refs: aiolabs/satmachineadmin#37 (parent), #39 (Layer 2, this commit is prep). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
52911af7b1
commit
aeaee1f568
3 changed files with 342 additions and 214 deletions
4
tasks.py
4
tasks.py
|
|
@ -408,9 +408,9 @@ async def _handle_cassette_state_event(
|
|||
CassetteEventDecodeError,
|
||||
CassetteEventTransientError,
|
||||
CassetteTransportError,
|
||||
_resolve_operator_signer,
|
||||
decrypt_and_parse_state_event,
|
||||
)
|
||||
from .nostr_publish import resolve_operator_signer
|
||||
|
||||
event_raw = event_message.event
|
||||
if isinstance(event_raw, str):
|
||||
|
|
@ -444,7 +444,7 @@ async def _handle_cassette_state_event(
|
|||
return
|
||||
|
||||
try:
|
||||
account, signer = await _resolve_operator_signer(machine.operator_user_id)
|
||||
account, signer = await resolve_operator_signer(machine.operator_user_id)
|
||||
except CassetteTransportError as exc:
|
||||
# OperatorIdentityMissing / SignerUnavailable — log + skip.
|
||||
logger.warning(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue