App composition
app/index.tsx is the composition root. It wires useWorklet,
useSubscription, join handling, preferences, list/grid rendering, loyalty
cards, and paywall gating.
| Path | Role | Why it exists |
|---|---|---|
app/ |
React Native application | All user-facing state, gestures, dialogs, preferences, and local UI features. |
app/hooks/_useWorklet.ts |
Runtime bridge | Starts the Bare bundle once and translates backend RPC messages into React state. |
backend/ |
Embedded backend source | Owns canonical list mutations, Autobase setup, Corestore, P2P join, and cleanup. |
rpc-commands.mjs |
Shared command ABI | Keeps frontend and backend in sync without string command names. |
assets/data/ |
Grocery taxonomy source | CSV inputs for category and translation generation. |
scripts/ |
Build and generation helpers | Bundles the Bare backend and generates multilingual grocery lookup files. |
website/ |
Public marketing/static site | Listam landing page, privacy page, and join link handling. |
/wiki/ |
This study site (multi-app wiki) | Human-readable architecture docs for all Listam apps; a sibling folder of listam-mobile. |
useWorklet starts a singleton Bare worklet, passing the Expo document
directory as the backend base path. It also creates the frontend RPC client.
The backend acquires a lock file, loads any saved base/encryption keys, starts an RPC server, and initializes Autobase.
backend/backend.mjs:50
Taps update React state optimistically and send RPC_UPDATE,
RPC_ADD, or RPC_DELETE. The backend serializes writes through
a promise chain before appending to Autobase.
Hyperswarm joins the Autobase discovery key as a topic. Peer connections are passed to
autobase.replicate(conn).
listam.ch/join?invite=... link.RPC_JOIN_KEY to the worklet.add-writer operation, and confirms base credentials.
app/index.tsx is the composition root. It wires useWorklet,
useSubscription, join handling, preferences, list/grid rendering, loyalty
cards, and paywall gating.
The standard list uses an animated inertial list with center-item scaling. The grid view groups grocery items into categories and renders image cards with haptic feedback.
intertial_scroll.tsx VisualGridList.tsxCategory lookup uses generated multilingual maps, keyword fallback, substring matching, and Levenshtein typo tolerance. Icon lookup normalizes item text, translates to English, tries exact and partial matches, then falls back to letter icons.
categoryLookup.ts:129 itemIconMap.ts:1680
The scanner captures QR/barcode data with Expo Camera. The viewer draws QR codes and
selected barcode formats with react-native-svg.
backend/lib/state.mjs exports shared module-level variables and setters for
Autobase, Corestore, swarm, discovery, RPC, keys, pairing, current list, and join flags.
This is simple for an embedded singleton backend, but it depends on only one backend
instance touching storage.
The backend opens lista.lock with exclusive write mode. If another backend
is already running, the new instance throws before it can touch storage.
initAutobase tears down prior resources, opens Corestore, clears
stale boot/encryption metadata when joining a different base, enables encrypted values,
rebuilds the list view, starts Hyperswarm, sets up blind pairing, and sends an invite to
the frontend.
The Autobase apply function is the reducer. It validates item payloads,
writes a materialized JSON view, updates currentList, and emits backend-to-
frontend RPC updates.
| Data | Storage | Owner | Notes |
|---|---|---|---|
| Shopping list log and materialized view | {document}/lista-local |
Bare backend | Corestore + Autobase, encrypted when Autobase has an encryption key. |
| Autobase base key | lista-autobase-key.txt |
Bare backend | Plain hex file in app documents, loaded on restart. |
| Autobase encryption key | lista-encryption-key.txt |
Bare backend | Plain hex file in app documents, loaded only when a base key exists. |
| Invite | lista-invite.json |
Bare backend | Serialized by saveInvite; current code creates a fresh runtime invite. |
| UI preferences | AsyncStorage keys like @lista_grid_view |
React Native app | View mode, category switches, icon/list sizing, and icon variant. |
| Loyalty cards | @lista_loyalty_cards |
React Native app | JSON array containing card name, barcode/QR data, and type. |
| Trial start | @lista_trial_start |
React Native app | Local timestamp used for the 30-day trial calculation. |
backend/lib/state.mjs.package-lock.json is ignored, so dependency resolution is not reproducible by default.app/hooks/_useWorklet.ts imports the iOS backend bundle while the Android import is commented.react-native-svg and qrcode-terminal are imported but not listed as direct dependencies.Read the README to understand why the architecture is local-first and P2P.
README.mdRead useWorklet, then backend lock/key loading, then initAutobase.
Start at a tap handler in app/index.tsx, cross RPC, append an op, then watch apply send a backend update.
Read the link parser, RPC_JOIN_KEY, blind-pairing candidate/member code, then the writable wait loop.
Read the CSV generator scripts, then category grouping and icon lookup. This is a data-heavy subsystem disguised as UI polish.
generate-category-lookup.mjs categoryGrouping.tsUse the Global security notes and the Plans review findings as a review checklist before release or wider collaboration.
Security notes Review findingsHow an implementation agent tests the mobile app in isolation and proves it interacts with other instances. Pair this with the implementation plans and the cross-instance matrix.
Reducers/selectors; category lookup and grocery intelligence; item reduction/replay order; id-migration of legacy text-only items (M1); redaction helpers (M5).
Worklet RPC round-trips (add/update/delete → backend → SYNC_LIST /
*_FROM_BACKEND); join success/timeout/rollback; secure-storage migration of
keys and loyalty cards; deep-link join requires confirmation (H2).
Parity checklist on iOS and Android; offline edit then reconnect-sync; restart persistence.
npm test (Jest/Vitest, added in milestone-zero); Expo dev client for manual
runs; two simulators for device-to-device.
--bootstrap) to test
mobile ↔ mobile, mobile ↔ desktop, and mobile ↔ headless. Mobile rows of the
cross-instance matrix: generated, edited,
completed, and deleted content converge; stays-synced-after-reopen; link-join confirmation
(H2); and id-based duplicate handling (M1).