diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..43a4dd5 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,87 @@ +# PROJECT KNOWLEDGE BASE + +**Generated:** 2026-08-20 +**Commit:** 7841a13 +**Branch:** main + +## OVERVIEW + +sattle — end-user PWA wallet for LNURLcash (LUD-25) Lightning bearer notes. +Quasar 2 / Vue 3 (`script setup` + TS) / Pinia / vue-i18n. Capacitor 8 +Android wrapper lives in `android/`; the PWA is the canonical build. + +## STRUCTURE + +``` +src/lnurlcash/ # protocol engine + storage, framework-free (own AGENTS.md) +src/stores/ # Pinia: wallet, mints, activity, nostrBackup, nwc +src/components/ # dialogs (send/, receive/), QrCode, QrScanner, UnlockForm +src/pages/ # IndexPage (main), Settings*, ManageMints, MoveFunds, Nwc, + # Backup, Security, Welcome (onboarding) +src/capabilities/ # ONLY place Capacitor plugins are imported +src/boot/ # wallet init, deeplinks +e2e/ # Playwright (own AGENTS.md) +``` + +## WHERE TO LOOK + +| Task | Location | Notes | +|------|----------|-------| +| Any fund movement | `src/lnurlcash/ops/` | engine returns changesets, never mutates state | +| Add a settings page | `src/pages/` + `router/routes.ts` + SettingsPage group | back-button header pattern | +| Native feature | `src/capabilities/` | never import plugins elsewhere | +| Change wallet state | stores call ops + `addBearers`/`markSpent` | add fresh notes BEFORE marking spent | +| Mint fee math | `src/lnurlcash/fees.ts` | gross vs net direction matters | + +## CODE MAP + +| Module | Role | +|--------|------| +| `lnurlcash/ops.ts` | façade: carve/mint/pay/receiveBearer/transfer | +| `lnurlcash/storage/` | AES-GCM bearers+activity, backup (merge entry point), settings | +| `lnurlcash/keys.ts` | BIP39 + LUD-05 linking key; password wrap | +| `lnurlcash/passkeys.ts` | WebAuthn PRF wrap of the SAME linking key | +| `lnurlcash/nostrBackup.ts` | kind-30078 NIP-44 backup + restore via applyBackup | +| `lnurlcash/nwc/` | NIP-47 wallet service (per-connection budget) | +| `stores/wallet.ts` | state none/locked/unlocked; linking key in memory only while unlocked | + +## CONVENTIONS + +- Commit style: semantic (`feat:`, `fix:`, `build:`, `ci:`, `test:`), English. +- Releases: release-please (`.github/workflows/release.yml`) — never bump + versions or write changelog entries by hand; merge the release PR. +- Hash router; `BlankLayout` for /welcome (q-page needs a layout). +- Dark theme, `#002222` bg, mint `#55ffcc` primary; `sattle-card` surfaces. +- Quasar `Notify` plugin is enabled — use it for toasts. +- Brand assets: `src/assets/sattle-{wallet,text}.png` (raster; source SVGs + live outside the repo). + +## ANTI-PATTERNS (THIS PROJECT) + +- NEVER mutate wallet state inside `src/lnurlcash/**` — engine is pure. +- NEVER auto-apply a mint's advertised new signing key — stage as pending rekey. +- NEVER log note URLs / k1 values. +- No type suppression (`as any`, `@ts-ignore`) anywhere. +- No new runtime deps without a recorded reason (nostr-tools is the bar). + +## COMMANDS + +```bash +npx quasar dev -m spa # dev +npx vitest run # unit (node env, mock mint) +npm run test:e2e # playwright, system chromium (CHROMIUM_PATH) +npx vue-tsc --noEmit # typecheck +npx quasar build -m pwa # production build +npm run cap:sync # build + cap sync android +``` + +## NOTES + +- NixOS: use the flake dev shell (nodejs_22 + chromium for e2e). + `sass-embedded` is aliased to pure-JS `sass` via npm overrides. +- lnurlcash-kit comes from `github:TheCryptoDonkey/lnurlcash-kit` pinned to + a commit — the `prepare` script (our merged PR#1) builds dist on install. +- tsconfig deliberately relaxed (`exactOptionalPropertyTypes` etc. off) to + keep the protocol core untouched; `src/lnurlcash` has an eslint override. +- Gitea remote dropped; origin = GitHub. Gitea mirror = pull-mirror on the + Gitea side (no secrets needed). diff --git a/e2e/AGENTS.md b/e2e/AGENTS.md new file mode 100644 index 0000000..cdd787b --- /dev/null +++ b/e2e/AGENTS.md @@ -0,0 +1,41 @@ +# e2e — Playwright suite + +Browser-level tests against the real app. Run: `npm run test:e2e` +(`test:e2e:ui` for the UI mode). + +## ENVIRONMENT + +- System Chromium, never downloaded browsers: `CHROMIUM_PATH` env or + `command -v chromium` fallback (flake dev shell provides both). CI + installs Playwright's own chromium — the config only forces + `executablePath` when a system browser exists. +- `webServer` boots `quasar dev -m spa --port 9333` (~15-25s ready), + `reuseExistingServer` locally. serviceWorkers blocked. + +## STRUCTURE + +- `fixtures.ts` — extended test with the `mint` fixture (MintMocker), + routes unrouted after each test. +- `helpers/MintMocker.ts` — `page.route` mocks on never-resolving + `https://mint.test` / `https://mint2.test` origins. Responses must carry + `Access-Control-Allow-Origin: *`. IMPORTANT: mirror the real protocol — + `mintPubkey` is announced on the mint-address (`/.well-known/lnurlw/`) + and note-info responses, NOT the payRequest (a too-generous mock once + masked a real bug). +- `helpers/wallet.ts` — `createFreshWallet(page)`: drives onboarding UI to + an unlocked 0-sat wallet. + +## CONVENTIONS + +- No real network, ever — every external call goes through a route mock. +- First-contact trust prompt: receiving a note with a `mintPubkey` opens + the "New mint" dialog — specs must click through it. +- Toasts duplicate in-page text — scope assertions to `.q-page` or the + dialog, never the raw text. +- Dev-only test hooks (e.g. `window.__sattleNwcTest`) exist for injecting + fake transports; never available in production builds. +- WebAuthn ceremonies are NOT e2e-testable headless — assert the honest + unsupported state instead. +- Known lint drift: `import()` type annotations trip + `consistent-type-imports` (pre-existing; the dev checker only lints app + code, so CI/e2e are unaffected). diff --git a/src/lnurlcash/AGENTS.md b/src/lnurlcash/AGENTS.md new file mode 100644 index 0000000..6f6c3e4 --- /dev/null +++ b/src/lnurlcash/AGENTS.md @@ -0,0 +1,50 @@ +# 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.