Phase D: typed LNbits error codes + re-pair UX (#52) #61
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "phase-d-rekey-ux"
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?
Phase D of the bunker migration (#52) — error handling. Stacked on #60 (Phase C); targets
phase-c-bunker-bootstrapso this diff is only Phase D (2 commits). Once #60 merges todev, I'll retarget this todev.Two self-contained pieces, both tested:
1.
feat(lnbits)— typed nostr-transport error codes (fc2b5d9)The error-handling layer from the 2026-05-26 cross-session handshake.
LnbitsClientnow rejects ERROR responses with a typedLnbitsRpcErrorcarrying the machine-readablecode+ itsretryPolicy, so callers branch on disposition, not string-matching.error-codes.ts:LnbitsErrorCode(14 codes, signer/transport/app classes) mirroring the lnbits canonical enum;retryPolicyFor();LnbitsRpcError.fromResponse().error_codeis optional-additive on the wire — an absent/unknown code maps tointernal_error(retry-once). No string-matching, no special parser paths.invoice_already_paidflaggedterminal-idempotentfor the cash-out resume-after-reboot case.2.
feat(machine)— re-pair UX on bunker deauth (b59b4ea)A revoked / TTL-expired / off-policy binding (now all enforced post-bind per nsecbunkerd#27) surfaces a dedicated "Pairing Required" screen instead of a raw error; a signer/relay timeout shows "Signer Unreachable" (transient).
classifyInitError()maps the typedBunkerRejectedError/BunkerTimeoutError(byname, so it survives bundle boundaries) to maintenance-screen sentinels, used at every store init catch + the App.vue fallback.Review focus
packages/lnbits/src/error-codes.ts— confirm the 14 codes + retry policies match the lnbits canonical enum /docs/devs/nostr-transport.md(this is the drift-detection surface).apps/machine/src/services/init-error.ts— thename-based classification (deliberately notinstanceof, to survive the dynamic-import boundary in App.vue's maintenance path).Deliberately deferred (remaining Phase D, follow-up)
retryPolicyinto the cash-out XState flow (retry transientoperator_signer_unavailable/rate_limited/internal_error; terminal on the rest) +invoice_already_paid→ success-equivalent at the dispense path. This touches the critical cash-out path and is best validated against live lnbits error emission, not mocks. The foundation (retryPolicy/isRetryable) is in place for it.Checks
typecheck 12/12; 156 tests pass (lnbits 19, machine 29); full electron prod build clean.
Do NOT use the MCP merge endpoint — merge via the Forgejo UI after review.
🤖 Generated with Claude Code
b59b4ea1d4to78d54cdc94