Repository navigation
Description
Review plan for the LavaMoat stack (#1641). Three PRs stacked on feat/lavamoat, each based on the previous one, plus one ADR. Review in this order; each PR's diff shows only its own changes.
1. #1642 — allow-scripts
What to look at:
.yarnrc(ignore-scripts true),.yarnrc.yml,package.json(setupscript,lavamoat.allowScripts, two dev deps),.depcheckrc.yml.- The 16 CI install steps now run
yarn setup. Check none was missed:grep -rn "run: yarn$" .github/workflowsshould return nothing. - The allowlist is all
false. The claim to verify:esbuild,unrs-resolverand@parcel/watcherwork without their install scripts because the binaries arrive as optional deps. Reproduce:rm -rf node_modules && yarn setup && yarn tsx --version.
Decision for the reviewer: keep or drop .yarnrc.yml. It only matters for Yarn 2+, which the repo pins out with packageManager.
2. #1646 — SES lockdown
What to look at:
metro.config.js:lockdownSerializerasserializer, with RN's defaultgetPolyfills.withStorybookdoes not touch the serializer.babel.config.js: theignorepattern accepts\as well as/; the README's pattern does not match on Windows.- The lockdown options are the package's and not configurable (
node_modules/@lavamoat/react-native-lockdown/src/repair.js): console and error taming off. Confirm the releaseconsole.logsilencing inindex.js(Audit Issue K) is unaffected.
What a failure looks like: TypeError: Cannot assign to read only property … at startup, naming the module.
3. #1652 — pinned host globals
What to look at:
app/utils/pinGlobals.ts: the 21 names againstreact-native/Libraries/Core/setUpXHR.jsandsetUpTimers.js. Bindings only, prototypes untouched, on purpose.app/hardenRuntime.tsis the first import ofindex.js.react-native-workletsreassigns timers insideexecuteOnUIRuntimeSync, on the UI runtime. Confirm that readingnode_modules/react-native-worklets/src/initializers.tsaround line 246.__tests__/pinGlobals.unit.ts. Note: the RN Babel preset emits sloppy-mode modules, so a reassignment is ignored, not thrown.
What a failure looks like: Cannot redefine property: <name> or RN's Failed to set polyfill. <name> is not configurable.
4. ADR
zingolabs/zingo-adrs#44, zingo-mobile/0021. One decision, three rules, with what it does not cover. Review it against the three PRs; the submodule pointer in zingo-mobile moves after it lands.
Evidence so far
- Full CI green on the three PRs, including
andr-ubu-integra-test-ci / offline device tests(the app runs on an Android emulator with lockdown and the pinned globals) andios-integration-test / build-for-testing. - Maestro flows dispatched on
lavamoat/3-pin-globals: https://github.com/zingolabs/zingo-mobile/actions/runs/38935961419 - Release bundle built offline and compiled with
hermesc -O; SES shim first,hardenIntrinsics();__r(0)after InitializeCore. - Not done: a debug or release build on a physical device, iOS at runtime, the visual and gesture paths (bottom sheets, QR scanner, gestures).
Device run, if the reviewer has one
Build lavamoat/3-pin-globals in debug. Cold boot, then bottom sheets, gestures, the QR scanner, toasts, charts. Any lockdown failure is at startup and names the culprit.
Merge order
#1642 → #1646 → #1652, then feat/lavamoat → dev.
🤖 Generated with Claude Code
Activity
juanky201271 commentedon Oct 16, 2026
Device run, 2026-10-16:
lavamoat/3-pin-globalsatf7ca6e417(rebased on dev, zingolib stable v7.0.0), Android debug build from Android Studio on a physical device. Cold boot and normal use, nothing odd. No lockdown or pinned-global failure at startup.Still open: iOS at runtime, release build.
juanky201271 commentedon Oct 16, 2026
Device run, 2026-10-16: same branch and tip (
f7ca6e417), iOS debug build on a physical device. Nothing odd.Still open: release build on a device. Note that the Maestro Android job installs the prod release APK, so the Maestro runs cover release on the emulator.
juanky201271 commentedon Oct 16, 2026
Maestro on the rebased tip
f7ca6e417: https://github.com/zingolabs/zingo-mobile/actions/runs/38946975410 (Android installs the prod release APK).lavamoat/3-pin-globalsaddressbook.button.new)addressbook.button.new)Identical to dev. Logcat of the branch run: no
FATAL EXCEPTION, no app death, no lockdown signature (read only property,Cannot redefine,Failed to set polyfill).An earlier dispatch on the pre-rebase tip (run 38935961419, base 25 commits behind dev) had three extra failures. For the record: Android 01 died with a Fabric
addViewAt: failed to insert view … IndexOutOfBoundsException index=4 count=3in release 57 s after launch; Android 06 missedserver.custom.log.final.ok(the server fixes in dev were missing from that base); iOS 05 typed into an unfocused field (the YAML already notes the simulator flake). None reproduced on the rebased tip.juanky201271 commentedon Oct 20, 2026
Stack rebased onto dev
ba7f43064(2026-10-20):feat/lavamoat= dev, heads90e317c5d(#1642),be07a3c83(#1646),5a1368d34(#1652). Conflicts were onlypackage.json/yarn.lock(dependency bumps on dev); resolved by taking dev's files and re-adding the LavaMoat entries, lockfile regenerated, allowlist regenerated (unchanged). Verified again on the new base: frozen install + patch, typecheck, pinGlobals test, prettier, depcheck, offline release bundle (SES first,hardenIntrinsics();__r(0), pinGlobals wired) compiled byhermesc -O. CI re-running.The earlier "failed" checks on the three PRs were cancelled runs, not failures.
juanky201271 commentedon Nov 2, 2026
Re-evaluation against dev
5fd3662f3(zingo 3.0.0), 2026-11-02. dev merged down the stack (no force push):feat/lavamoat= dev, headse620b5d96(#1642),4b61a52f6(#1646),5c8ccf218(#1652). No conflicts.What changed on dev since the stack was written, as far as these PRs care:
@react-navigation/stackleft;zingolib-bundle-pinarrived as a dev dependency, with no install script. The allowlist is unchanged (allow-scripts auto: nothing to add).@lavamoat/react-native-lockdown3.0.0 still applies; the Static Hermes question only arrives with 0.82.index.js,metro.config.js,babel.config.js,jest.config.js: untouched by dev; the only diffs are the stack's own.global.X =in app code. Rescan of every runtime dependency for intrinsic patching or reassignment of a pinned global: nothing new (js-base64opt-in,recyclerlistviewandreact-native-svg's PEG parser patch their own classes).postinstallis nowpatch-package && node scripts/generate-swapkit-secrets.js; it runs throughallow-scriptsas the root script, verified.yarn setup(17 of them on dev, none left as plainyarn).Verified on the merged tip:
yarn setupfrom a clean tree (patch applied, lockdown resolvable),typecheck,pinGlobalstest,prettier:check,depcheck,lint:check(0 errors), offline release bundle (SES first and untransformed,hardenIntrinsics();__r(0),pinGlobalswired) compiled byhermesc -O.The red checks visible since 2026-10-26 were cancelled runs (two pushes a minute apart), not failures; CI is running again on the new heads.