mirror of
https://github.com/tompro/sattle.git
synced 2026-08-26 23:05:58 +00:00
docs: add AGENTS.md knowledge base (root, protocol engine, e2e)
This commit is contained in:
@@ -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).
|
||||
@@ -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).
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user