deviant/shells/mobile
walerikus 518a3ce174 chore: initial project baseline — full Deviant codebase under version control
Brings the whole project into git (previously only the on-ramp feature and
a few parallel files were tracked). Includes: wallet core (src/), CashScript
contracts/, the Capacitor shell (shells/), scripts/, tests, and the docs
corpus (wiki/, snapshots/, Parameters/, Archive/, KnowledgeBase text).

Excluded via .gitignore: node_modules/, dist/, .env (secrets), testnet WIF
key files (.chipnet-*.json, devWallets.ts), KnowledgeBase/RAW media (~9.6 GB),
transcripts/ (session logs), and machine-local config (.claude/*.local,
*.lock). Secret-scanned before commit — no credentials in the tree.

Note: src/swapMath.ts, src/swapBuilder.ts, contracts/OpenMintToken.cash are
legacy (OP_RETURN era) but still imported; committed as-is, flagged for a
separate removal once de-referenced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 20:38:07 +02:00
..
nodejs-assets/nodejs-project chore: initial project baseline — full Deviant codebase under version control 2026-06-13 20:38:07 +02:00
src chore: initial project baseline — full Deviant codebase under version control 2026-06-13 20:38:07 +02:00
package.json chore: initial project baseline — full Deviant codebase under version control 2026-06-13 20:38:07 +02:00
README.md chore: initial project baseline — full Deviant codebase under version control 2026-06-13 20:38:07 +02:00
tsconfig.json chore: initial project baseline — full Deviant codebase under version control 2026-06-13 20:38:07 +02:00

Deviant — React Native mobile shell (Phase 4)

STATUS: architecture slice only — UNBUILT and UNVERIFIED. This directory contains the runtime-architecture decision (ADR-0022) made real as a reviewable skeleton: the RN↔Node bridge and the KeyChainKeyStore. It has never been compiled or run — it was authored on a Windows box with no RN / iOS / Android toolchain. No screens exist yet, by deliberate scope: they are worthless until the runtime path (esp. the iOS open risk below) is device-verified. Do not treat any of this as working code.

Why the core runs in Node, not in React Native

See ../../wiki/decisions.md → ADR-0022 for the full reasoning. In short:

@bitauth/libauth ships its secp256k1 / sha256 / sha512 / ripemd160 primitives as WebAssembly only, with no pure-JS fallback and no seam to swap them. Hermes (and RN's JSC) have no WebAssembly support. mainnet-js depends on libauth, so it inherits the blocker. Therefore the core cannot run directly in the RN JS engine.

Decision: embed the shared core in an on-device nodejs-mobile process (a real Node/V8 runtime, where WASM works) and make the RN/Hermes layer a thin UI + key-custody + bridge client. One crypto implementation across all shells; the ~231-test core ships unmodified; the seed never leaves the device (non-custodial).

┌─────────────── React Native (Hermes) ───────────────┐      ┌──── nodejs-mobile (V8) ────┐
│  screens (NOT BUILT)                                 │      │  main.js  (bridge entry)   │
│  bootstrap.ts                                        │      │   ├─ InMemoryTokenRegistry │
│   ├─ KeyChainKeyStore ── SecureStore (Keychain)      │ JSON │   ├─ connectWallet(...)     │
│   │     └─ IdentityCache (AsyncStorage, non-secret)  │◄────►│   └─ src/web/api.ts handlers│
│   └─ NodeCoreClient ── BridgeChannel ────────────────┼──────┤      (balance/tokens/...)   │
└──────────────────────────────────────────────────────┘      └────────────────────────────┘
        UI + secret custody only; NO blockchain crypto              ALL derivation + signing

⚠️ Load-bearing OPEN RISK — iOS jitless WASM (must be device-verified)

Apple forbids JIT for third-party apps, so nodejs-mobile runs V8 in --jitless mode on iOS. V8 jitless mode has historically disabled WebAssembly compilation. If that still holds, libauth's WASM will fail on iOS even inside nodejs-mobile — defeating the whole decision on iOS specifically. This is unverified (no Mac/device here). Verify on a real iOS device before committing to the iOS shell. Android (V8 with JIT) is expected to work and should be validated first.

Fallbacks if iOS jitless WASM proves fatal, in preference order:

  1. Ship Android first; resolve iOS separately.
  2. Compile a native secp256k1/hash addon into the nodejs-mobile iOS build to replace libauth's WASM crypto.
  3. Adopt a Hermes-side pure-JS derivation/signing path (@scure/bip39 + @scure/bip32 + @noble/secp256k1) for iOS only — accepting a parity-test burden against libauth (the reason it was rejected for v1).

Layout

Path Role
src/core-bridge/protocol.ts Wire contract; mirrors src/web/api.ts + session lifecycle
src/core-bridge/NodeCoreClient.ts RN-side client; correlates requests by id (pure, testable)
src/core-bridge/bridge-channel.native.ts Real channel over nodejs-mobile-react-native (device-only)
src/keystore/core-contract.ts Single re-export of src/wallet/keystore.ts
src/keystore/KeyChainKeyStore.ts The KeyStore impl — secret custody + lifecycle; delegates crypto to Node
src/keystore/SecureStore.ts Biometric/PIN secret-custody seam
src/keystore/KeychainSecureStore.ts react-native-keychain impl (device-only)
src/keystore/IdentityCache.ts Non-secret identity cache (AsyncStorage) for locked-view
src/bootstrap.ts Composition point wiring it all together (no UI)
nodejs-assets/nodejs-project/main.js Node bridge entry; rebuilds the src/web/server.ts RunContext per session

KeyStore mapping (src/wallet/keystore.ts)

KeyChainKeyStore implements the cross-shell contract verbatim, including the documented wiring: unlock() returns an UnlockedAccount whose cashaddr, tokenAddress, ownedAddresses and await exportPrivateKey() feed connectWallet(). Mechanics on this platform (ADR-0022 §4): the seed is sealed in the Keychain and never leaves it; derivation runs in the Node core, which returns the derived signing key to RN so exportPrivateKey() yields real bytes. That key is held in memory only while unlocked and zeroed on lock(). A live Node session (currentSessionId()) also exists for executing signed commands over the bridge.

To actually build this (NOT done here — needs a Mac + device/simulator)

  1. npx @react-native-community/cli init DeviantMobile (or fold these src/ files into an existing RN 0.76 app) and copy nodejs-assets/.
  2. yarn add nodejs-mobile-react-native react-native-keychain @react-native-async-storage/async-storage.
  3. Wire the build step that compiles the core (npm run build at repo root → dist/) and copies/bundles it into nodejs-assets/nodejs-project/core/, so main.js's ./core/* imports resolve. Add mainnet-js to the nodejs-project.
  4. Map the cross-project import in src/keystore/core-contract.ts (Metro resolver/watchFolders + tsconfig path) so RN resolves ../../../../src/wallet/keystore.
  5. iOS: cd ios && pod install. Then npx react-native run-android / run-ios.
  6. First verification = the iOS open risk above: on a real iOS device, confirm instantiateSecp256k1() succeeds inside the nodejs-mobile process before building any screens.

Next (only after the runtime path is device-verified)

Build the IA from ../../wiki/wallet-ui-design.md: bottom tab bar (Home / Activity / Swap / Settings; Founder as a 5th tab once a minting NFT is detected), the four V2.7 §9 interfaces, the verification-status surface, the eight-class matrix, D-4/D-5/D-6, tier + verification badges, QR receive scan, deep-linking, preview-before-broadcast on every tx, and app-store packaging.