mirror of
https://github.com/tompro/sattle.git
synced 2026-08-26 23:05:58 +00:00
5.7 KiB
5.7 KiB
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, owner-bound credentials and trusted mints, 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 |
serialized owner lifecycle; 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;
BlankLayoutfor /welcome (q-page needs a layout). - Dark theme,
#002222bg, mint#55ffccprimary;sattle-cardsurfaces. - Quasar
Notifyplugin 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
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-embeddedis aliased to pure-JSsassvia npm overrides. lnurlcash-kit@0.1.1andlnurlcash-conformance@0.1.1are exact npm registry packages. Keep their manifest pins, registry tarball URLs, and lock integrity values intact.- Credential and authorization records are bound to the canonical owner ID derived from the linking key. Ordinary writes require an exact match with the persisted saved-key owner. Ownerless legacy records migrate only after a proven unlock, never from a restored file claim or passkey-first attempt.
- Wallet create, unlock, lock, restore, and forget transitions are serialized. Forget locks first, drains NWC, clears runtime keys and owner namespaces, then removes the saved key only after biometric deletion succeeds.
- Storage events are wakeups, not authoritative payloads. Re-read matching
storage on an event, including
key === null, before converging state or invalidating a stale owner tab. Web Locks serialize supported browsers but do not make another tab's localStorage cache current; trusted mints reconcile through a durable IndexedDB commit mirror before success.withStorageLockfalls back to local execution without a cross-tab guarantee. - tsconfig deliberately relaxed (
exactOptionalPropertyTypesetc. off) to keep the protocol core untouched;src/lnurlcashhas an eslint override. - Gitea remote dropped; origin = GitHub. Gitea mirror = pull-mirror on the Gitea side (no secrets needed).