feat: operator command channel via Nostr (manual dispense)

Add a Nostr-native operator command channel using Kind 21003 (CLINK
Manage) events. Operators listed in OPERATOR_PUBKEYS can send encrypted
commands to the machine.

Phase 1 implements manual dispense: operator sends a dispense command,
machine verifies sender, checks it's idle, performs a direct HAL
dispense (bypassing state machine), and records the transaction.

When ref_txid is provided, the referenced failed transaction is updated
to status 'remediated', closing the loop on dispense errors.

Changes:
- CLINK types: add 'machine' resource, MachineDispenseRequest type
- CLINK client: support operator pubkey list (string | string[])
- Runtime config: VITE_OPERATOR_PUBKEYS env var
- Schema v4→v5: manual_dispense type, remediated_by column
- Lightning services: wire onManagement callback
- ATM store: handleManagementCommand with idle check + remediation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-03-23 00:36:55 -04:00
commit e03782803b
11 changed files with 274 additions and 24 deletions

View file

@ -41,6 +41,14 @@ VITE_LIGHTNING_PUB_API_URL=http://localhost:1776
# If not set, generates ephemeral identity on each restart
VITE_ATM_PRIVATE_KEY=
# =============================================================================
# Operator Identity
# =============================================================================
# Comma-separated list of Nostr hex pubkeys authorized to send operator commands
# (manual dispense, remote management). Decoupled from Lightning.Pub identity.
# VITE_OPERATOR_PUBKEYS=abcd1234...,ef567890...
# =============================================================================
# Mock Fallback (Production Safety)
# =============================================================================