A self-hosted, offline-first personal finance PWA for tracking transactions and managing bank accounts. Built with SvelteKit, Dexie.js, and Supabase, Radiant connects to your bank accounts via Teller.io and syncs transactions in real-time -- all while keeping your financial data under your control. Works offline, syncs across devices, and runs entirely on your own infrastructure.
Try it at: https://finance.prabhas.io/demo
| Document | Description |
|---|---|
| FRAMEWORKS.md | Complete guide to all frameworks and architectural patterns |
| ARCHITECTURE.md | System design, sync engine, conflict resolution, auth flows |
| stellar-drive | Offline-first sync engine powering the data layer |
git clone https://github.com/your-username/radiant.git
cd radiant
npm install
cp .env.example .env
Edit .env with your credentials. See the Configuration section below for a full reference.
.envDATABASE_URL for auto schema syncnpm run dev and npm run build -- no manual SQL needed.envnpm run dev
Visit http://localhost:5173. Types auto-generate and Supabase auto-migrates on first run.
adapter-auto/api/config to confirm server config is loadedOr use the built-in setup wizard at /setup for guided deployment with validation.
Security note: DATABASE_URL is only used server-side during the build step. It is never bundled into client code. The public Supabase keys are safe to expose -- they are protected by Row Level Security.
Once installed, the app runs as a standalone window with full offline support.
Radiant includes a full demo mode for evaluating the app without any backend configuration.
How it works:
/demo on any running instance and click Start Demo to explore with pre-populated sample data.${name}_demo), never touches real data.authMode === 'demo') enables protected routes without real authentication.seedData(db).What's included:
For developers:
src/lib/demo/mockData.ts to customize the sample data.src/lib/demo/config.ts to customize the mock user profile.| Variable | Description | Required |
|---|---|---|
PUBLIC_SUPABASE_URL |
Supabase project URL | Yes |
PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY |
Supabase publishable (anon) key | Yes |
DATABASE_URL |
Supabase Postgres connection string (URI) | Yes (for schema sync) |
| Variable | Description | Required |
|---|---|---|
PUBLIC_TELLER_APP_ID |
Teller application ID | Yes (for bank connections) |
PUBLIC_TELLER_ENVIRONMENT |
Teller API environment (sandbox or production) |
No (defaults to sandbox) |
TELLER_CERT |
mTLS certificate (PEM or base64) | Yes (for bank connections) |
TELLER_KEY |
mTLS private key (PEM or base64) | Yes (for bank connections) |
TELLER_WEBHOOK_SECRET |
Webhook HMAC signing secret | Yes (for webhooks) |
| Variable | Description | Default |
|---|---|---|
PUBLIC_APP_NAME |
Application display name | Radiant |
SYNC_INTERVAL_MS |
Background sync interval in milliseconds | 30000 |
| Layer | Technology |
|---|---|
| Framework | SvelteKit 2 + Svelte 5 |
| Sync Engine | stellar-drive (stellar-drive) |
| Database | Supabase (PostgreSQL) |
| Local Storage | IndexedDB (Dexie) |
| Auth | Supabase Auth + PIN gate |
| Banking | Teller.io |
| Deployment | Vercel (adapter-auto) |
| Language | TypeScript (strict) |
| PWA | Service Worker (stellarPWA) |
| Design | CSS Custom Properties |
src/
lib/
schema.ts --> Single source of truth (database schema)
types.ts --> App types (re-exports + narrowings)
types.generated.ts --> Auto-generated entity types (do not edit)
components/ --> Svelte 5 components
stores/ --> Reactive stores (collection + detail)
demo/ --> Demo mode config and mock data
routes/
+layout.ts --> Root load function (engine init, auth)
+layout.svelte --> App shell
(app)/ --> Authenticated routes
+page.svelte --> Dashboard (greeting)
transactions/ --> Filterable transaction list
accounts/ --> Connected accounts + Teller Connect
profile/ --> Settings, PIN, devices, diagnostics
login/ --> PIN auth (setup/unlock/link modes)
setup/ --> Configuration wizard
demo/ --> Demo mode toggle
confirm/ --> Email verification
policy/ --> Privacy policy
api/ --> Server endpoints
config/ --> Server config check
setup/ --> Credential validation + deployment
teller/ --> Teller sync proxy + webhooks
service-worker.ts --> PWA service worker
static/
sw.js --> Generated service worker
manifest.json --> PWA manifest
.env.example --> Environment variable template
| Command | Description |
|---|---|
npm run dev |
Start development server (types auto-generate, schema auto-syncs) |
npm run build |
Production build (includes schema sync) |
npm run check |
Type-check with svelte-check |
npm run lint |
Lint with ESLint |
npm run format |
Format with Prettier |
npm run dead-code |
Dead code detection with Knip |
npm run cleanup |
Auto-fix lint + format |
npm run validate |
Full validation (check + lint + dead-code) |
npm install
npm run dev
| Command | Description |
|---|---|
npm run build |
Production build with schema sync |
npm run cleanup |
Auto-fix formatting and lint issues |
npm run validate |
Full validation (type check + lint + dead-code detection) |
git checkout -b feature/my-featurenpm installnpm run cleanup
npm run validate
If your change modifies src/lib/schema.ts, types auto-generate at src/lib/types.generated.ts on save, Supabase schema auto-syncs if DATABASE_URL is set, and IndexedDB auto-upgrades on next page load via hash-based version detection. Never edit types.generated.ts manually.
Radiant is self-hosted. Your financial data lives in your own Supabase instance on infrastructure you control. Bank credentials are handled entirely by Teller.io using mutual TLS authentication; your server never sees login credentials. There is no third-party analytics, no tracking, and no telemetry.