Files
sattle/src/lnurlcash/AGENTS.md
T

2.3 KiB

src/lnurlcash — protocol engine

Framework-free LNURLcash core: no Vue, no Pinia, no Quasar imports. Pinia stores call these modules and apply returned changesets; UI never touches lnurlcash-kit directly.

FUND-CRITICAL INVARIANTS (non-negotiable)

  • Rotate on every receive, and immediately after claiming a fresh mint.
  • A note's declared amount is a claim; the service's maxWithdrawable is authoritative.
  • A melt's "OK" only means the payment is in flight — the verify URL (or the note becoming spendable again) is the real outcome.
  • An ambiguous mutation NEVER loses fresh secrets: rescued into tracked notes, probed, or surfaced to the caller unverified.
  • Definitive service answers (NoteSpentError / NoteUnknownError / PendingNoteError) always propagate distinctly — never papered over by an unverified fallback.

STRUCTURE

ops.ts / ops/    # flows: carve (exact-amount), mint, pay, receiveBearer,
                 # transfer (inter-mint); ops.ts is the façade
storage/         # encrypted bearers + activity log, settings, backup,
                 # nwcConnections, passkeySlots; storage.ts is the façade
keys.ts          # BIP39, LUD-05 linking key derivation, password wrap
passkeys.ts + passkeyWrap.ts   # WebAuthn PRF wrap (same linking key)
nostrBackup.ts + nostr/        # kind-30078 backup, NIP-44 self-encryption
nwc.ts + nwc/    # NIP-47 wallet service
fees.ts          # mint fee math (gross/net direction!) + cached quotes
trustedMints.ts  # key pinning, rekey staging, backup merge rules
test-utils.ts    # mock mint harness used by *.test.ts

CONVENTIONS

  • Style: NO semicolons, 2-space indent, single quotes, {braced} imports without inner spaces — deliberately different from the rest of the app (eslint override); keep the tested core diffable against its lineage.
  • Storage: localStorage keys sattle_*; strict shape validation on read, malformed entries dropped; read-modify-write under withStorageLock.
  • Network: kit calls only; injectable transport/options so tests never touch the network. No WebSocket at import time (lazy import()).
  • Every module header comment explains the WHY, including failure models.

TESTS

npx vitest run — node environment, *.test.ts next to the modules. Adversarial mock mint from lnurlcash-conformance via test-utils.