deviant/snapshots/0074-capacitor-device-build-keystores.md
walerikus 6ea6279d13 docs(snapshots): backfill domain/summary/tags frontmatter + enhance index
Phase 1 of the docs-by-domain reorg. Every snapshot now carries YAML
frontmatter (domain + summary + tags); the auto-guess was hand-corrected
against the full index (T6/interchain -> cross-chain, CDP/MBA/stablecoin
-> fiatm, CitizenToken/Ballot -> governance, etc.). No file renamed/moved.

build-snapshot-index.mjs now reads the frontmatter and emits a greppable
INDEX.md: a top Quick-find table (No | domain | tags | summary) plus
per-domain sections with summary + tags. 224/224 tagged, 8 domains, zero
uncategorised.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 20:12:24 +02:00

3.6 KiB

domain summary tags
wallet Capacitor device build: CapacitorKeyStore

Snapshot 0074 — Capacitor device build: CapacitorKeyStore

Date: 2026-05-29
Track: shells/app (web PWA shell → native device build)
Status: ✅ Typecheck clean, production build passes — device build ready to cap add android/ios

Captured 2026-05-29T20:40:32.192Z

Git state

  • Rev: (no git)
  • Working tree: clean

File counts

Area Files
src/ 57
contracts/ 56
test/ 45

What shipped

Capacitor upgrade: 6.1 → 8.3

shells/app/package.json updated:

  • @capacitor/core ^6.1.0 → ^8.3.4
  • @capacitor/cli ^6.1.0 → ^8.3.4

No native projects exist yet (no android/ or ios/ directories), so the upgrade is a pure package bump — no native code migration needed.

New Capacitor plugins installed

Plugin Version Role
capacitor-secure-storage-plugin ^0.13.0 Vault storage — iOS Keychain / Android EncryptedSharedPreferences
@capgo/capacitor-native-biometric ^8.4.5 Biometric unlock — Face ID / Touch ID / fingerprint with BIOMETRY_ANY hardware access control

CapacitorKeyStore.ts (NEW)

shells/app/src/keystore/CapacitorKeyStore.ts implements the full KeyStore interface:

Vault layer (capacitor-secure-storage-plugin):

  • Mirrors WebKeyStore's Vault structure (AES-GCM / PBKDF2 / base64)
  • ssGet/ssSet/ssRemove async wrappers; ssGet catches "key not found" → null

Biometric layer (@capgo/capacitor-native-biometric):

  • create() / restore(): auto-enrolls biometric PIN using AccessControl.BIOMETRY_ANY hardware-backed credential (server: 'cash.deviant.wallet')
  • unlock() with no secret: triggers NativeBiometric.getSecureCredentials() (native Face ID / Touch ID prompt), retrieves hardware-backed PIN, decrypts vault
  • unlock({ secret }): direct PIN AES-GCM decrypt (identical to WebKeyStore path)
  • destroy(): clears SecureStoragePlugin keys + NativeBiometric credentials

Biometric auto-enroll is non-fatal; PIN always available as fallback.

App.tsx — conditional KeyStore

Uses Capacitor.isNativePlatform() to select CapacitorKeyStore (native) vs WebKeyStore (browser/PWA).

Unlock.tsx — biometric UX on native

  • useEffect auto-prompts biometrics on mount when native
  • Native: prominent "🔐 Use Face ID / Fingerprint" CTA + PIN as secondary
  • Web: PIN-only, unchanged
  • Onboarding + Unlock props changed from WebKeyStore class to KeyStore interface

package.json — native build scripts

Added cap:add:android, cap:add:ios, cap:open:android/ios, cap:run:android/ios.


Build stats

  • TypeScript: 0 errors
  • Vite production build: ✅ 982 modules, JS 2,072 kB (623 kB gzip), CSS 47 kB

Native project bootstrap (manual — requires device SDK)

npm run cap:add:android   # generates android/ (requires Android Studio)
npm run cap:add:ios       # generates ios/ (requires Xcode on macOS)
npm run cap:run:android   # build → sync → run

Capacitor CLI writes USE_BIOMETRIC, USE_FINGERPRINT (Android) and NSFaceIDUsageDescription (iOS) permissions on first cap sync.


What's next

Priority Item
High Generate native projects: cap add android / cap add ios (requires device SDK)
Medium Swap flow: live routeSwap() rate from src/wallet/market.ts
Medium Activity: wire real on-chain event history
Low Code splitting: bring JS chunk below 500 kB warning threshold
Low UX polish: swap asset-picker sheet; larger QR code in Receive