A passkey-powered Stellar smart wallet with on-chain policies and social recovery.
No seed phrases. No passwords. Just your biometrics.
🚀 Live Demo · 📖 Setup Guide · 🏛️ Architecture · 🤝 Contributing · 🐛 Report a Bug
Guardian Wallet is an open-source Stellar smart wallet secured entirely by passkeys (WebAuthn). Users create a wallet by scanning their face or fingerprint — no seed phrases, no passwords, nothing to back up and nothing to steal.
The wallet is deployed as a Soroban smart contract on the Stellar blockchain. All policies — spend limits, session keys, contract allow-lists — are enforced on-chain, not in application logic that can be bypassed. If you lose your device, a pre-configured set of guardian signers can collectively approve a recovery that installs your new passkey as the wallet's authorised signer.
This repository (web-dashboard) is the Next.js frontend — the face of the product. It talks to three other services in the Rayos ecosystem.
Guardian Wallet is built across four repositories. Here's how they connect:
┌────────────────────────────────────────────────────────────────┐
│ User's Browser / Mobile │
│ web-dashboard ← you are here │
│ Next.js 16 · React 19 · shadcn/ui · @rayos/wallet-sdk │
└──────────────────────────┬─────────────────────────────────────┘
│ /api/* proxy (no CORS, cookie-safe)
▼
┌────────────────────────────────────────────────────────────────┐
│ relay-backend (NestJS) │
│ WebAuthn · Fee Relay · Indexer · Sessions · Recovery │
└────────────────┬───────────────────────────────────────────────┘
│ Soroban XDR + RPC calls
▼
┌────────────────────────────────────────────────────────────────┐
│ wallet-contracts (Soroban / Rust) │
│ GuardianWallet · PolicyModule · RecoveryModule │
└────────────────────────────────────────────────────────────────┘
| Repository | Description |
|---|---|
web-dashboard (this) |
Next.js frontend — the UI, API proxy, and passkey flows |
relay-backend |
NestJS backend — WebAuthn ceremony, fee sponsorship, session & recovery management |
wallet-contracts |
Soroban smart contracts — wallet logic, spend limits, social recovery |
infra |
Terraform IaC — Vercel, Render, NeonDB, Cloudflare |
- Create a wallet by registering a WebAuthn passkey (Face ID, Touch ID, Windows Hello) — no seed phrase is ever generated
- Sign in on any device by asserting the passkey — no password reset flows
- Every sensitive action (send, set policy, add guardian) requires a fresh passkey assertion
- Live balance and signers read from the wallet contract over Soroban RPC via
@rayos/wallet-sdk - Transaction history from Soroban contract events (native token
transfers) with direction, amounts and explorer links - One-tap testnet funding through the relay faucet (the relay's sponsor account sends XLM to the contract)
- Sends are passkey-signed Soroban auth entries: the wallet contract verifies the WebAuthn signature on-chain, the relay only pays the fee
- Spend Limits, Session Keys and Contract Allow-List screens are wired to the policy contract's interface but are shown as previews — the policy contract is not yet linked to the wallet's
__check_auth, so the UI is deliberately read-only rather than pretending to sign - Guardians lists the wallet's live on-chain signers; adding guardians / recovery ships with the recovery module
- Configure trusted guardian signers with configurable weights (M-of-N threshold)
- Lost your device? Initiate recovery from a new device — generates a new passkey and proposes it to your guardians
- Guardians approve via a shareable link — no app download required
- Timelock and approval status polled in real time
- Dark mode by default (animated light/dark switch), Geist Sans + Geist Mono, Indigo accent (
#4F46E5/#6D6AF7on dark) - Motion-driven landing page (aurora backdrop, scroll reveals, 3-D tilt cards) built with
motion - Skeleton loaders, empty states with clear CTAs, toast notifications
- Responsive — works on mobile and desktop
web-dashboard/
├── app/ ← Next.js App Router
│ ├── (onboarding)/ ← Public flows: create, login, recover
│ ├── (dashboard)/ ← Protected: wallet, policies, guardians
│ ├── api/ ← Route Handlers proxying to relay-backend
│ └── actions/ ← Server Actions (login/logout)
│
├── components/
│ ├── ui/ ← shadcn/ui primitives — never edited directly
│ ├── wallet/ ← BalanceCard, QuickSend, TransactionList, SignersCard
│ ├── policies/ ← SpendLimitForm, SessionKeyList, AllowListEditor
│ └── guardians/ ← GuardianList, RecoveryStatusBanner
│
├── hooks/ ← All data fetching (React Query + SDK)
│ ├── useWallet.ts ← Wallet state, send transaction
│ ├── usePolicies.ts ← Session keys CRUD
│ ├── useRecovery.ts ← Recovery proposal lifecycle
│ └── useTransactions.ts ← SDK: transfer history (Soroban events), tx status
│
├── lib/ ← Shared utilities
│ ├── auth.ts ← JWT session (jose)
│ ├── config.ts ← Zod-validated env vars
│ ├── proxy.ts ← Generic relay-backend proxy
│ └── sdk-client.ts ← WalletSdk singleton
│
├── docs/ ← Contributor documentation
│ ├── ARCHITECTURE.md ← Deep-dive into structure and decisions
│ ├── SETUP.md ← Local development guide
│ ├── CONTRIBUTING.md ← How to contribute
│ ├── TESTING.md ← Testing strategy and guide
│ ├── SECURITY.md ← Vulnerability reporting
│ └── CODE_OF_CONDUCT.md
│
└── .github/
├── workflows/ci.yml ← TypeScript · ESLint · Vitest · Playwright
├── ISSUE_TEMPLATE/ ← Bug, feature, docs templates
└── PULL_REQUEST_TEMPLATE.md
Full annotated structure: docs/ARCHITECTURE.md → Directory Structure
sequenceDiagram
actor User
participant UI as web-dashboard
participant BE as relay-backend
participant SDK as wallet-sdk
participant Chain as Stellar Testnet
User->>UI: Visit /create, enter wallet name
UI->>BE: POST /webauthn/register/options
BE-->>UI: Registration challenge
UI->>User: Browser passkey prompt (Face ID / Touch ID)
User-->>UI: Biometric confirmation
UI->>BE: POST /webauthn/register/verify
BE-->>UI: Credential verified ✓
UI->>SDK: registerPasskey(options) → P-256 public key
UI->>SDK: deployWallet(credential, salt)
SDK->>BE: POST /relay/deploy
BE->>Chain: WalletFactory.deploy_wallet (sponsor pays)
Chain-->>BE: txHash
BE-->>SDK: { walletAddress, txHash }
SDK-->>UI: { address, txHash }
UI->>UI: loginAction() → sets JWT cookie
UI->>User: Redirect to /wallet dashboard
sequenceDiagram
actor User
participant UI as web-dashboard
participant BE as relay-backend
User->>UI: Visit /login
UI->>BE: POST /webauthn/assert/options
BE-->>UI: Assertion challenge
UI->>User: Browser passkey prompt
User-->>UI: Biometric confirmation
UI->>BE: POST /webauthn/assert/verify
BE-->>UI: { credentialId }
UI->>BE: GET /wallets/:credentialId
BE-->>UI: { walletAddress }
UI->>UI: loginAction() → sets JWT cookie (2h)
UI->>User: Redirect to /wallet
sequenceDiagram
actor User
participant UI as web-dashboard
participant BE as relay-backend
participant SDK as wallet-sdk
participant Chain as Stellar Testnet
User->>UI: Click Send, enter recipient + amount
UI->>SDK: transfer({ walletAddress, to, amount, credentialId })
SDK->>Chain: simulate token.transfer → wallet auth entry
SDK->>User: Passkey prompt (challenge = auth-entry hash)
User-->>SDK: WebAuthn assertion
SDK->>SDK: put authenticatorData/clientDataJSON/signature in the auth entry
SDK->>BE: POST /relay/submit (passkey-signed XDR)
BE->>Chain: re-simulate (contract runs __check_auth), sign envelope, submit
Chain-->>BE: txHash
BE-->>SDK: { txHash, status }
SDK-->>UI: SubmitTransactionResponse
UI->>User: ✓ Real transaction hash + Stellar Expert link
graph TB
subgraph Browser["🌐 Browser (web-dashboard)"]
direction TB
LP[Landing Page] --> Create[/create — Wallet Creation]
LP --> Login[/login — Passkey Sign In]
LP --> Recover[/recover — Lost Device Recovery]
Create --> Dashboard
Login --> Dashboard
subgraph Dashboard["Protected Dashboard"]
W["/wallet — Balance + History"]
P["/policies — Spend Limits, Sessions, Allow-list"]
G["/guardians — Guardian Management"]
end
Dashboard --> Hooks["hooks/* — React Query"]
Hooks --> SDK["@rayos/wallet-sdk"]
Hooks --> APIProxy["app/api/* — Route Handlers"]
Hooks --> RPC["Soroban RPC (balance, signers, events)"]
end
subgraph Backend["☁️ relay-backend (NestJS · Render)"]
WA["WebAuthn Module"]
RL["Relay Module — Fee Sponsorship"]
IDX["Indexer Module — Credential→Address"]
SESS["Sessions Module"]
REC["Recovery Module"]
end
subgraph Contracts["⛓️ Stellar Testnet (Soroban)"]
GW["GuardianWallet Contract"]
PM["PolicyModule Contract"]
RM["RecoveryModule Contract"]
end
APIProxy -->|HTTP JSON| Backend
SDK -->|Soroban XDR + RPC| Contracts
RL -->|Signed XDR| Contracts
RPC -->|JSON-RPC| Contracts
| Technology | |
|---|---|
| Framework | Next.js 16 (App Router) + React 19 |
| Language | TypeScript 5 (strict) |
| Styling | Tailwind CSS v4 + shadcn/ui |
| Server State | TanStack React Query v5 |
| Local State | Zustand |
| Passkeys | @simplewebauthn/browser |
| Session | JWT in httpOnly cookie (jose) |
| Wallet SDK | @rayos/wallet-sdk |
| Blockchain | Stellar (Soroban / Testnet) |
| Unit Tests | Vitest + Testing Library |
| E2E Tests | Playwright (Chromium, Firefox, WebKit) |
| CI | GitHub Actions |
| Deployment | Vercel |
| Package Manager | pnpm 9 |
pnpm test --run # Unit + component tests (Vitest)
pnpm test:e2e # End-to-end tests (Playwright, 3 browsers)
pnpm tsc --noEmit # TypeScript strict check
pnpm lint # ESLint
pnpm build # Production build (zero warnings = CI gate)| Layer | Tool | Coverage |
|---|---|---|
| Component / Unit | Vitest + Testing Library | Form validation, hook states, passkey flows |
| End-to-End | Playwright | Onboarding flow, dashboard navigation |
| Type safety | TypeScript strict |
Every file — no any without justification |
Full testing guide: docs/TESTING.md
# 1. Clone
git clone https://github.com/Rayos-Org/web-dashboard.git
cd web-dashboard
# 2. Install
pnpm install
# 3. Configure
cp .env.local.example .env.local
# → edit .env.local with your relay-backend URL and contract IDs
# 4. Run
pnpm dev
# → http://localhost:3000Complete setup guide with troubleshooting: docs/SETUP.md
| Variable | Description |
|---|---|
NEXT_PUBLIC_RELAY_BACKEND_URL |
relay-backend URL including /api (browser calls go through app/api/* proxies, so this is server-side only in practice) |
NEXT_PUBLIC_WEBAUTHN_RP_ID |
Your domain (must match exactly — use localhost for dev) |
NEXT_PUBLIC_FACTORY_CONTRACT_ID |
Stellar wallet factory contract address |
NEXT_PUBLIC_POLICY_CONTRACT_ID |
Stellar policy module contract address |
NEXT_PUBLIC_SOROBAN_RPC_URL |
Soroban RPC endpoint |
NEXT_PUBLIC_STELLAR_NETWORK_PASSPHRASE |
Stellar network identifier |
SESSION_SECRET |
Server-side JWT secret (≥ 32 chars, never commit) |
- Click the button above — or import the repo manually at vercel.com/new
- Add all environment variables in the Vercel Project Settings
- Set
NEXT_PUBLIC_WEBAUTHN_RP_IDto your production domain (e.g.,rayos-stellar-frontend.vercel.app) - Deploy
The live deployment is at rayos-stellar-frontend.vercel.app
| Document | Description |
|---|---|
| docs/SETUP.md | Local development setup, env vars, troubleshooting |
| docs/ARCHITECTURE.md | Codebase structure, design decisions, request lifecycles |
| docs/CONTRIBUTING.md | How to contribute, coding conventions, PR process |
| docs/TESTING.md | Testing strategy, how to write tests, CI pipeline |
| docs/SECURITY.md | Vulnerability reporting and security policy |
| docs/CODE_OF_CONDUCT.md | Community standards |
We welcome contributions of all kinds — bug reports, feature ideas, documentation fixes, and code.
- Find an issue — browse open issues, especially ones labelled
good first issue - Set up your environment — follow docs/SETUP.md
- Read the conventions — read docs/CONTRIBUTING.md before writing code
- Open a PR — use the PR template, link the issue, fill in every section
⚠️ No mock data rule: The codebase must contain zero dummy/placeholder data in production paths. Every data point shown to the user must come from a real API or on-chain source. See docs/CONTRIBUTING.md → No Mock Data.
MIT — Copyright © 2026 Rayos Org contributors.
Built with ❤️ by the Rayos Org team
Powered by Stellar · Soroban · Passkeys
⭐ Star this repo if you find it useful!