mirror of
https://github.com/selfxyz/self.git
synced 2026-04-27 03:01:15 -04:00
Add engineering standards for coding agents (#1789)
* add purple md files * update rules * udpates * last update
This commit is contained in:
30
purple/business/core-value-prop.md
Normal file
30
purple/business/core-value-prop.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Core Value Proposition
|
||||
|
||||
## Problem
|
||||
|
||||
Identity verification today requires users to hand over sensitive personal data (passport scans, selfies, addresses) to centralized services. This data is stored, shared, and often breached. Users have no control over what's revealed or how it's used.
|
||||
|
||||
## Solution: Privacy-First Identity
|
||||
|
||||
Self lets users **prove identity attributes without revealing the underlying data**. Using zero-knowledge proofs:
|
||||
|
||||
- Prove you're over 18 **without revealing your date of birth**
|
||||
- Prove your nationality **without revealing your passport number**
|
||||
- Pass OFAC sanctions screening **without exposing your name**
|
||||
- Complete KYC requirements **without uploading documents to a server**
|
||||
|
||||
## What Makes Self Different
|
||||
|
||||
| Traditional KYC | Self |
|
||||
|-----------------|------|
|
||||
| Upload passport photo to a server | Scan NFC chip locally on your device |
|
||||
| Company stores your data | No data leaves your device (ZK proofs only) |
|
||||
| Full identity revealed to verifier | Selective disclosure — reveal only what's needed |
|
||||
| Trust the company with your data | Cryptographic verification — trust math, not companies |
|
||||
| Re-verify for every new service | Prove once, reuse proof across services |
|
||||
|
||||
## Target Users
|
||||
|
||||
- **End users**: Anyone who needs to verify their identity online
|
||||
- **Businesses**: Companies that need KYC/age/identity verification for their users
|
||||
- **Developers**: Build identity verification into their apps via the Self SDK
|
||||
42
purple/business/general-description.md
Normal file
42
purple/business/general-description.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# General Description
|
||||
|
||||
## What is Self?
|
||||
|
||||
Self is an identity verification wallet. Users scan their passport or ID card via NFC, and the app generates zero-knowledge proofs that verify identity attributes without revealing the underlying data.
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
1. User scans passport/ID (NFC chip)
|
||||
2. App extracts identity data locally
|
||||
3. ZK proof generated (proves attributes without revealing raw data)
|
||||
4. Proof verified on-chain via smart contracts
|
||||
5. Third-party apps verify identity via SDK integration
|
||||
```
|
||||
|
||||
## Product Surfaces
|
||||
|
||||
| Surface | Description |
|
||||
|---------|-------------|
|
||||
| **Self Wallet** | Mobile app (iOS/Android) — user-facing identity wallet |
|
||||
| **Self SDK** | Embeddable SDK for third-party apps to request and verify identity |
|
||||
| **Smart Contracts** | On-chain verification infrastructure |
|
||||
| **KYC Service** | Full KYC coverage via third-party provider for 100% document support |
|
||||
|
||||
## Supported Documents
|
||||
|
||||
| Document | Method | Coverage |
|
||||
|----------|--------|----------|
|
||||
| E-Passport | NFC chip reading | ICAO-compliant countries |
|
||||
| EU ID Card | NFC chip reading | EU member states |
|
||||
| Aadhaar | Special handling | India |
|
||||
| KYC (all others) | Third-party provider | Global (100% coverage) |
|
||||
|
||||
## Key Capabilities
|
||||
|
||||
- **Selective disclosure**: Reveal only specific attributes (age, nationality, name)
|
||||
- **Privacy-preserving**: Zero-knowledge proofs — verifier never sees raw data
|
||||
- **On-chain verification**: Cryptographic proof verified by smart contracts
|
||||
- **OFAC screening**: Sanctions list checking without exposing identity
|
||||
- **Age verification**: Prove "older than X" without revealing date of birth
|
||||
- **Country restrictions**: Verify nationality without revealing passport data
|
||||
10
purple/business/pricing-model.md
Normal file
10
purple/business/pricing-model.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Pricing Model
|
||||
|
||||
## Overview
|
||||
|
||||
- **Users**: Free. The Self Wallet app is free to download and use.
|
||||
- **Businesses/Enterprise**: SDK integration fees for third-party apps that verify identity via Self.
|
||||
|
||||
## Details
|
||||
|
||||
Pricing specifics for SDK usage are subject to change. Refer to current sales/business documentation for up-to-date pricing tiers.
|
||||
119
purple/crypto/sdk-architecture.md
Normal file
119
purple/crypto/sdk-architecture.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# SDK Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
Three-layer architecture: platform-agnostic core SDK → WebView bridge protocol → native shells (React Native / KMP).
|
||||
|
||||
## Architecture Diagram
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ Native Shell (rn-sdk or kmp-sdk) │
|
||||
│ ├── Hosts WebView │
|
||||
│ ├── MessageRouter routes bridge requests │
|
||||
│ └── Handlers: NFC, Camera, Biometrics, Keychain │
|
||||
└──────────────────────┬───────────────────────────────┘
|
||||
│ postMessage / evaluateJS
|
||||
┌──────────────────────▼───────────────────────────────┐
|
||||
│ WebView Bridge (webview-bridge) │
|
||||
│ ├── JSON request/response/event protocol (v1) │
|
||||
│ ├── 10 bridge domains │
|
||||
│ └── Transport detection: KMP iOS/Android, RN WebView│
|
||||
└──────────────────────┬───────────────────────────────┘
|
||||
│ adapter interfaces
|
||||
┌──────────────────────▼───────────────────────────────┐
|
||||
│ Core SDK (mobile-sdk-alpha) │
|
||||
│ ├── SelfClient (created via createSelfClient()) │
|
||||
│ ├── Adapter pattern (all platform code abstracted) │
|
||||
│ ├── XState proving machine │
|
||||
│ └── Zustand state stores │
|
||||
└──────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌──────────────────────▼───────────────────────────────┐
|
||||
│ WebView App (webview-app) │
|
||||
│ ├── React + React Router SPA │
|
||||
│ ├── Vite build │
|
||||
│ └── Renders inside WebView │
|
||||
└──────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Adapter Pattern
|
||||
|
||||
The SDK requires adapters for all platform-specific operations. Core logic never imports platform-specific modules directly.
|
||||
|
||||
### Required Adapters
|
||||
|
||||
| Adapter | Interface | Purpose |
|
||||
|---------|-----------|---------|
|
||||
| `auth` | `AuthAdapter` | Private key access from secure storage |
|
||||
| `scanner` | `NFCScannerAdapter` | NFC/MRZ document scanning |
|
||||
| `network` | `NetworkAdapter` | HTTP + WebSocket (proving) |
|
||||
| `crypto` | `CryptoAdapter` | Hashing and signing |
|
||||
| `documents` | `DocumentsAdapter` | Document catalog persistence |
|
||||
| `navigation` | `NavigationAdapter` | Screen routing (17 predefined routes) |
|
||||
|
||||
### Optional Adapters
|
||||
|
||||
| Adapter | Interface | Purpose |
|
||||
|---------|-----------|---------|
|
||||
| `analytics` | `AnalyticsAdapter` | Event tracking |
|
||||
| `storage` | `StorageAdapter` | Key-value storage |
|
||||
| `clock` | `ClockAdapter` | Time utilities |
|
||||
| `logger` | `LoggerAdapter` | Structured logging |
|
||||
|
||||
## WebView Bridge Protocol
|
||||
|
||||
Message format (JSON over postMessage):
|
||||
|
||||
```
|
||||
Request: { type: 'request', domain, method, params, id, timestamp }
|
||||
Response: { type: 'response', domain, requestId, success, data/error, id, timestamp }
|
||||
Event: { type: 'event', domain, event, data, id, timestamp }
|
||||
```
|
||||
|
||||
### Bridge Domains (10)
|
||||
|
||||
| Domain | Methods |
|
||||
|--------|---------|
|
||||
| `nfc` | scan, cancelScan, isSupported |
|
||||
| `biometrics` | authenticate, isAvailable, getBiometryType |
|
||||
| `secureStorage` | get, set, remove |
|
||||
| `camera` | scanMRZ, isAvailable |
|
||||
| `crypto` | sign, generateKey, getPublicKey |
|
||||
| `haptic` | trigger |
|
||||
| `analytics` | trackEvent, trackNfcEvent, logNfcEvent |
|
||||
| `lifecycle` | ready, dismiss, setResult |
|
||||
| `documents` | loadCatalog, saveCatalog, loadById, save, delete |
|
||||
| `navigation` | goBack, goTo |
|
||||
|
||||
### Transport Detection (Priority Order)
|
||||
|
||||
1. Android KMP: `globalThis.SelfNativeAndroid.postMessage()`
|
||||
2. iOS KMP: `window.webkit.messageHandlers.SelfNativeIOS.postMessage()`
|
||||
3. React Native WebView: `window.ReactNativeWebView.postMessage()`
|
||||
|
||||
## Package Build Strategy
|
||||
|
||||
- **mobile-sdk-alpha**: Dual ESM/CJS via tsup. Separate entry points for `react-native` and `browser` (package.json `exports` conditions)
|
||||
- **webview-bridge**: Dual ESM/CJS via tsup
|
||||
- **webview-app**: Vite bundle (loaded into WebView)
|
||||
- **rn-sdk**: React Native package (hosts WebView + message router)
|
||||
|
||||
## DOs
|
||||
|
||||
- DO implement all 6 required adapters when creating a new native shell
|
||||
- DO use `createSelfClient()` to initialize the SDK (validates adapters)
|
||||
- DO use `createReactNativeAdapters()` as the factory for RN platforms
|
||||
- DO keep all platform-specific code in `src/adapters/{platform}/`
|
||||
- DO use the bridge protocol version constant (`BRIDGE_PROTOCOL_VERSION`)
|
||||
- DO build new native features as bridge domain handlers, not direct native modules
|
||||
- DO support AbortSignal in scanner adapters for user cancellation
|
||||
|
||||
## DON'Ts
|
||||
|
||||
- DON'T import `react-native` anywhere in `mobile-sdk-alpha/src/` except in `src/adapters/react-native/`
|
||||
- DON'T add new platform-specific adapters without defining the interface in `types/public.ts` first
|
||||
- DON'T bypass the bridge protocol for native communication
|
||||
- DON'T persist private keys in the SDK — the app manages key lifecycle via `AuthAdapter`
|
||||
- DON'T use web fallbacks for secure storage in production
|
||||
- DON'T add new bridge domains without updating both the bridge schema and native handlers
|
||||
90
purple/crypto/smart-contracts.md
Normal file
90
purple/crypto/smart-contracts.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Smart Contracts
|
||||
|
||||
## Overview
|
||||
|
||||
Solidity smart contracts for on-chain identity verification using zero-knowledge proofs. Deployed via Hardhat with UUPS upgradeable proxy pattern.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
IdentityVerificationHub (ERC1967 Proxy)
|
||||
│
|
||||
▼
|
||||
IdentityVerificationHubImplV2 (UUPS Implementation)
|
||||
│
|
||||
├── Registries (per attestation type)
|
||||
│ ├── IdentityRegistry (E-Passport)
|
||||
│ ├── IdentityRegistryIdCard (EU-ID)
|
||||
│ ├── IdentityRegistryAadhaar
|
||||
│ └── IdentityRegistryKyc
|
||||
│
|
||||
├── Register Verifiers (32 variants)
|
||||
│ └── One per hash/signature algorithm combo
|
||||
│
|
||||
├── DSC Verifiers (24 variants)
|
||||
│ └── Document Signing Certificate chain verification
|
||||
│
|
||||
└── Disclose Verifiers (4 variants)
|
||||
└── Selective attribute disclosure
|
||||
```
|
||||
|
||||
## Contract Patterns
|
||||
|
||||
- **Proxy**: ERC1967 via `ProxyRoot` (extends `ERC1967Proxy`)
|
||||
- **Upgradeable**: UUPS via `ImplRoot` (extends `UUPSUpgradeable + AccessControlUpgradeable`)
|
||||
- **Storage**: ERC-7201 namespaced storage (V2 contracts only)
|
||||
- **Governance**: Two roles — `SECURITY_ROLE` (upgrade authority) and `OPERATIONS_ROLE` (configuration)
|
||||
|
||||
## Attestation Types
|
||||
|
||||
| ID | Type | Constant |
|
||||
|----|------|----------|
|
||||
| 1 | E-Passport | `AttestationId.E_PASSPORT` |
|
||||
| 2 | EU ID Card | `AttestationId.EU_ID_CARD` |
|
||||
| 3 | Aadhaar | `AttestationId.AADHAAR` |
|
||||
| 4 | KYC | `AttestationId.KYC` |
|
||||
|
||||
## Deployment
|
||||
|
||||
- **Tool**: Hardhat Ignition (declarative deployment modules)
|
||||
- **Modules location**: `contracts/ignition/modules/`
|
||||
- **Networks**: Hardhat (local), Sepolia (testnet), Ethereum mainnet, Celo, Celo-Sepolia
|
||||
- **Compiler**: Solidity 0.8.28, EVM target: Cancun, optimizer: 200 runs
|
||||
|
||||
## Key Libraries
|
||||
|
||||
| Library | Purpose |
|
||||
|---------|---------|
|
||||
| `SelfStructs` | Shared data structures |
|
||||
| `CustomVerifier` | Proof verification logic |
|
||||
| `Formatter` / `GenericFormatter` | Data packing/unpacking |
|
||||
| `CircuitAttributeHandler(V2)` | Attribute extraction |
|
||||
| `PoseidonT3` | Merkle tree hashing |
|
||||
| `RootCheckLib` | Merkle root validation |
|
||||
| `OfacCheckLib` | Sanctions screening |
|
||||
|
||||
## Testing
|
||||
|
||||
- Framework: Hardhat (Mocha + Chai + ethers.js)
|
||||
- Pattern: `deploySystemFixtures()` + EVM snapshots for isolation
|
||||
- Location: `contracts/test/unit/`, `contracts/test/integration/`, `contracts/test/v2/`
|
||||
- Run: `yarn test` (all), `yarn test:hub` (hub only), `yarn test:coverage`
|
||||
|
||||
## DOs
|
||||
|
||||
- DO use Hardhat Ignition modules for deployments
|
||||
- DO use ERC-7201 namespaced storage for new contract state
|
||||
- DO extend `ImplRoot` for new upgradeable implementations
|
||||
- DO use `ProxyRoot` for new proxy contracts
|
||||
- DO use `deploySystemFixtures()` for test setup with EVM snapshots
|
||||
- DO add verifier contracts for new hash/signature algorithm combinations
|
||||
- DO use the `SelfStructs` library for shared data types
|
||||
|
||||
## DON'Ts
|
||||
|
||||
- DON'T modify V1 storage layout — it's frozen (causes catastrophic storage collisions)
|
||||
- DON'T deploy directly — always use Ignition modules
|
||||
- DON'T bypass UUPS upgrade authorization
|
||||
- DON'T use `allowUnlimitedContractSize` outside of local development
|
||||
- DON'T hardcode network addresses — use environment variables and deployment artifacts
|
||||
- DON'T create new proxy patterns — reuse `ProxyRoot`
|
||||
105
purple/crypto/zk-circuits.md
Normal file
105
purple/crypto/zk-circuits.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# Zero-Knowledge Circuits
|
||||
|
||||
## Overview
|
||||
|
||||
ZK circuits for identity verification using passport NFC data. Circom is the primary framework (production). Noir is in development.
|
||||
|
||||
## Circuit Types
|
||||
|
||||
| Type | Purpose | Variants |
|
||||
|------|---------|----------|
|
||||
| Register | User registration — verify passport signature, generate commitment | 32 |
|
||||
| Disclose | Selective attribute disclosure with age/country checks | 4 (passport, id_card, aadhaar, kyc) |
|
||||
| DSC | Document Signing Certificate chain verification | 24 |
|
||||
| OFAC | Sanctions screening via Sparse Merkle Trees | 3 (name, name+DOB, passport#) |
|
||||
| GCP JWT | Google Cloud attestation verification | 1 |
|
||||
|
||||
## Naming Convention
|
||||
|
||||
```
|
||||
{circuit_type}_{dg_hash}_{econtent_hash}_{sig_hash}_{algorithm}_{exponent}_{keysize}
|
||||
|
||||
Example: register_sha256_sha256_sha256_rsa_65537_4096
|
||||
```
|
||||
|
||||
## Supported Algorithm Combinations
|
||||
|
||||
- **RSA**: Key sizes 2048, 3072, 4096. Exponent 65537.
|
||||
- **ECDSA curves**: secp256r1, secp384r1, secp521r1, brainpoolP224/256/384/512r1
|
||||
- **RSA-PSS**: Various exponents and salt lengths
|
||||
- **Hash functions**: SHA1, SHA224, SHA256, SHA384, SHA512
|
||||
|
||||
## Circuit Flow
|
||||
|
||||
```
|
||||
NFC Passport Data
|
||||
│
|
||||
▼
|
||||
Register Circuit DSC Circuit
|
||||
├── Verify passport signature ├── Verify DSC→CSCA chain
|
||||
├── Check DSC in merkle tree └── Output: dsc_tree_leaf
|
||||
├── Output: commitment, nullifier
|
||||
│
|
||||
▼
|
||||
Disclose Circuit
|
||||
├── Prove commitment knowledge
|
||||
├── Check passport expiry
|
||||
├── Age verification
|
||||
├── Country restrictions
|
||||
├── OFAC screening (SMT proofs)
|
||||
└── Output: selectively revealed data
|
||||
```
|
||||
|
||||
## Build Pipeline
|
||||
|
||||
1. Compile Circom → R1CS + WASM
|
||||
2. Trusted setup with Powers of Tau (PTAU)
|
||||
3. Generate proving key (zkey)
|
||||
4. Export verification key
|
||||
5. Generate Solidity verifier → deployed on-chain
|
||||
|
||||
## Proving Architecture
|
||||
|
||||
- **Mobile**: Document validation + circuit input generation
|
||||
- **TEE**: Remote proof computation over WebSocket (AES-256-GCM encrypted)
|
||||
- **On-chain**: Solidity verifier contract validates proof
|
||||
|
||||
Timing estimates:
|
||||
- RSA (2048-4096): 2-6 seconds
|
||||
- ECDSA (256-384): 25-100 seconds
|
||||
- ECDSA (512-521): 100-200+ seconds
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
circuits/
|
||||
├── circuits/
|
||||
│ ├── register/instances/ # 32 parametrized register circuits
|
||||
│ ├── disclose/ # 4 disclose variants
|
||||
│ ├── dsc/instances/ # 24 DSC circuits
|
||||
│ └── utils/ # Shared Circom templates
|
||||
├── tests/ # ts-mocha + circom_tester
|
||||
└── scripts/build/ # Build automation
|
||||
|
||||
noir/crates/ # Noir circuits (in development)
|
||||
├── dg1/ # DG1 verification
|
||||
├── econtent/ # eContent hashing
|
||||
└── ofac/ # OFAC checks
|
||||
```
|
||||
|
||||
## DOs
|
||||
|
||||
- DO follow the naming convention: `{type}_{dg}_{econtent}_{sig}_{algo}_{exp}_{keysize}`
|
||||
- DO add new circuit instances as minimal files that include the main template with parameters
|
||||
- DO generate Solidity verifiers from compiled circuits
|
||||
- DO test circuits with `circom_tester` (WASM backend) before deployment
|
||||
- DO use Poseidon hashing for all in-circuit hash operations
|
||||
- DO keep the Noir codebase in sync with Circom circuit functionality
|
||||
|
||||
## DON'Ts
|
||||
|
||||
- DON'T modify shared Circom templates without testing all dependent instances
|
||||
- DON'T skip the trusted setup phase for new circuits
|
||||
- DON'T expose circuit witness data outside the proving environment
|
||||
- DON'T hard-code merkle tree depths — use parametrized values
|
||||
- DON'T deploy verifiers without testing the full prove-then-verify pipeline
|
||||
69
purple/frontend/components.md
Normal file
69
purple/frontend/components.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Components
|
||||
|
||||
## Design System: Euclid
|
||||
|
||||
**Euclid** (`@selfxyz/euclid`) is the canonical component library. All new UI should pull from Euclid first.
|
||||
|
||||
| Package | Platform | Usage |
|
||||
|---------|----------|-------|
|
||||
| `@selfxyz/euclid` | React Native | App and mobile-sdk-alpha |
|
||||
| `@selfxyz/euclid-core` | Shared | Design tokens, types |
|
||||
| `@selfxyz/euclid-web` | Browser/WebView | webview-app |
|
||||
|
||||
Euclid provides screen-level components (CountryPickerScreen, RecoveryPhraseScreen, HomeScreen, etc.), icons, colors, and shared UI patterns.
|
||||
|
||||
## Component Architecture
|
||||
|
||||
```
|
||||
@selfxyz/euclid(-web/-core) ← Design system (source of truth)
|
||||
│
|
||||
▼
|
||||
mobile-sdk-alpha/src/flows/ ← SDK flow screens (wrap Euclid)
|
||||
│
|
||||
▼
|
||||
app/src/screens/ ← App screens (consume SDK or Euclid directly)
|
||||
│
|
||||
▼
|
||||
app/src/components/ ← App-specific components (only if not in Euclid)
|
||||
```
|
||||
|
||||
## Component Patterns
|
||||
|
||||
- **Functional components** with TypeScript interfaces for props
|
||||
- **Tamagui primitives** as base building blocks: `XStack`, `YStack`, `View`, `Text`
|
||||
- **Feature-based organization**: Group components by domain (homescreen, navbar, documents, etc.)
|
||||
- **Provider nesting**: 12-deep provider tree wrapping the navigation root
|
||||
|
||||
## State Management in Components
|
||||
|
||||
| Pattern | When to Use |
|
||||
|---------|-------------|
|
||||
| Zustand stores | Global app state (user, settings, proof history) |
|
||||
| XState machines | Complex workflows (proving, onboarding) |
|
||||
| React Context | Service injection (auth, database, SDK client) |
|
||||
| Local useState | Component-specific UI state |
|
||||
|
||||
## Hook Patterns
|
||||
|
||||
- 29+ custom hooks in `app/src/hooks/`
|
||||
- Complex flows use a **callback registry** pattern for navigation with state
|
||||
- Responsive hooks: `useCardDimensions()`, `useWindowDimensions()`
|
||||
- Platform hooks: `useAppUpdates()` (native) / `useAppUpdates.web.ts` (web stub)
|
||||
|
||||
## DOs
|
||||
|
||||
- DO use Euclid components before creating custom ones
|
||||
- DO import colors and icons from Euclid (or from `mobile-sdk-alpha/constants/colors`)
|
||||
- DO use functional components with TypeScript prop interfaces
|
||||
- DO separate business logic into hooks; keep components focused on rendering
|
||||
- DO use the provider pattern for cross-cutting concerns
|
||||
- DO use Tamagui primitives (XStack, YStack, Text) as layout building blocks
|
||||
|
||||
## DON'Ts
|
||||
|
||||
- DON'T create new components that duplicate Euclid screens or patterns
|
||||
- DON'T put business logic directly in component bodies — extract to hooks
|
||||
- DON'T use class components
|
||||
- DON'T hardcode colors — use Euclid tokens or SDK color constants
|
||||
- DON'T create deeply nested prop drilling — use context providers or Zustand
|
||||
- DON'T import from Euclid dist paths directly (e.g., `@selfxyz/euclid/dist/...`) unless the component isn't re-exported at the package root
|
||||
73
purple/frontend/responsiveness.md
Normal file
73
purple/frontend/responsiveness.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# Responsiveness
|
||||
|
||||
## Platform Targets
|
||||
|
||||
| Platform | Build | Rendering |
|
||||
|----------|-------|-----------|
|
||||
| iOS | React Native (Metro) | Native views |
|
||||
| Android | React Native (Metro) | Native views |
|
||||
| Web | Vite (React Native Web) | DOM |
|
||||
| WebView | Vite (embedded) | DOM inside native shell |
|
||||
|
||||
## Platform-Specific Files
|
||||
|
||||
Use React Native's platform extension system for divergent implementations:
|
||||
|
||||
```
|
||||
component.tsx ← Default (shared)
|
||||
component.web.tsx ← Web override
|
||||
component.native.tsx ← Native override (iOS + Android)
|
||||
component.ios.tsx ← iOS-only override
|
||||
component.android.tsx ← Android-only override
|
||||
```
|
||||
|
||||
Existing platform-specific files:
|
||||
- `authProvider.tsx` / `authProvider.web.tsx` — Auth (keychain vs stub)
|
||||
- `database.ts` / `database.web.ts` — SQLite vs stub
|
||||
- `sentry.ts` / `sentry.web.ts` — Error tracking config
|
||||
- `useAppUpdates.ts` / `useAppUpdates.web.ts` — Update checking
|
||||
- `SvgXmlWrapper.native.tsx` / `SvgXmlWrapper.web.tsx` — SVG rendering
|
||||
|
||||
## Responsive Layout
|
||||
|
||||
### Safe Areas
|
||||
```
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
const { top, bottom } = useSafeAreaInsets();
|
||||
// Apply as padding: paddingBottom={bottom + extraPadding}
|
||||
```
|
||||
|
||||
### Responsive Sizing
|
||||
```
|
||||
import { Dimensions } from 'react-native';
|
||||
const { width, height } = Dimensions.get('window');
|
||||
const scale = measuredWidth / FIGMA_REFERENCE_WIDTH;
|
||||
```
|
||||
|
||||
### Figma-Based Scaling
|
||||
Components use a scale factor derived from Figma reference dimensions:
|
||||
- `useCardDimensions()` — scales card sizes to screen width
|
||||
- Reference: `FIGMA_CARD_WIDTH` constant as baseline
|
||||
|
||||
## Navigation Differences
|
||||
|
||||
```
|
||||
Platform.OS === 'web' ? 'Home' : 'Splash' // Web skips splash
|
||||
```
|
||||
|
||||
## DOs
|
||||
|
||||
- DO use platform file extensions (`.web.ts`, `.native.tsx`) for divergent implementations
|
||||
- DO use `useSafeAreaInsets()` for all edge-to-edge screens
|
||||
- DO use `Dimensions.get('window')` for responsive sizing calculations
|
||||
- DO test on both iOS and Android — shadows, elevation, and fonts differ
|
||||
- DO provide web stubs for native-only features (NFC, biometrics, keychain)
|
||||
- DO use Figma-based scale factors for pixel-perfect layout
|
||||
|
||||
## DON'Ts
|
||||
|
||||
- DON'T use `Platform.select()` for large code divergences — use platform files instead
|
||||
- DON'T hardcode dimensions in pixels — derive from screen width or Figma references
|
||||
- DON'T assume safe area insets are zero — always account for notches and home indicators
|
||||
- DON'T use web-only APIs (localStorage, window.location) without platform checks
|
||||
- DON'T forget to test the web build (`yarn web`) — it shares the same codebase
|
||||
78
purple/frontend/styling.md
Normal file
78
purple/frontend/styling.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Styling
|
||||
|
||||
## Styling Hierarchy
|
||||
|
||||
Use the highest-priority approach that fits:
|
||||
|
||||
```
|
||||
1. Euclid components ← Preferred: pre-styled, standardized
|
||||
2. Tamagui inline props ← Atomic styles on primitives
|
||||
3. Tamagui styled() ← Reusable styled components
|
||||
4. StyleSheet.create ← Platform-specific or complex styles
|
||||
```
|
||||
|
||||
## Tamagui Configuration
|
||||
|
||||
Config: `app/tamagui.config.ts`
|
||||
|
||||
### Fonts
|
||||
|
||||
| Token | Font | Usage |
|
||||
|-------|------|-------|
|
||||
| `advercase` | Advercase-Regular | Display headlines |
|
||||
| `dinot` | DINOT-Medium | Body text, UI labels |
|
||||
| `plexMono` | IBMPlexMono-Regular | Code, technical content |
|
||||
|
||||
Import via: `import { advercase, dinot } from '@selfxyz/mobile-sdk-alpha/constants/fonts'`
|
||||
|
||||
### Font Sizes (Tamagui tokens)
|
||||
|
||||
```
|
||||
1: 12 2: 14 3: 15 4: 16 5: 18 6: 20
|
||||
7: 24 8: 28 9: 32 10: 40 11: 52 12: 62
|
||||
```
|
||||
|
||||
## Colors
|
||||
|
||||
Import from SDK constants:
|
||||
```
|
||||
import { black, white, cyan300, slate400, zinc900 } from '@selfxyz/mobile-sdk-alpha/constants/colors';
|
||||
```
|
||||
|
||||
Or from Euclid: `import { colors } from '@selfxyz/euclid-web'`
|
||||
|
||||
## Shadows (Cross-Platform)
|
||||
|
||||
```
|
||||
shadowColor={black}
|
||||
shadowOffset={{ width: 0, height: 4 }}
|
||||
shadowOpacity={0.2}
|
||||
shadowRadius={12}
|
||||
elevation={8} // Android shadow equivalent
|
||||
```
|
||||
|
||||
## Animations
|
||||
|
||||
| Library | Usage |
|
||||
|---------|-------|
|
||||
| Lottie (`lottie-react-native`) | Complex animations, loading states |
|
||||
| Tamagui animations (`@tamagui/animations-react-native`) | Transitions, micro-interactions |
|
||||
|
||||
Use the SDK's `DelayedLottieView` wrapper for Lottie animations.
|
||||
|
||||
## DOs
|
||||
|
||||
- DO use Euclid/SDK color constants — never hardcode hex values
|
||||
- DO use Tamagui font tokens (advercase, dinot, plexMono) — never raw font family strings
|
||||
- DO use Tamagui inline props for simple atomic styles (padding, gap, flex)
|
||||
- DO use `styled()` for components reused in multiple places
|
||||
- DO use `StyleSheet.create` only when Tamagui can't express the style (complex shadows, platform quirks)
|
||||
- DO use Lottie via `DelayedLottieView` for loading and success animations
|
||||
|
||||
## DON'Ts
|
||||
|
||||
- DON'T mix styling approaches unnecessarily in a single component
|
||||
- DON'T use raw hex colors — use named constants
|
||||
- DON'T use React Native Animated API directly — use Tamagui animations or Lottie
|
||||
- DON'T create global stylesheet files — keep styles co-located with components
|
||||
- DON'T override Euclid component styles unless absolutely necessary
|
||||
101
purple/global/authentication.md
Normal file
101
purple/global/authentication.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# Authentication
|
||||
|
||||
## Overview
|
||||
|
||||
Mnemonic-based wallet authentication secured by device biometrics. No traditional session tokens or server-side auth. The mnemonic is the root of identity — all keys are derived from it.
|
||||
|
||||
## Auth Flow
|
||||
|
||||
```
|
||||
User opens app
|
||||
│
|
||||
▼
|
||||
Biometric prompt (fingerprint / Face ID / passcode fallback)
|
||||
│
|
||||
▼
|
||||
Keychain unlocks → mnemonic accessible
|
||||
│
|
||||
▼
|
||||
Session active (15-minute timeout)
|
||||
│
|
||||
▼
|
||||
Any secret access → _getSecurely() or _getWithBiometrics()
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
- `AuthProvider` (React Context) wraps the entire app
|
||||
- Biometric login via `react-native-biometrics` with `allowDeviceCredentials: true`
|
||||
- Mnemonic stored in native keychain via `react-native-keychain`
|
||||
- Adaptive security: automatically uses the device's highest available security level
|
||||
- Session timeout: 15 minutes of inactivity → requires re-authentication
|
||||
|
||||
## Key Derivation
|
||||
|
||||
```
|
||||
BIP39 Mnemonic
|
||||
│
|
||||
├── m/44'/60'/0'/0/0 → Primary wallet (main account)
|
||||
└── m/44'/60'/0'/0/1 → Points wallet (secondary)
|
||||
```
|
||||
|
||||
## SDK Auth Adapter
|
||||
|
||||
The mobile SDK defines a minimal `AuthAdapter` interface:
|
||||
|
||||
```
|
||||
AuthAdapter {
|
||||
getPrivateKey(): Promise<string | null>
|
||||
}
|
||||
```
|
||||
|
||||
Platform adapters implement this — React Native uses keychain, web uses volatile in-memory storage (development only).
|
||||
|
||||
## External Auth (Third-Party Services Only)
|
||||
|
||||
| Service | Method | Purpose | Token Persistence |
|
||||
|----------------|-------------|----------------------|-------------------|
|
||||
| Google Drive | OAuth 2.0 | Mnemonic backup | Per-session |
|
||||
| Sumsub KYC | API token | Identity verification | Per-session |
|
||||
| Turnkey | Google OAuth | Wallet backup | DISABLED |
|
||||
|
||||
## Keychain Security Levels (Adaptive)
|
||||
|
||||
```
|
||||
Device has secure hardware? → SECURE_HARDWARE + BIOMETRY_ANY_OR_DEVICE_PASSCODE
|
||||
Device has biometrics only? → SECURE_SOFTWARE + BIOMETRY_ANY_OR_DEVICE_PASSCODE
|
||||
Device has passcode only? → ANY + DEVICE_PASSCODE
|
||||
None available? → BLOCKED (do not store secrets)
|
||||
```
|
||||
|
||||
> When the device has no passcode or biometrics, secrets are not protected at rest. The app must warn the user and block sensitive operations (proving, key derivation) until device security is configured.
|
||||
|
||||
Migration function `migrateToSecureKeychain()` upgrades old entries to the highest available level.
|
||||
|
||||
## DOs
|
||||
|
||||
- DO use `_getSecurely()` for ALL access to keychain-stored secrets
|
||||
- DO check biometric availability before prompting
|
||||
- DO use the adaptive keychain configuration from `integrations/keychain/`
|
||||
- DO track auth events via the analytics service (AuthEvents constants)
|
||||
- DO handle keychain errors by category (user cancellation vs crypto failure)
|
||||
- DO use the `AuthAdapter` interface for SDK-level auth (not direct keychain access)
|
||||
|
||||
## DON'Ts
|
||||
|
||||
- DON'T store mnemonics, private keys, or secrets in AsyncStorage or Zustand
|
||||
- DON'T create web fallbacks for keychain — native keychain is a security boundary
|
||||
- DON'T transmit the mnemonic over the network
|
||||
- DON'T bypass biometric auth to access secrets
|
||||
- DON'T use OAuth tokens for core identity — they're only for third-party services
|
||||
- DON'T store sensitive auth state in persisted Zustand stores (flags only, not secrets)
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `app/src/providers/authProvider.tsx` | Core biometric + keychain auth |
|
||||
| `app/src/providers/authProvider.web.tsx` | Web stub (incomplete) |
|
||||
| `app/src/integrations/keychain/index.ts` | Adaptive security config |
|
||||
| `app/src/utils/keychainErrors.ts` | Error categorization |
|
||||
| `packages/mobile-sdk-alpha/src/adapters/react-native/auth.ts` | SDK auth adapter |
|
||||
73
purple/global/code-style.md
Normal file
73
purple/global/code-style.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# Code Style
|
||||
|
||||
## Formatting (Prettier)
|
||||
|
||||
- Single quotes (double quotes in YAML)
|
||||
- 2-space indentation, no tabs
|
||||
- Trailing commas everywhere
|
||||
- Semicolons always
|
||||
- Arrow parens: avoid (omit when single param)
|
||||
- Bracket spacing: yes
|
||||
- Bracket same line: no (JSX closing bracket on new line)
|
||||
- End of line: auto
|
||||
|
||||
## TypeScript
|
||||
|
||||
- Strict mode enabled
|
||||
- Path aliases: `@/` maps to `./src/`, `@selfxyz/*` for workspace packages
|
||||
- Use `type` keyword for type-only imports (enforced: `consistent-type-imports`)
|
||||
- Use `type` keyword for type-only exports (enforced: `consistent-type-exports`)
|
||||
- `no-explicit-any`: warn (avoid but don't block builds)
|
||||
- `no-require-imports`: error (use ES imports)
|
||||
|
||||
## Import Order (Enforced via `simple-import-sort`)
|
||||
|
||||
```
|
||||
1. Node.js built-ins import fs from 'node:fs';
|
||||
2. External packages import { View } from 'react-native';
|
||||
3. Workspace packages import { utils } from '@selfxyz/common';
|
||||
4. Internal alias imports import { Button } from '@/components/Button';
|
||||
5. Relative imports import { helper } from './utils';
|
||||
```
|
||||
|
||||
Blank line between each group. No duplicate imports.
|
||||
|
||||
## Export Rules
|
||||
|
||||
- Exports sorted alphabetically, types first (`sort-exports/sort-exports`)
|
||||
- `export *` is **banned** — use selective named exports for tree shaking
|
||||
|
||||
## License Headers
|
||||
|
||||
Every source file requires the BUSL-1.1 license header:
|
||||
|
||||
```
|
||||
// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
```
|
||||
|
||||
Enforced via `scripts/check-license-headers.mjs`, not ESLint. Run `yarn lint:headers:fix` to auto-add.
|
||||
|
||||
## Dead Code
|
||||
|
||||
- Knip configured at workspace root for dead code detection
|
||||
- Remove unused exports, files, and dependencies proactively
|
||||
|
||||
## DOs
|
||||
|
||||
- DO use `type` imports/exports for types: `import type { Foo } from './types'`
|
||||
- DO use selective named exports: `export { foo, bar } from './module'`
|
||||
- DO follow the 5-group import order
|
||||
- DO run `yarn lint` before committing
|
||||
- DO add license headers to new files
|
||||
- DO use `@/` alias for imports within the app package
|
||||
|
||||
## DON'Ts
|
||||
|
||||
- DON'T use `export *` — it kills tree shaking
|
||||
- DON'T use `require()` in TypeScript source (only allowed in tests and CJS configs)
|
||||
- DON'T use `require('react')` or `require('react-native')` in tests (causes OOM in CI)
|
||||
- DON'T use tabs or inconsistent formatting — Prettier handles this
|
||||
- DON'T disable ESLint rules without justification in a comment
|
||||
- DON'T use relative paths to import from other workspace packages — use `@selfxyz/*`
|
||||
68
purple/global/database.md
Normal file
68
purple/global/database.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Database & Persistence
|
||||
|
||||
## Overview
|
||||
|
||||
No traditional backend database. On-chain smart contracts are the source of truth. Local persistence uses three tiers based on data sensitivity.
|
||||
|
||||
## Storage Tiers
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ Tier 1: Keychain (Secrets) │
|
||||
│ ├── Mnemonic phrase │
|
||||
│ ├── Private keys │
|
||||
│ └── Biometric-gated access only │
|
||||
│ Package: react-native-keychain │
|
||||
│ NO web fallback (security boundary) │
|
||||
├──────────────────────────────────────────────┤
|
||||
│ Tier 2: SQLite (Structured Data) │
|
||||
│ ├── proof_history table │
|
||||
│ ├── Pagination (PAGE_SIZE=20) │
|
||||
│ └── Stale proof cleanup (10-min timeout) │
|
||||
│ Package: react-native-sqlite-storage │
|
||||
│ DB file: proof_history.db │
|
||||
│ Web: stub implementation (database.web.ts) │
|
||||
├──────────────────────────────────────────────┤
|
||||
│ Tier 3: AsyncStorage (App State) │
|
||||
│ ├── User preferences │
|
||||
│ ├── Zustand persisted stores │
|
||||
│ └── Feature flags, non-sensitive cache │
|
||||
│ Package: @react-native-async-storage │
|
||||
└──────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## SDK Document Persistence
|
||||
|
||||
The SDK uses a `DocumentsAdapter` interface for document catalog storage:
|
||||
|
||||
| Platform | Implementation |
|
||||
|----------|---------------|
|
||||
| React Native | AsyncStorage (key prefix: `@self:document:`) |
|
||||
| WebView/Web | IndexedDB (`documents-web.ts` adapter) |
|
||||
|
||||
Operations: `loadDocumentCatalog`, `saveDocumentCatalog`, `loadDocumentById`, `saveDocument`, `deleteDocument` — all must be idempotent.
|
||||
|
||||
## On-Chain State
|
||||
|
||||
Smart contracts maintain:
|
||||
- Merkle trees (commitment tree, DSC tree, CSCA tree)
|
||||
- Nullifiers (prevent double-registration)
|
||||
- Verification configurations
|
||||
- Attestation registries
|
||||
|
||||
## DOs
|
||||
|
||||
- DO use Keychain for ALL secrets (mnemonics, private keys, auth tokens)
|
||||
- DO use SQLite for structured, queryable local data (proof history)
|
||||
- DO use AsyncStorage for non-sensitive app state and Zustand persistence
|
||||
- DO implement the `DocumentsAdapter` interface for SDK document storage
|
||||
- DO clean up stale proofs (10-minute timeout for pending proofs)
|
||||
- DO use pagination for SQLite queries (PAGE_SIZE=20)
|
||||
|
||||
## DON'Ts
|
||||
|
||||
- DON'T store secrets in AsyncStorage or SQLite
|
||||
- DON'T create web implementations for Keychain — it's a security boundary
|
||||
- DON'T treat local storage as source of truth — on-chain state is canonical
|
||||
- DON'T add new SQLite tables without considering the web stub fallback
|
||||
- DON'T bypass the DocumentsAdapter interface for SDK document operations
|
||||
56
purple/global/external-services.md
Normal file
56
purple/global/external-services.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# External Services
|
||||
|
||||
## Service Map
|
||||
|
||||
| Service | Package | Purpose | Location |
|
||||
|---------|---------|---------|----------|
|
||||
| Firebase Messaging | `@react-native-firebase/messaging` | Push notifications | `app/src/services/notifications/` |
|
||||
| Firebase Remote Config | `@react-native-firebase/remote-config` | Feature flags, remote config | `app/src/providers/remoteConfigProvider.tsx` |
|
||||
| Segment | `@segment/analytics-react-native` | Analytics / event tracking | `app/src/services/analytics.ts` |
|
||||
| Sentry | `@sentry/react-native` | Error tracking / crash reporting | `app/src/config/sentry.ts` |
|
||||
| Sumsub | `@sumsub/react-native-mobilesdk-module` | KYC identity verification | `app/src/integrations/sumsub/` |
|
||||
| Google Drive | `@robinbobin/react-native-google-drive-api-wrapper` | Cloud mnemonic backup | `app/src/services/cloud-backup/google.ts` |
|
||||
| Google OAuth | `react-native-app-auth` | OAuth 2.0 for Drive access | `app/src/services/cloud-backup/google.ts` |
|
||||
|
||||
## Integration Patterns
|
||||
|
||||
### Analytics (Segment)
|
||||
- Centralized in `app/src/services/analytics.ts`
|
||||
- Events defined as constants in `packages/mobile-sdk-alpha/src/constants/analytics.ts`
|
||||
- Track via the analytics adapter (SDK) or service directly (app)
|
||||
|
||||
### Error Tracking (Sentry)
|
||||
- Initialized early in app lifecycle
|
||||
- Platform-specific configs: `sentry.ts` / `sentry.web.ts`
|
||||
- Captures unhandled exceptions and breadcrumbs
|
||||
|
||||
### Remote Config (Firebase)
|
||||
- Wrapped in `RemoteConfigProvider` context
|
||||
- Provides feature flags and runtime configuration
|
||||
- Fetched on app start, cached locally
|
||||
|
||||
### KYC (Sumsub)
|
||||
- Access token fetched per-session via TEE endpoint
|
||||
- 30-second timeout on token fetch
|
||||
- Token not persisted — fresh for each KYC session
|
||||
|
||||
### Cloud Backup (Google Drive)
|
||||
- OAuth 2.0 with offline access + consent prompt
|
||||
- Scoped to Google Drive app data folder only
|
||||
- Mnemonic encrypted before upload
|
||||
|
||||
## DOs
|
||||
|
||||
- DO use the centralized analytics service for event tracking
|
||||
- DO define new analytics events as constants (not inline strings)
|
||||
- DO handle service failures gracefully — external services are not critical path
|
||||
- DO use Firebase Remote Config for feature flags (not hardcoded booleans)
|
||||
- DO scope OAuth to minimum required permissions
|
||||
|
||||
## DON'Ts
|
||||
|
||||
- DON'T call external services synchronously in the critical app startup path
|
||||
- DON'T persist third-party service tokens long-term — fetch fresh per session
|
||||
- DON'T send sensitive identity data to analytics services
|
||||
- DON'T bypass the provider pattern for service access — use context providers
|
||||
- DON'T add new external service integrations without a dedicated service module
|
||||
23
purple/global/how-agents-document.md
Normal file
23
purple/global/how-agents-document.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Documentation Standards for all Agents
|
||||
|
||||
All agents use Markdown files to create specifications, and document their work. All agents have to follow a specific folder/file structure:
|
||||
|
||||
./ (project root)
|
||||
/purple
|
||||
/documentation <-- Created by `implement-spec` command if not exist yet
|
||||
/{YYMMDD}-{feature-slug} <-- Created by `implement-spec` command
|
||||
user-provided-product-spec-{feature-slug}.md <-- Written by user, copied into here by `implement-spec` command
|
||||
user-provided-technical-spec-{feature-slug}.md <-- Optionally written by user, copied into here by `implement-spec` command
|
||||
/agent-written-specifications <-- Created by `implement-spec` command
|
||||
agent-written-product-spec-{feature-slug}.md <-- Created by `product-spec-writer` agent
|
||||
implementation-spec-{feature-slug}.md <-- Created by `engineering-architect` agent
|
||||
/completed-tickets-documentation <-- Created by `implement-spec` command
|
||||
{phase}-{ticketnumber}-{feature-slug}-documentation.md <-- Created by `implementation-engineer` agent
|
||||
... (one for each completed ticket)
|
||||
/qa-results <-- Created by QA agents
|
||||
/repos
|
||||
... (repos that are relevant to this project) <-- Loaded by Purple CLI
|
||||
|
||||
The project root is the base folder out of which agents operate, not a specific codebase inside of `repos`
|
||||
|
||||
> **Note:** `purple/` may be a symlink. If Glob returns no results, use Bash or Read to access files directly.
|
||||
122
purple/global/tech-stack.md
Normal file
122
purple/global/tech-stack.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# Tech Stack Overview
|
||||
|
||||
## Runtime & Package Management
|
||||
|
||||
- Language: TypeScript (strict mode) everywhere except native code
|
||||
- Runtime: Node.js >=22 <23
|
||||
- Package manager: Yarn 4.12.0 (workspaces)
|
||||
- License: BUSL-1.1 (converts to Apache-2.0 on 2029-06-11)
|
||||
|
||||
## Monorepo Layout
|
||||
|
||||
```
|
||||
repos/self/
|
||||
├── app/ # React Native wallet app (main product)
|
||||
│ ├── src/
|
||||
│ │ ├── components/ # Shared UI components
|
||||
│ │ ├── screens/ # Screen components by feature
|
||||
│ │ ├── navigation/ # React Navigation setup
|
||||
│ │ ├── providers/ # Context providers (auth, db, etc.)
|
||||
│ │ ├── stores/ # Zustand stores
|
||||
│ │ ├── hooks/ # Custom React hooks
|
||||
│ │ ├── services/ # Analytics, backup, notifications, etc.
|
||||
│ │ ├── proving/ # ZK proof generation logic
|
||||
│ │ ├── integrations/ # Third-party integrations
|
||||
│ │ ├── utils/ # Utilities
|
||||
│ │ ├── config/ # App configuration
|
||||
│ │ └── types/ # Type definitions
|
||||
│ ├── ios/ # Native iOS (Swift)
|
||||
│ └── android/ # Native Android (Java/Kotlin)
|
||||
│
|
||||
├── packages/
|
||||
│ ├── mobile-sdk-alpha/ # Embeddable SDK (WebView engine, platform-agnostic core)
|
||||
│ ├── webview-app/ # WebView application (React + Vite)
|
||||
│ ├── webview-bridge/ # Bidirectional WebView <-> native communication
|
||||
│ ├── rn-sdk/ # React Native SDK wrapper (consumer-facing)
|
||||
│ ├── mobile-sdk-demo/ # SDK demo application
|
||||
│ ├── kmp-sdk/ # Kotlin Multiplatform SDK (future architecture)
|
||||
│ ├── kmp-test-app/ # KMP test application
|
||||
│ └── self-sdk-swift/ # Swift SDK
|
||||
│
|
||||
├── sdk/
|
||||
│ ├── core/ # Backend SDK (@selfxyz/core) - proving & verification
|
||||
│ ├── qrcode/ # QR code generation (React)
|
||||
│ ├── qrcode-angular/ # QR code generation (Angular)
|
||||
│ ├── sdk-common/ # Shared SDK utilities
|
||||
│ └── sdk-go/ # Go SDK
|
||||
│
|
||||
├── contracts/ # Solidity smart contracts (Hardhat)
|
||||
├── circuits/ # Zero-knowledge circuits (Circom/Noir)
|
||||
├── common/ # Shared types, constants, utilities (@selfxyz/common)
|
||||
├── noir/ # Noir circuit development
|
||||
└── scripts/ # Build, test, and utility scripts
|
||||
```
|
||||
|
||||
## Architectural Direction
|
||||
|
||||
The project is migrating from React Native toward a **WebView-based architecture** with a **Kotlin Multiplatform (KMP) compatibility layer** for iOS and Android. Key packages driving this:
|
||||
|
||||
- `mobile-sdk-alpha` — Platform-agnostic SDK core (WebView engine)
|
||||
- `webview-bridge` — Communication layer between WebView and native shells
|
||||
- `webview-app` — The web UI rendered inside the WebView
|
||||
- `kmp-sdk` — Kotlin Multiplatform native layer (replaces RN native modules)
|
||||
|
||||
```
|
||||
Current Target
|
||||
─────── ──────
|
||||
React Native App WebView App (web tech)
|
||||
+ Native Modules (RN bridge) + KMP Native Layer (iOS/Android)
|
||||
+ Platform-specific JS + webview-bridge (communication)
|
||||
```
|
||||
|
||||
## Core Frameworks
|
||||
|
||||
| Layer | Technology |
|
||||
|--------------------|-------------------------------------|
|
||||
| Mobile app | React Native 0.77 + Expo ~52 |
|
||||
| UI library | Tamagui |
|
||||
| State (simple) | Zustand |
|
||||
| State (workflows) | XState (state machines) |
|
||||
| Navigation | React Navigation 7 (native-stack) |
|
||||
| Web build | Vite + React |
|
||||
| Library bundling | tsup (ESM/CJS dual) |
|
||||
| Smart contracts | Solidity + Hardhat + OpenZeppelin |
|
||||
| ZK circuits | Circom / Noir + snarkjs |
|
||||
| CI/CD | GitHub Actions (20+ workflows) |
|
||||
|
||||
## Git Branching Strategy
|
||||
|
||||
```
|
||||
feature branches
|
||||
│
|
||||
▼
|
||||
dev ← All feature PRs target dev
|
||||
│
|
||||
▼
|
||||
staging ← QA / integration testing
|
||||
│
|
||||
▼
|
||||
main ← Production releases
|
||||
```
|
||||
|
||||
- Feature branches fork off `dev` (source of truth)
|
||||
- PRs always target `dev`
|
||||
- `dev` merges into `staging` for QA
|
||||
- `staging` merges into `main` for production releases
|
||||
|
||||
## DOs
|
||||
|
||||
- DO use TypeScript strict mode for all new packages
|
||||
- DO use Yarn workspaces for dependency management
|
||||
- DO use tsup for building library packages (ESM + CJS dual output)
|
||||
- DO build new SDK features in the WebView/KMP architecture, not in the RN app directly
|
||||
- DO use the `@selfxyz/common` package for shared types and utilities
|
||||
- DO keep shared constants and types in `common/`
|
||||
|
||||
## DON'Ts
|
||||
|
||||
- DON'T add new React Native native modules — use the WebView bridge pattern instead
|
||||
- DON'T use npm or pnpm — this is a Yarn workspace monorepo
|
||||
- DON'T import from other workspace packages by relative path — use package names (`@selfxyz/*`)
|
||||
- DON'T add dependencies to the workspace root unless they're dev-only tooling
|
||||
- DON'T bypass the adapter layer for platform-specific code
|
||||
72
purple/global/testing.md
Normal file
72
purple/global/testing.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Testing
|
||||
|
||||
## Framework Map
|
||||
|
||||
| Package | Framework | Command |
|
||||
|-----------------|------------------------|-----------------------------|
|
||||
| `app/` | Jest + babel-jest | `yarn test` |
|
||||
| `packages/mobile-sdk-alpha/` | Vitest | `npx vitest run && npx tsc --noEmit` |
|
||||
| `packages/webview-bridge/` | Vitest | `yarn build && yarn vitest run` |
|
||||
| `packages/webview-app/` | Vite | `npx tsc --noEmit && npx vite build` |
|
||||
| `packages/kmp-sdk/` | Kotlin | `./gradlew :shared:jvmTest` |
|
||||
| `common/` | Vitest | `yarn test` |
|
||||
| `contracts/` | Hardhat (Mocha + Chai) | `yarn hardhat test` |
|
||||
| `circuits/` | ts-mocha + circom_tester | `yarn test` |
|
||||
| `app/` E2E | Detox | `yarn test:e2e:ios/android` |
|
||||
| `scripts/` | Node.js native `--test`| `node --test scripts/tests/*.cjs` |
|
||||
|
||||
**Full repo validation:** `yarn lint && yarn types && yarn build`
|
||||
|
||||
## Test File Locations
|
||||
|
||||
```
|
||||
app/
|
||||
├── tests/ # Main test directory
|
||||
│ ├── __setup__/ # Mocks (SVG, images, @env)
|
||||
│ ├── e2e/ # Detox end-to-end tests
|
||||
│ └── src/ # Test utilities (@tests/ alias)
|
||||
├── src/**/__tests__/ # Co-located unit tests
|
||||
└── src/**/*.test.ts # Co-located test files
|
||||
|
||||
packages/*/
|
||||
└── src/**/*.test.ts # Co-located with source
|
||||
```
|
||||
|
||||
## Test File Patterns
|
||||
|
||||
- `**/__tests__/**/*.{js,jsx,ts,tsx,cjs}`
|
||||
- `**/?(*.)+(spec|test).{js,jsx,ts,tsx,cjs}`
|
||||
|
||||
## Required Mocks (App / Jest)
|
||||
|
||||
| Mock | Purpose |
|
||||
|------|---------|
|
||||
| `tests/__setup__/svgMock.js` | SVG imports return empty component |
|
||||
| `tests/__setup__/imageMock.js` | Image imports return dummy value |
|
||||
| `tests/__setup__/@env.js` | Environment variables |
|
||||
|
||||
Path aliases are mapped in `jest.config.cjs`:
|
||||
- `@/` → `<rootDir>/src/`
|
||||
- `@tests/` → `<rootDir>/tests/src/`
|
||||
- `@selfxyz/mobile-sdk-alpha` → dist CJS builds
|
||||
|
||||
## SDK Resolution in Tests
|
||||
|
||||
The app's Jest config maps `@selfxyz/mobile-sdk-alpha` imports to the **built dist output** (not source). You must build the SDK before running app tests that depend on it.
|
||||
|
||||
## DOs
|
||||
|
||||
- DO use Jest for the `app/` package and Vitest for library packages
|
||||
- DO co-locate tests near source files (`__tests__/` directory or `.test.ts` suffix)
|
||||
- DO use ES `import` statements for React and React Native in tests
|
||||
- DO build SDK packages before running app tests that depend on them
|
||||
- DO use the mock files in `tests/__setup__/` for SVG, images, and env vars
|
||||
- DO use the `@tests/` path alias for shared test utilities
|
||||
|
||||
## DON'Ts
|
||||
|
||||
- DON'T use `require('react')` or `require('react-native')` in test files — causes OOM in CI
|
||||
- DON'T mix Jest and Vitest in the same package
|
||||
- DON'T skip mocking native modules (NFC, keychain, biometrics) — they don't exist in test env
|
||||
- DON'T put Detox E2E tests in the Jest test match patterns (they're excluded)
|
||||
- DON'T import SDK source directly in app tests — use the package name (resolved via dist)
|
||||
76
purple/mobile/native-platforms.md
Normal file
76
purple/mobile/native-platforms.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Native Platforms
|
||||
|
||||
## Overview
|
||||
|
||||
iOS (Swift) and Android (Java/Kotlin) native code supports features that require direct hardware or OS access. Managed via React Native bridges and the WebView bridge protocol.
|
||||
|
||||
## iOS
|
||||
|
||||
- **Language**: Swift
|
||||
- **Build**: Xcode + CocoaPods
|
||||
- **Location**: `app/ios/`
|
||||
- **Deployment**: Fastlane (`app/fastlane/`)
|
||||
|
||||
### Native Modules
|
||||
|
||||
| Module | File | Purpose |
|
||||
|--------|------|---------|
|
||||
| MRZ Scanner | `MRZScanner.swift` | Machine Readable Zone detection |
|
||||
| Live MRZ View | `LiveMRZScannerView.swift` | Real-time camera MRZ UI |
|
||||
| Camera View | `CameraView.swift` | Camera integration |
|
||||
| Logger Bridge | `NativeLoggerBridge.swift` | Structured logging from JS |
|
||||
|
||||
### Custom Frameworks
|
||||
|
||||
| Framework | Purpose |
|
||||
|-----------|---------|
|
||||
| `NFCPassportReader.xcframework` | NFC chip reading (ICAO standard) |
|
||||
| `OpenSSL.xcframework` | Cryptographic operations |
|
||||
|
||||
### iOS Config
|
||||
|
||||
- Firebase: `GoogleService-Info.plist`
|
||||
- Entitlements: NFC tag reading, keychain access, associated domains
|
||||
- CocoaPods: Managed via `Podfile`
|
||||
|
||||
## Android
|
||||
|
||||
- **Language**: Java/Kotlin
|
||||
- **Build**: Gradle
|
||||
- **Location**: `app/android/`
|
||||
- **Min SDK**: Check `build.gradle`
|
||||
|
||||
### Key Config
|
||||
|
||||
- Gradle properties for large heap, Hermes engine
|
||||
- Dev keystore for debug builds
|
||||
- NFC permissions in AndroidManifest
|
||||
- ProGuard rules for release builds
|
||||
|
||||
## Version Management
|
||||
|
||||
- `yarn sync-versions` — syncs iOS and Android version numbers
|
||||
- App version tracked in both `Info.plist` (iOS) and `build.gradle` (Android)
|
||||
|
||||
## Migration Direction
|
||||
|
||||
New native features should target the **KMP (Kotlin Multiplatform)** architecture:
|
||||
- `packages/kmp-sdk/` — shared native code compiled for both platforms
|
||||
- WebView bridge handles JS ↔ native communication
|
||||
- Avoids duplicating native code across iOS and Android
|
||||
|
||||
## DOs
|
||||
|
||||
- DO build new native features in the KMP layer, not as platform-specific React Native modules
|
||||
- DO use Fastlane for iOS deployment automation
|
||||
- DO use `yarn sync-versions` before releases to keep platform versions aligned
|
||||
- DO test NFC features on physical devices (simulators don't support NFC)
|
||||
- DO use the native logger bridge for structured logging from native code
|
||||
|
||||
## DON'Ts
|
||||
|
||||
- DON'T add new React Native native modules — use the WebView bridge pattern
|
||||
- DON'T modify iOS entitlements without understanding the signing implications
|
||||
- DON'T commit dev keystores or signing credentials to the repository
|
||||
- DON'T bypass CocoaPods for iOS dependency management
|
||||
- DON'T test NFC in simulators — it requires real hardware
|
||||
93
purple/mobile/nfc-biometrics.md
Normal file
93
purple/mobile/nfc-biometrics.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# NFC & Biometrics
|
||||
|
||||
## NFC Passport Reading
|
||||
|
||||
### Flow
|
||||
|
||||
```
|
||||
1. MRZ Scan (optical) ← Camera reads Machine Readable Zone
|
||||
│
|
||||
▼
|
||||
2. Extract MRZ Key ← Document number + DOB + expiry
|
||||
│
|
||||
▼
|
||||
3. NFC Chip Read ← Authenticate and read passport chip
|
||||
│
|
||||
▼
|
||||
4. Extract Data Groups ← DG1 (MRZ data), DG2 (photo), etc.
|
||||
│
|
||||
▼
|
||||
5. Validate Signatures ← Verify certificate chain (DSC → CSCA)
|
||||
│
|
||||
▼
|
||||
6. Generate Circuit Inputs ← Prepare for ZK proof generation
|
||||
```
|
||||
|
||||
### NFC Implementation
|
||||
|
||||
- **Package**: `react-native-nfc-manager`
|
||||
- **Protocol**: ICAO 9303 (Machine Readable Travel Documents)
|
||||
- **SDK adapter**: `packages/mobile-sdk-alpha/src/adapters/react-native/nfc-scanner.ts`
|
||||
- **Bridge domain**: `nfc` (methods: `scan`, `cancelScan`, `isSupported`)
|
||||
- **iOS framework**: `NFCPassportReader.xcframework` (native chip reading)
|
||||
|
||||
### Supported Documents
|
||||
|
||||
| Type | NFC Support | MRZ Format |
|
||||
|------|-------------|------------|
|
||||
| E-Passport | Full (ICAO chip) | TD3 (2 lines, 44 chars) |
|
||||
| EU ID Card | Full (ICAO chip) | TD1 (3 lines, 30 chars) |
|
||||
| Aadhaar | Special handling | Custom |
|
||||
|
||||
### Error Handling
|
||||
|
||||
NFC errors are categorized for recovery:
|
||||
- Tag connection lost → retry prompt
|
||||
- Authentication failed → MRZ mismatch
|
||||
- User cancelled → clean abort via AbortSignal
|
||||
- Unsupported tag → document not compatible
|
||||
|
||||
## Biometric Authentication
|
||||
|
||||
### Implementation
|
||||
|
||||
- **Package**: `react-native-biometrics`
|
||||
- **Config**: `allowDeviceCredentials: true` (passcode fallback)
|
||||
- **Prompt**: "Confirm your identity to access the stored secret"
|
||||
|
||||
### Biometric Types
|
||||
|
||||
| Platform | Method |
|
||||
|----------|--------|
|
||||
| iOS | Face ID, Touch ID |
|
||||
| Android | Fingerprint, face unlock |
|
||||
| Both | Device passcode (fallback) |
|
||||
|
||||
### Error Categories
|
||||
|
||||
Defined in `app/src/utils/keychainErrors.ts`:
|
||||
- **User cancellation**: User dismissed the prompt
|
||||
- **Crypto failure**: Keychain decryption failed (may need reset)
|
||||
- **Biometric-specific**: Hardware errors (codes 5, 10 on Android)
|
||||
|
||||
## Haptic Feedback
|
||||
|
||||
- **Package**: `react-native-haptic-feedback`
|
||||
- **Usage**: Success/error feedback, button presses, NFC scan events
|
||||
|
||||
## DOs
|
||||
|
||||
- DO support AbortSignal for NFC scan cancellation
|
||||
- DO validate MRZ checksum before attempting NFC read
|
||||
- DO always allow passcode fallback for biometric prompts
|
||||
- DO categorize biometric errors (cancellation vs hardware failure)
|
||||
- DO test NFC on physical devices with real passports
|
||||
- DO use haptic feedback for NFC scan success/failure
|
||||
|
||||
## DON'Ts
|
||||
|
||||
- DON'T attempt NFC without first extracting MRZ data (key material needed)
|
||||
- DON'T assume biometrics are available — always check first
|
||||
- DON'T block the UI during NFC scan — show progress via bridge events
|
||||
- DON'T store NFC-read data in plain text — process and discard raw chip data
|
||||
- DON'T skip certificate chain validation after NFC read
|
||||
97
purple/mobile/security-storage.md
Normal file
97
purple/mobile/security-storage.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# Security & Storage
|
||||
|
||||
## Security Model
|
||||
|
||||
The device keychain is the root of trust. No web fallbacks for security-critical operations.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ SECURITY BOUNDARY │
|
||||
│ │
|
||||
│ Keychain (native only) │
|
||||
│ ├── Mnemonic phrase │
|
||||
│ ├── Private keys │
|
||||
│ └── Auth adapter secrets │
|
||||
│ │
|
||||
│ Access requires: │
|
||||
│ ├── Biometric or passcode auth │
|
||||
│ └── Active session (15-min timeout) │
|
||||
│ │
|
||||
│ NO web implementation. │
|
||||
│ NO AsyncStorage fallback. │
|
||||
│ NO in-memory fallback (prod). │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Adaptive Security Levels
|
||||
|
||||
The keychain configuration automatically adapts to the device's highest available security:
|
||||
|
||||
| Device Capability | Security Level | Access Control |
|
||||
|-------------------|---------------|----------------|
|
||||
| Secure hardware (StrongBox/SE) + biometrics + passcode | `SECURE_HARDWARE` | `BIOMETRY_ANY_OR_DEVICE_PASSCODE` |
|
||||
| Biometrics + passcode (no secure hardware) | `SECURE_SOFTWARE` | `BIOMETRY_ANY_OR_DEVICE_PASSCODE` |
|
||||
| Passcode only (no biometrics) | `ANY` | `DEVICE_PASSCODE` |
|
||||
| None | NOT ALLOWED | Block secret storage; require device passcode or biometrics |
|
||||
|
||||
Detection functions in `app/src/integrations/keychain/index.ts`:
|
||||
- `checkBiometricsAvailable()`
|
||||
- `checkPasscodeAvailable()`
|
||||
- `getMaxSecurityLevel()`
|
||||
- `detectSecurityCapabilities()`
|
||||
|
||||
## Keychain Migration
|
||||
|
||||
`migrateToSecureKeychain()` upgrades existing entries to the device's highest available security level. Tracked via `hasCompletedKeychainMigration` flag in the settings store.
|
||||
|
||||
## Session Management
|
||||
|
||||
- Session starts on successful biometric auth or device passcode (`allowDeviceCredentials: true`)
|
||||
- Timeout: 15 minutes of inactivity
|
||||
- Expired session requires re-authentication
|
||||
- Tracked via `isAuthenticated` flag in AuthProvider
|
||||
|
||||
## Secure Access Pattern
|
||||
|
||||
All secret access must go through wrapper functions:
|
||||
|
||||
```
|
||||
_getSecurely(fn, formatter, options) ← Adaptive keychain access
|
||||
_getWithBiometrics(fn, formatter, opts) ← Explicit biometric prompt
|
||||
```
|
||||
|
||||
These detect device capabilities, configure keychain options, and handle errors by category.
|
||||
|
||||
## Android-Specific
|
||||
|
||||
- **StrongBox**: Optional hardware security module
|
||||
- Toggle: `useStrongBox` in settings store
|
||||
- Some devices support StrongBox but have bugs — configurable per-device
|
||||
|
||||
## Error Recovery
|
||||
|
||||
| Error Type | Action |
|
||||
|------------|--------|
|
||||
| User cancellation | Show auth prompt again |
|
||||
| Crypto failure | Show error modal, may need keychain reset |
|
||||
| Biometric lockout | Fall back to device passcode |
|
||||
| Hardware error | Log and degrade to software security |
|
||||
|
||||
Global callback: `setKeychainCryptoFailureCallback()` for centralized error handling.
|
||||
|
||||
## DOs
|
||||
|
||||
- DO use adaptive keychain configuration from `integrations/keychain/`
|
||||
- DO handle all three error categories (cancellation, crypto, biometric)
|
||||
- DO run keychain migration on app start for existing users
|
||||
- DO check device capabilities before presenting biometric options
|
||||
- DO use the `_getSecurely()` wrapper for ALL keychain access
|
||||
|
||||
## DON'Ts
|
||||
|
||||
- DON'T create web implementations for keychain operations
|
||||
- DON'T store secrets outside the keychain (no AsyncStorage, no SQLite, no Zustand)
|
||||
- DON'T skip error categorization — different errors need different recovery flows
|
||||
- DON'T assume StrongBox works on all Android devices
|
||||
- DON'T access the keychain without going through the AuthProvider's secure wrappers
|
||||
- DON'T extend session timeout beyond 15 minutes without security review
|
||||
53
purple/temp/codebase-analysis.md
Normal file
53
purple/temp/codebase-analysis.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Codebase Analysis - Self XYZ
|
||||
|
||||
## Project Overview
|
||||
Identity verification wallet using NFC passport scanning + zero-knowledge proofs. Yarn 4.12.0 monorepo.
|
||||
|
||||
## Key Tech Stack
|
||||
- **Mobile**: React Native 0.77.0 + Expo ~52.0.40
|
||||
- **UI**: Tamagui 1.126.14
|
||||
- **State**: Zustand + XState (state machines)
|
||||
- **Navigation**: React Navigation 7
|
||||
- **Database**: SQLite (mobile), Blockchain (contracts)
|
||||
- **Auth**: Passkey, Biometric, Turnkey wallet, OAuth
|
||||
- **Testing**: Jest (app), Vitest (packages), Hardhat/Mocha (contracts), Detox (E2E)
|
||||
- **Linting**: ESLint + Prettier + Knip
|
||||
- **Build**: Vite (web), Metro (RN), tsup (libraries), Hardhat (contracts)
|
||||
- **CI/CD**: GitHub Actions (20+ workflows)
|
||||
- **ZK**: Circom/Noir circuits + snarkjs
|
||||
- **Contracts**: Solidity + Hardhat + OpenZeppelin
|
||||
- **Native**: Swift (iOS), Java/Kotlin (Android)
|
||||
- **Analytics**: Segment + Sentry
|
||||
- **Node**: >=22 <23
|
||||
- **License**: BUSL-1.1
|
||||
|
||||
## Monorepo Structure
|
||||
- `/app/` - React Native mobile app (main product)
|
||||
- `/packages/mobile-sdk-alpha/` - Embeddable SDK (WebView engine)
|
||||
- `/packages/webview-bridge/` - WebView-native communication
|
||||
- `/packages/webview-app/` - WebView application
|
||||
- `/packages/rn-sdk/` - React Native SDK wrapper
|
||||
- `/sdk/core/` - Backend SDK for verification
|
||||
- `/contracts/` - Solidity smart contracts
|
||||
- `/circuits/` - ZK circuits (Circom/Noir)
|
||||
- `/common/` - Shared utilities/types/constants
|
||||
|
||||
## Architecture Patterns
|
||||
- Platform adapter pattern (RN adapters wrap platform-specific APIs)
|
||||
- WebView bridge for SDK distribution
|
||||
- Provider pattern for context/state injection
|
||||
- XState for complex workflows (proof generation)
|
||||
- Zustand for simple global state
|
||||
- Strict ESLint: no `export *`, enforced import sorting
|
||||
|
||||
## Database
|
||||
- SQLite via react-native-sqlite-storage
|
||||
- Single DB: proof_history.db with proof_history table
|
||||
- Keychain for secure storage (native only)
|
||||
- AsyncStorage for non-sensitive state
|
||||
|
||||
## No Traditional Backend
|
||||
- No Express/Nest/etc server
|
||||
- Smart contracts serve as "backend"
|
||||
- SDK core package handles proving/verification
|
||||
- External services: Firebase, Segment, Sentry, Sumsub KYC
|
||||
Reference in New Issue
Block a user