mirror of
https://github.com/tompro/sattle.git
synced 2026-08-27 07:15:59 +00:00
docs: document wallet integrity boundaries
This commit is contained in:
@@ -25,25 +25,25 @@ e2e/ # Playwright (own AGENTS.md)
|
|||||||
|
|
||||||
## WHERE TO LOOK
|
## WHERE TO LOOK
|
||||||
|
|
||||||
| Task | Location | Notes |
|
| Task | Location | Notes |
|
||||||
|------|----------|-------|
|
| ------------------- | ------------------------------------------------------ | ---------------------------------------------- |
|
||||||
| Any fund movement | `src/lnurlcash/ops/` | engine returns changesets, never mutates state |
|
| 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 |
|
| Add a settings page | `src/pages/` + `router/routes.ts` + SettingsPage group | back-button header pattern |
|
||||||
| Native feature | `src/capabilities/` | never import plugins elsewhere |
|
| Native feature | `src/capabilities/` | never import plugins elsewhere |
|
||||||
| Change wallet state | stores call ops + `addBearers`/`markSpent` | add fresh notes BEFORE marking spent |
|
| 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 |
|
| Mint fee math | `src/lnurlcash/fees.ts` | gross vs net direction matters |
|
||||||
|
|
||||||
## CODE MAP
|
## CODE MAP
|
||||||
|
|
||||||
| Module | Role |
|
| Module | Role |
|
||||||
|--------|------|
|
| -------------------------- | --------------------------------------------------------------------------------------------------------- |
|
||||||
| `lnurlcash/ops.ts` | façade: carve/mint/pay/receiveBearer/transfer |
|
| `lnurlcash/ops.ts` | façade: carve/mint/pay/receiveBearer/transfer |
|
||||||
| `lnurlcash/storage/` | AES-GCM bearers+activity, backup (merge entry point), settings |
|
| `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/keys.ts` | BIP39 + LUD-05 linking key; password wrap |
|
||||||
| `lnurlcash/passkeys.ts` | WebAuthn PRF wrap of the SAME linking key |
|
| `lnurlcash/passkeys.ts` | WebAuthn PRF wrap of the SAME linking key |
|
||||||
| `lnurlcash/nostrBackup.ts` | kind-30078 NIP-44 backup + restore via applyBackup |
|
| `lnurlcash/nostrBackup.ts` | kind-30078 NIP-44 backup + restore via applyBackup |
|
||||||
| `lnurlcash/nwc/` | NIP-47 wallet service (per-connection budget) |
|
| `lnurlcash/nwc/` | NIP-47 wallet service (per-connection budget) |
|
||||||
| `stores/wallet.ts` | state none/locked/unlocked; linking key in memory only while unlocked |
|
| `stores/wallet.ts` | serialized owner lifecycle; state none/locked/unlocked; linking key in memory only while unlocked |
|
||||||
|
|
||||||
## CONVENTIONS
|
## CONVENTIONS
|
||||||
|
|
||||||
@@ -79,8 +79,22 @@ npm run cap:sync # build + cap sync android
|
|||||||
|
|
||||||
- NixOS: use the flake dev shell (nodejs_22 + chromium for e2e).
|
- NixOS: use the flake dev shell (nodejs_22 + chromium for e2e).
|
||||||
`sass-embedded` is aliased to pure-JS `sass` via npm overrides.
|
`sass-embedded` is aliased to pure-JS `sass` via npm overrides.
|
||||||
- lnurlcash-kit comes from `github:TheCryptoDonkey/lnurlcash-kit` pinned to
|
- `lnurlcash-kit@0.1.1` and `lnurlcash-conformance@0.1.1` are exact npm
|
||||||
a commit — the `prepare` script (our merged PR#1) builds dist on install.
|
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. `withStorageLock`
|
||||||
|
falls back to local execution without a cross-tab guarantee.
|
||||||
- tsconfig deliberately relaxed (`exactOptionalPropertyTypes` etc. off) to
|
- tsconfig deliberately relaxed (`exactOptionalPropertyTypes` etc. off) to
|
||||||
keep the protocol core untouched; `src/lnurlcash` has an eslint override.
|
keep the protocol core untouched; `src/lnurlcash` has an eslint override.
|
||||||
- Gitea remote dropped; origin = GitHub. Gitea mirror = pull-mirror on the
|
- Gitea remote dropped; origin = GitHub. Gitea mirror = pull-mirror on the
|
||||||
|
|||||||
+18
-4
@@ -22,8 +22,9 @@ lnurlcash-kit directly.
|
|||||||
```
|
```
|
||||||
ops.ts / ops/ # flows: carve (exact-amount), mint, pay, receiveBearer,
|
ops.ts / ops/ # flows: carve (exact-amount), mint, pay, receiveBearer,
|
||||||
# transfer (inter-mint); ops.ts is the façade
|
# transfer (inter-mint); ops.ts is the façade
|
||||||
storage/ # encrypted bearers + activity log, settings, backup,
|
storage/ # encrypted bearers + activity log, owner-bound NWC,
|
||||||
# nwcConnections, passkeySlots; storage.ts is the façade
|
# passkey and trusted-mint records, settings, backup;
|
||||||
|
# storage.ts is the façade
|
||||||
keys.ts # BIP39, LUD-05 linking key derivation, password wrap
|
keys.ts # BIP39, LUD-05 linking key derivation, password wrap
|
||||||
passkeys.ts + passkeyWrap.ts # WebAuthn PRF wrap (same linking key)
|
passkeys.ts + passkeyWrap.ts # WebAuthn PRF wrap (same linking key)
|
||||||
nostrBackup.ts + nostr/ # kind-30078 backup, NIP-44 self-encryption
|
nostrBackup.ts + nostr/ # kind-30078 backup, NIP-44 self-encryption
|
||||||
@@ -38,8 +39,21 @@ test-utils.ts # mock mint harness used by *.test.ts
|
|||||||
- Style: NO semicolons, 2-space indent, single quotes, `{braced}` imports
|
- Style: NO semicolons, 2-space indent, single quotes, `{braced}` imports
|
||||||
without inner spaces — deliberately different from the rest of the app
|
without inner spaces — deliberately different from the rest of the app
|
||||||
(eslint override); keep the tested core diffable against its lineage.
|
(eslint override); keep the tested core diffable against its lineage.
|
||||||
- Storage: localStorage keys `sattle_*`; strict shape validation on read,
|
- Storage: localStorage keys `sattle_*`; strict shape validation on read.
|
||||||
malformed entries dropped; read-modify-write under `withStorageLock`.
|
Credential, NWC, passkey, and trusted-mint records belong to the canonical
|
||||||
|
saved-key owner. Normal writes require that exact persisted owner; migration
|
||||||
|
of ownerless legacy records has its own proof-gated API.
|
||||||
|
- Concurrency: read-modify-write uses `withStorageLock` where Web Locks are
|
||||||
|
available, but lock handoff is not a localStorage visibility barrier. The
|
||||||
|
trusted-mint repository reconciles from a durable IndexedDB commit mirror
|
||||||
|
before one successful localStorage write and before resolving. Its fallback
|
||||||
|
is local execution only, with no cross-tab serialization guarantee. Storage
|
||||||
|
events are wakeups, so listeners re-read current storage instead of trusting
|
||||||
|
`event.newValue`, including on clears.
|
||||||
|
- Lifecycle: wallet transitions serialize create, restore, unlock, lock, and
|
||||||
|
forget. Activation completes proven-owner migration before exposing unlocked
|
||||||
|
state. Forget locks, drains NWC, clears runtime and owner-bound state, then
|
||||||
|
removes the saved key after biometric deletion succeeds.
|
||||||
- Network: kit calls only; injectable transport/options so tests never
|
- Network: kit calls only; injectable transport/options so tests never
|
||||||
touch the network. No WebSocket at import time (lazy `import()`).
|
touch the network. No WebSocket at import time (lazy `import()`).
|
||||||
- Every module header comment explains the WHY, including failure models.
|
- Every module header comment explains the WHY, including failure models.
|
||||||
|
|||||||
Reference in New Issue
Block a user