An open-source knowledge management system built on Supabase. Capture thoughts, search semantically, and build your second brain with PostgreSQL, pgvector, and Edge Functions.
No hosted service. No vendor lock-in. Just your own Supabase project and your own data.
- Semantic Search — find thoughts by meaning, not just keywords, using pgvector and OpenAI embeddings
- Full-Text Search — PostgreSQL websearch (
wfts) fallback for instant keyword matching - Spaces — organise thoughts into flexible, overlapping containers
- Knowledge Graph — link thoughts together with named relationships
- Decision Log — track decisions with context, alternatives considered, and outcomes
- Edge Functions — serverless API for capturing and searching knowledge
- TypeScript Client — fully typed client library with intuitive methods
- Row Level Security — production-ready RLS policies out of the box
┌─────────────────────────────────────────────────────────┐
│ Your Application │
│ (Web app, CLI, AI assistant) │
└──────────────────────┬──────────────────────────────────┘
│
┌────────▼────────┐
│ TypeScript SDK │ src/client.ts
│ (SecondBrain) │
└────────┬────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌───────────┐ ┌──────────┐ ┌────────────┐
│ capture- │ │ search │ │get-context │ Edge Functions
│ thought │ │ │ │ │
└─────┬─────┘ └────┬─────┘ └─────┬──────┘
│ │ │
└─────────────┼─────────────┘
▼
┌────────────────┐
│ PostgreSQL │
│ + pgvector │ Supabase
│ + RLS │
└────────────────┘
git clone https://github.com/YOUR_USERNAME/supabase-second-brain.git
cd supabase-second-brain
npm installCreate a project at supabase.com (the free tier works fine).
Enable the vector extension in your project:
- Go to Database > Extensions and search for
vector - Or run:
CREATE EXTENSION IF NOT EXISTS vector;
cp .env.example .env
# Edit .env with your Supabase credentials and OpenAI API key# Link to your Supabase project
npx supabase link --project-ref YOUR_PROJECT_REF
# Push migrations
npm run db:pushThis creates the tables, indexes, RPC functions, and RLS policies.
# Set your OpenAI key as an Edge Function secret
npx supabase secrets set OPENAI_API_KEY=sk-your-key
# Deploy all functions
npm run functions:deployimport { SecondBrain } from "supabase-second-brain";
const brain = new SecondBrain({
supabaseUrl: process.env.SUPABASE_URL!,
supabaseKey: process.env.SUPABASE_ANON_KEY!,
});
// Capture a thought
const thought = await brain.captureThought({
content: "Supabase + pgvector makes semantic search incredibly simple",
type: "thought",
tags: ["supabase", "ai", "search"],
space: "Engineering",
});
// Search your knowledge
const { results } = await brain.search({
query: "how does semantic search work",
});
// Get context for an AI assistant
const context = await brain.getContext({
query: "What do I know about vector databases?",
});
// Log a decision
await brain.logDecision({
title: "Choose pgvector over Pinecone",
context: "Need vector search for the second brain project",
decision: "Use pgvector via Supabase",
alternatives: [
{ option: "Pinecone", reason_rejected: "External dependency, extra cost" },
{ option: "Weaviate", reason_rejected: "Self-hosted complexity" },
],
});| Method | Description |
|---|---|
captureThought(input) |
Capture a new thought with auto-generated embedding |
search(input) |
Semantic search with text fallback |
getContext(input) |
Combined search for AI assistant context |
logDecision(input) |
Log a decision with alternatives |
listDecisions(limit?) |
List recent decisions |
createSpace(input) |
Create an organisational space |
listSpaces() |
List all spaces |
linkThoughts(input) |
Create a link between two thoughts |
getLinkedThoughts(id) |
Get all thoughts linked to a thought |
listRecent(options?) |
List recent thoughts with filters |
getThought(id) |
Get a thought by ID |
updateThought(id, updates) |
Update a thought |
deleteThought(id) |
Delete a thought |
| Endpoint | Method | Description |
|---|---|---|
/capture-thought |
POST | Capture a thought with embedding generation |
/search |
POST | Search with semantic + text fallback |
/get-context |
POST | Get contextual results for AI assistants |
| Type | Use For |
|---|---|
thought |
General knowledge, observations, notes |
decision |
Decisions and their rationale |
entity |
People, companies, projects, concepts |
reference |
Links, articles, external resources |
meeting |
Meeting notes and action items |
idea |
Ideas, hypotheses, things to explore |
| Variable | Required | Description |
|---|---|---|
SUPABASE_URL |
Yes | Your Supabase project URL |
SUPABASE_ANON_KEY |
Yes | Supabase anonymous/public key |
SUPABASE_SERVICE_ROLE_KEY |
Edge Functions | Service role key (used by Edge Functions) |
OPENAI_API_KEY |
Yes | OpenAI API key for embedding generation |
thoughts— core knowledge entries with content, type, tags, metadata, and vector embeddingspaces— organisational containers (a thought can belong to multiple spaces)thought_spaces— many-to-many junction tablethought_links— directed links between thoughts (knowledge graph)decisions— decision log with context, alternatives, and outcomes
search_thoughts— cosine similarity search via pgvectorsearch_thoughts_text— full-text search usingwebsearch_to_tsquery(wfts)get_context— combined semantic (70%) + text (30%) weighted searchlist_recent— paginated listing with type/space filters
- Update the
CHECKconstraint onthoughts.typein a new migration - Add the type to
ThoughtTypeinsrc/types.ts - Create any type-specific RPC functions you need
The Edge Functions use OpenAI's text-embedding-ada-002 by default. To switch providers:
- Update the
generateEmbedding()function in each Edge Function - Ensure your new model outputs 1536-dimension vectors (or update the
VECTOR(1536)column definition)
See the comments in supabase/migrations/003_rls_policies.sql for instructions on adding per-user isolation with a user_id column.
See CONTRIBUTING.md for guidelines.