🤖 Download Android APK/AAB · 🌐 Web Dashboard · 📖 Docs · 🐛 Report Bug · ✨ Request Feature
🚀 Preview Build (ANDROID ONLY): Download Android App (AAB) Note: This build artifact is valid for 29 days from compilation. iOS preview builds are currently unsupported as they require a paid Apple Developer account.
Rayos is a seedless, self-custodial smart wallet on the Stellar network. There are no seed phrases — your keys live in your device's Secure Enclave (iOS) or StrongBox (Android), protected by Face ID, Touch ID, or biometrics. The on-chain wallet is a Soroban smart contract that verifies WebAuthn signatures directly, meaning you get the security of passkeys with the programmability of smart contracts.
This repository is the native mobile app — the most polished way to experience the passkey UX. It is feature-equivalent to the web-dashboard but passkeys on a real phone, unlocked by your face, are where the "no seed phrase" pitch really lands.
This app is one piece of a larger open-source system. Here's how the repos connect:
| Repo | Role | How mobile uses it |
|---|---|---|
mobile-app ← you are here |
Native iOS & Android app | — |
wallet-sdk |
Shared TypeScript SDK | Vendored tarball in vendor/ (pnpm sdk:vendor to refresh) — WalletSdk class, PasskeyProvider interface, all types |
relay-backend |
NestJS gasless relay API | All HTTP calls: WebAuthn, relay, sessions, recovery, .well-known files |
wallet-contracts |
Soroban smart contracts (Rust) | Indirect — JS bindings consumed through wallet-sdk |
web-dashboard |
Next.js web app | Design token parity; same conceptual API contract |
infra |
GitHub Actions, Docker, env specs | EAS build workflow reused; env variable naming convention |
| Feature | Details |
|---|---|
| 🔑 Passkey Wallet Creation | Register with Face ID / Touch ID — no seed phrase, no password. Deploys a Soroban smart contract on Stellar automatically. |
| 🔐 Native Biometric Signing | Every transaction is approved by your biometric. The passkey never leaves the device Secure Enclave. |
| 💸 Send & Receive XLM | Send Stellar assets with a biometric confirmation. Transaction history pulled live from Horizon. |
| 🛡️ Spend Limits | Set rolling per-token spend limits enforced on-chain by the Policy contract. |
| 🔑 Session Keys | Create scoped, time-limited session keys for specific dapps or automations. |
| 👥 Social Recovery | Add guardian addresses. If you lose your device, 2-of-N guardians can approve a new passkey — with a 48-hour timelock. |
| 📲 Deep Link Recovery Approval | Guardians receive an email link; tapping it on mobile opens the app directly on the approval screen. |
| 🌙 System / Light / Dark Theme | Follows OS by default; user can override in Settings. Choice is persisted securely. |
| 📡 Offline-first | TanStack Query with networkMode: offlineFirst — pending state is clear, retries happen automatically. |
mobile-app/
│
├── app/ ← expo-router file-based routes
│ ├── _layout.tsx ← Root: QueryClient, deep-link handler, auth gate
│ ├── +not-found.tsx
│ ├── (onboarding)/ ← Unauthenticated group (Stack)
│ │ ├── index.tsx ← Welcome / landing screen
│ │ ├── create.tsx ← Passkey registration → wallet deployment
│ │ ├── login.tsx ← Passkey assertion → wallet lookup
│ │ └── recover.tsx ← Initiate guardian recovery
│ ├── (dashboard)/ ← Authenticated group (Tabs)
│ │ ├── wallet.tsx ← Balance · send · receive · activity
│ │ ├── policies.tsx ← Spend limit · session keys · allow-list
│ │ ├── guardians.tsx ← Guardians · threshold · active recovery
│ │ └── settings.tsx ← Theme · app info · sign out
│ └── recovery/[proposalId].tsx ← Guardian approval (deep-link target)
│
├── components/
│ ├── ui/ ← Design system: Button, Card, Input, Sheet, Toast…
│ ├── layout/ ← TabBar, ThemeSwitch, OnboardingHeader, StepDots
│ ├── wallet/ ← BalanceCard, TransactionList, SendSheet, SignerList
│ ├── policies/ ← SpendLimitCard, SessionKeysCard, AllowListCard
│ └── guardians/ ← GuardianList, RecoveryBanner, RecoveryStatusCard
│
├── native/
│ └── passkey-adapter.ts ← ★ ONLY platform-specific seam
│ Implements PasskeyProvider (react-native-passkeys)
│
├── hooks/
│ ├── useTheme.tsx ← Color-scheme resolution + SecureStore override
│ ├── useWallet.ts ← Balance, signers, send mutation
│ ├── usePolicies.ts ← Session keys CRUD
│ └── useRecovery.ts ← Recovery proposal lifecycle (10s polling)
│
├── lib/
│ ├── config.ts ← Zod-validated EXPO_PUBLIC_* env vars
│ ├── sdk-client.ts ← WalletSdk singleton (native passkeyProvider injected)
│ ├── storage-adapter.ts ← StorageAdapter → expo-secure-store
│ ├── query-client.ts ← TanStack Query (offline-first defaults)
│ ├── api.ts ← Typed fetch helpers for relay-backend
│ └── format.ts ← XLM, address, date formatting
│
├── store/auth.ts ← Zustand (walletAddress + credentialId, SecureStore-persisted)
├── assets/ ← Icons, splash, logo — light & dark variants
├── e2e/flows/ ← Maestro YAML E2E flows
│
├── docs/ ← 📖 Extended documentation
│ ├── ARCHITECTURE.md ← Deep-dive: design decisions, flows, CI/CD
│ ├── SETUP.md ← Step-by-step local dev + real-device setup
│ ├── CONTRIBUTING.md ← How to contribute, code standards, commit convention
│ └── SECURITY.md ← Security model + vulnerability reporting
│
├── .github/
│ ├── workflows/ ← ci · eas-preview · eas-release · eas-update
│ ├── ISSUE_TEMPLATE/ ← Bug report · Feature request (GitHub Forms)
│ └── PULL_REQUEST_TEMPLATE.md
│
├── app.config.ts ← Expo config (env-driven, no secrets)
├── eas.json ← development / preview / production EAS profiles
└── .env.example ← All EXPO_PUBLIC_* vars — copy to .env
flowchart TD
A([Open App]) --> B{Has Session?}
B -- No --> C[Welcome Screen]
B -- Yes --> DASH
C --> D{New or Returning?}
D -- New User --> E[Enter Display Name]
D -- Returning --> F[Login with Passkey]
E --> G["Face ID / Touch ID Prompt<br/>create passkey"]
G --> H["Relay: /webauthn/register/options<br/>+ /webauthn/register/verify"]
H --> I["Deploy Wallet Contract<br/>on Stellar Testnet"]
I --> DASH
F --> J["Face ID / Touch ID Prompt<br/>assert passkey"]
J --> K["Relay: GET /wallets/:credentialId<br/>lookup wallet address"]
K --> DASH
DASH([Dashboard]) --> TAB1["💰 Wallet Tab"]
DASH --> TAB2["🛡️ Policies Tab"]
DASH --> TAB3["👥 Guardians Tab"]
TAB1 --> L["View XLM Balance<br/>+ Transaction History"]
TAB1 --> M["Send XLM<br/>Biometric sign → relay submit"]
TAB2 --> N[View Session Keys]
TAB2 --> O["Revoke Session Key<br/>DELETE /api/sessions/:id"]
TAB3 --> P{Active Recovery?}
P -- Yes --> Q["View Status<br/>+ Approve Recovery<br/>POST /api/recovery/approve"]
P -- No --> R["Initiate Recovery<br/>POST /api/recovery/propose"]
Z(["Deep Link<br/>rayos://recovery/:id"]) --> Q
graph TB
subgraph "Mobile App (this repo)"
APP["expo-router screens"]
HOOKS["hooks/<br/>useWallet · usePolicies · useRecovery"]
ADAPTER["native/passkey-adapter.ts<br/>PasskeyProvider impl"]
SDK_CLIENT["lib/sdk-client.ts<br/>WalletSdk singleton"]
STORE["store/auth.ts<br/>Zustand + SecureStore"]
APP --> HOOKS
HOOKS --> SDK_CLIENT
SDK_CLIENT --> ADAPTER
HOOKS --> STORE
end
subgraph "wallet-sdk (file dep)"
WSDK[WalletSdk]
WCLIENT["WalletClient<br/>Soroban contract calls"]
PCLIENT["PolicyClient<br/>Sessions · Recovery"]
RCLIENT["RelayClient<br/>HTTP relay calls"]
WSDK --> WCLIENT
WSDK --> PCLIENT
WSDK --> RCLIENT
end
subgraph "relay-backend (NestJS)"
WA["/webauthn/*<br/>Challenge + verify"]
REL["/relay/submit<br/>+ /relay/status"]
SES["/sessions<br/>Session key CRUD"]
REC["/recovery/*<br/>Propose + approve"]
IDX["/wallets/:credentialId<br/>Address lookup"]
WK["/.well-known/*<br/>AASA + assetlinks"]
end
subgraph "Stellar Network"
SRPC[Soroban RPC]
HRZ["Horizon API<br/>Transaction history"]
FACTORY[FactoryContract]
WALLET_C[WalletContract]
POLICY_C[PolicyContract]
end
subgraph "Device"
ENCLAVE["Secure Enclave / StrongBox<br/>Passkey never leaves here"]
end
SDK_CLIENT --> WSDK
ADAPTER --> ENCLAVE
RCLIENT --> REL
RCLIENT --> WA
WCLIENT --> SRPC
SRPC --> FACTORY
SRPC --> WALLET_C
SRPC --> POLICY_C
HOOKS -->|Horizon fetch| HRZ
HOOKS --> IDX
HOOKS --> SES
HOOKS --> REC
# 1. Clone wallet-sdk alongside (required — file: dependency)
git clone https://github.com/Rayos-Org/wallet-sdk.git
cd wallet-sdk && pnpm install && pnpm build && cd ..
# 2. Clone and install this app
git clone https://github.com/Rayos-Org/mobile-app.git
cd mobile-app
pnpm install
# 3. Configure environment
cp .env.example .env
# 4. Start dev server
pnpm ios # Xcode simulator
pnpm android # Android emulator
pnpm start # Metro only (for use with Expo Dev Client)
⚠️ Expo Go will not work. Passkeys require the native module fromreact-native-passkeys. Use a development build:pnpm build:dev(EAS) orpnpm ios/pnpm androidlocally.
→ Full setup guide (real device, passkeys, EAS): docs/SETUP.md
pnpm typecheck # TypeScript strict check
pnpm lint # ESLint (eslint-config-expo)
pnpm format:check # Prettier
pnpm test # Jest unit + component tests
pnpm test:ci # Jest with coverage report
pnpm e2e # Maestro E2E flows (device/simulator must be running)| Layer | Coverage |
|---|---|
lib/format.ts |
XLM formatting, address truncation, date helpers |
lib/api.ts |
Error normalisation, typed responses |
lib/theme.ts |
Token resolution in both color schemes |
native/passkey-adapter.ts |
Output shape matches PasskeyCredential / PasskeyAssertion types |
store/auth.ts |
Zustand store mutations and persistence |
components/ |
Render in light + dark theme; interaction snapshots |
e2e/flows/onboarding.yml |
Full onboarding flow to wallet screen |
e2e/flows/send.yml |
Send flow up to biometric gate |
Biometric ceremonies themselves are exercised in manual device-lab testing before every release.
| Workflow | Trigger | What it does |
|---|---|---|
ci.yml |
Every PR + main push |
typecheck → lint → prettier → jest → expo-doctor → expo export |
eas-preview.yml |
Every PR + main push |
eas build --profile preview · posts QR code comment on PR |
eas-release.yml |
Push tag v*.*.* |
quality gates → production build → ⏸ manual approval → eas submit |
eas-update.yml |
main push or manual dispatch |
eas update OTA to preview or production channel |
PR ──▶ ci.yml ──▶ eas-preview.yml (installable QR build)
main ──▶ eas-update.yml (OTA to preview channel)
tag v1.x.x ──▶ eas-release.yml ──▶ ⏸ manual approval ──▶ eas submit
| Document | Description |
|---|---|
| docs/ARCHITECTURE.md | Deep-dive: tech stack, directory structure, design decisions, all key flows |
| docs/SETUP.md | Step-by-step: local dev, real device setup, passkey configuration, EAS first-time setup |
| docs/CONTRIBUTING.md | How to contribute: commit convention, PR process, code standards, testing requirements |
| docs/SECURITY.md | Security model, responsible disclosure process |
We welcome contributions of all kinds — bug reports, feature ideas, code, tests, documentation, design feedback.
Quick start:
- Read docs/CONTRIBUTING.md
- Check open issues for
good first issuelabels - Fork → branch → PR against
main
Found a security issue? Please use GitHub Security Advisories instead of a public issue. See docs/SECURITY.md.
MIT © 2026 Rayos Org contributors.
Built with ❤️ by the Rayos community.
⭐ Star this repo if you find it useful — it helps others discover the project.