An AI-powered second brain — capture thoughts, auto-organise them into topic clusters (suns), and chat with your knowledge through a real AI assistant.
- Capture — fire off quick thoughts, notes, and ideas (called rays)
- Auto-cluster — an embedding + LLM pipeline groups rays into topics (suns) automatically
- AI assistant — a chat panel per note that can read, write, and manage your rays via function calling
- Workspaces — share collections of rays with collaborators
- Native app — ships as a Tauri desktop app for Mac and Windows alongside the web version
- Node.js ≥ 20 and
pnpm(ornpm) - A Supabase account (free tier works)
- A DeepSeek API key
- Vercel CLI for running the API routes locally:
npm i -g vercel
git clone https://github.com/0xDI/sonlum.git
cd sonlum
pnpm installMac / Linux:
bash setup.shWindows (PowerShell):
.\setup.ps1The script asks for your Supabase URL, keys, and DeepSeek key, then writes .env and .env.vercel for you.
You can also copy .env.example and fill it in manually.
- Open your Supabase dashboard → SQL Editor
- Paste the entire contents of
supabase/schema.sql - Click Run
This creates all tables, RLS policies, and indexes.
vercel devThe app starts at http://localhost:3000. The /api/* routes (data load/save) run alongside it.
pnpm dev/npm run devalso works for frontend-only development, but data load/save requires the Vercel API routes (vercel dev).
vercelThen go to your Vercel project dashboard → Settings → Environment Variables and add:
| Variable | Where to find it |
|---|---|
VITE_SUPABASE_URL |
Supabase → Project Settings → API |
VITE_SUPABASE_ANON_KEY |
Supabase → Project Settings → API |
VITE_DEEPSEEK_API_KEY |
platform.deepseek.com/api_keys |
SUPABASE_URL |
Same as VITE_SUPABASE_URL |
SUPABASE_SERVICE_ROLE_KEY |
Supabase → Project Settings → API (service_role) |
ENCRYPTION_SECRET |
32 random hex bytes — openssl rand -hex 32 |
Push to main to deploy.
See .env.example for a fully annotated list.
| Variable | Side | Required | Description |
|---|---|---|---|
VITE_SUPABASE_URL |
client | ✓ | Supabase project URL |
VITE_SUPABASE_ANON_KEY |
client | ✓ | Supabase anon key (public) |
VITE_DEEPSEEK_API_KEY |
client | ✓ | DeepSeek key for AI features |
SUPABASE_URL |
server | ✓ | Same URL, used by API routes |
SUPABASE_SERVICE_ROLE_KEY |
server | ✓ | Bypasses RLS — keep secret |
ENCRYPTION_SECRET |
server | ✓ | AES-256-GCM key derivation seed — keep secret |
sonlum/
├── api/ # Vercel serverless functions
│ ├── _crypto.js # AES-256-GCM encryption helpers
│ ├── load.js # Fetch all user data in one call
│ ├── save.js # Persist rays, suns, workspaces
│ ├── accept-invites.js # Workspace invite acceptance
│ ├── update-user.js # Profile updates
│ └── upload-avatar.js # Avatar upload
├── src/
│ ├── ai/ # AI pipeline
│ │ ├── classifier.ts # DeepSeek ray clustering
│ │ ├── clustering.ts # Cluster orchestration
│ │ ├── embeddings.ts # Cosine similarity helpers
│ │ └── semantics.ts # Semantic fallback dictionary
│ ├── components/ # React components
│ ├── store/ # Zustand stores
│ │ ├── useStore.ts # Main app state (rays, suns, clustering)
│ │ ├── useAIChat.ts # Per-ray chat history
│ │ ├── useAuth.ts # Auth state
│ │ ├── useSettings.ts # App settings + storage mode
│ │ └── useWorkspace.ts # Workspace selection
│ └── lib/
│ ├── supabase.ts # Supabase client
│ └── supabase-db.ts # DB query helpers
├── supabase/
│ ├── schema.sql # Full DB schema — run this in Supabase SQL Editor
│ └── migrations/ # Incremental migrations
├── src-tauri/ # Tauri desktop app
├── setup.sh # Interactive setup (Mac/Linux)
├── setup.ps1 # Interactive setup (Windows)
└── .env.example # Annotated env vars template
A ray is a short thought or note. When you save a ray, the app runs an embedding pipeline (via Transformers.js — entirely in the browser, no external model call) and compares it against existing rays. Similar rays are clustered into a sun by DeepSeek, which also names the cluster.
Clustering is debounced at 2.5 s and re-runs when the queue is non-empty after a cluster finishes.
Ray content (content, raw_content, body) and sun names are encrypted at rest using AES-256-GCM before being written to Supabase. Keys are derived per-user from ENCRYPTION_SECRET using HKDF-SHA256. Decryption happens in the Vercel API route, so the database never stores plaintext.
The Tauri desktop app calls Supabase directly (no Vercel proxy) and stores data unencrypted. This is a known trade-off — the desktop app is local-first by design.
Users can choose between Sonlum Cloud (Supabase, with encryption) and Local Storage (browser localStorage, no account needed). Switching between modes offers to migrate existing data.
- The service role key and ENCRYPTION_SECRET must never be committed or exposed client-side.
- The anon key is intentionally public — Supabase RLS policies enforce per-user access.
- The DeepSeek key is used in the browser. For a production deployment you may want to proxy AI calls through your own server to keep the key hidden.
- Supabase RLS is enabled on all tables. See
supabase/schema.sqlfor the policies.
| Layer | Tech |
|---|---|
| Frontend | React 18 + TypeScript + Tailwind CSS + Framer Motion |
| State | Zustand with persist middleware |
| Backend | Supabase (Postgres + Auth + Realtime) |
| API layer | Vercel serverless functions |
| Embeddings | Transformers.js (in-browser, Xenova/all-MiniLM-L6-v2) |
| AI | DeepSeek Chat (function calling) |
| Desktop | Tauri 2 |
MIT — free to use, modify, and self-host.
Built by 0xDI
