Skip to content

Latest commit

 

History

45 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Guardian Wallet

Guardian Wallet — Web Dashboard

A passkey-powered Stellar smart wallet with on-chain policies and social recovery.
No seed phrases. No passwords. Just your biometrics.

Live on Vercel Next.js 16 React 19 TypeScript Tailwind CSS v4 shadcn/ui Stellar pnpm CI MIT License

🚀 Live Demo · 📖 Setup Guide · 🏛️ Architecture · 🤝 Contributing · 🐛 Report a Bug


What is Guardian Wallet?

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.


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

Features

🔑 Passkey-Powered Authentication

  • 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

💸 Send & Receive XLM

  • 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 Policies & Guardians (preview)

  • 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

👥 Social Recovery

  • 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

🎨 UI / UX

  • Dark mode by default (animated light/dark switch), Geist Sans + Geist Mono, Indigo accent (#4F46E5 / #6D6AF7 on 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

File Architecture

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


User Workflow

New User — Wallet Creation

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
Loading

Returning User — Sign In

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
Loading

Sending a Transaction

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
Loading

System Architecture

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
Loading

Tech Stack

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

Testing

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


Getting Started

# 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:3000

Complete setup guide with troubleshooting: docs/SETUP.md


Environment Variables

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)

Deploying to Vercel

Deploy with Vercel

  1. Click the button above — or import the repo manually at vercel.com/new
  2. Add all environment variables in the Vercel Project Settings
  3. Set NEXT_PUBLIC_WEBAUTHN_RP_ID to your production domain (e.g., rayos-stellar-frontend.vercel.app)
  4. Deploy

The live deployment is at rayos-stellar-frontend.vercel.app


Documentation

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

Contributing

We welcome contributions of all kinds — bug reports, feature ideas, documentation fixes, and code.

  1. Find an issue — browse open issues, especially ones labelled good first issue
  2. Set up your environment — follow docs/SETUP.md
  3. Read the conventions — read docs/CONTRIBUTING.md before writing code
  4. 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.


License

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!

About

A passkey-powered Stellar smart wallet with on-chain policies and social recovery. No seed phrases. No passwords. Just your biometrics.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages