A personal knowledge repository that aggregates data from multiple external sources into a searchable, relational database. Built with React 19, TanStack Router, tRPC, Drizzle ORM, and PostgreSQL, deployed on Bun server.
- Frontend: React 19 + TanStack (Start, Router, Query) + Tailwind CSS v4
- Backend: tRPC + Drizzle ORM + PostgreSQL
- Deployment/Hosting: Bun server + local PostgreSQL on a Tailscale network
- Search: PostgreSQL full-text search + OpenAI embeddings
Before you begin, ensure you have the following installed:
- Bun runtime & package manager (
curl -fsSL https://bun.sh/install | bash) - Node.js v24+ (check with
node --version) - PostgreSQL (v14+ recommended) with the following extensions:
vector- for vector embeddings (install viaCREATE EXTENSION vector;)pg_trgm- for trigram text search (install viaCREATE EXTENSION pg_trgm;)- Note: Extensions are automatically created by migrations if your user has permission
- Git for version control
- Cloudflare Account - for R2 storage
- Airtable - for Airtable integration
- Arc Browser - for Arc Browser integration
- Dia Browser - for Dia Browser integration
- Feedbin - for RSS feed integration
- GitHub - for GitHub integration
- Raindrop.io - for Raindrop.io integration
- Readwise - for Readwise integration
- Twitter/X - for Twitter/X bookmarks integration
- Adobe Lightroom - for Adobe Lightroom integration
git clone https://github.com/yourusername/red-cliff-record.git
cd red-cliff-recordbun installbun linkNote about database providers: The app connects to a local PostgreSQL database. If you use a different provider, update src/server/db/connections/postgres.ts accordingly.
# Connect to PostgreSQL
psql -U postgres
# Create database
CREATE DATABASE redcliffrecord;
# Exit psql
\q- Copy the example environment file:
cp .env.example .env- Update the
DATABASE_URLin.env:
DATABASE_URL="postgresql://username:password@localhost:5432/redcliffrecord"
The migration system uses Drizzle ORM and includes all necessary PostgreSQL extensions (vector, pg_trgm) in the initial migration. Run migrations with:
bun run db:migrateNote: The initial migration (0000_rapid_triathlon.sql) creates the extensions schema and installs required extensions. Ensure your PostgreSQL user has permission to create extensions, or install them manually before running migrations:
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS pg_trgm;After running migrations, seed the database with initial predicate vocabulary and core records:
./src/server/db/db-manager.sh seed localThis loads:
- Predicates: Canonical relationship types (e.g.,
created_by,contains,references,related_to) - Records: Core entities (e.g., user record, project record)
The seed script is idempotent and safe to run multiple times—it uses upsert logic to avoid duplicates.
To inspect your database:
bun run db:studioEdit your .env file and add API keys for the services you want to use:
- OpenAI - For generating embeddings
OPENAI_API_KEY=sk-...
Each integration is optional. Only configure the ones you need:
-
GitHub - For syncing repositories and stars
GITHUB_TOKEN=ghp_...Create a token with
repoanduserscopes. -
Airtable - For syncing Airtable bases
AIRTABLE_BASE_ID=app... AIRTABLE_ACCESS_TOKEN=pat... -
Raindrop.io - For syncing bookmarks
RAINDROP_TEST_TOKEN=...Create an app and get a test token.
-
Readwise - For syncing highlights
READWISE_TOKEN=... -
Feedbin - For syncing RSS feeds and entries
FEEDBIN_USERNAME=your@email.com FEEDBIN_PASSWORD=your-passwordSign up at feedbin.com
-
Adobe Lightroom - For syncing photos from a Lightroom album
Note: Currently hardcoded to the author's album. See INTEGRATIONS.md for setup details.
For media storage, you'll need a Cloudflare R2 bucket:
- Create a Cloudflare account
- Create an R2 bucket
- Create API tokens with R2 read/write permissions
- Update your
.env:CLOUDFLARE_ACCOUNT_ID=... S3_ACCESS_KEY_ID=... S3_SECRET_ACCESS_KEY=... S3_REGION=auto S3_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com S3_BUCKET=your-bucket-name ASSETS_DOMAIN=https://your-assets-domain.com
bun run devrcr is a local CLI wrapper around the same tRPC procedures used by the app. It is JSON-first by default and supports a table output for quick inspection.
Install the CLI globally once from the repo:
bun link# General help
rcr --help
# Records
rcr records get 123
rcr records get 123 --links # Include all incoming/outgoing links
rcr records get 123 456 789 # Multiple IDs in parallel
rcr records list --type=entity --limit=10
rcr records list --source=github --limit=10
rcr records create '{"title":"Example","type":"concept"}'
# Search
rcr search "machine learning"
rcr search semantic "machine learning" --limit=5
rcr search similar 456 --limit=5
# Links
rcr links list 123
rcr links list 123 456 # Multiple records
rcr links create '{"sourceId":1,"targetId":2,"predicateId":3}'
# Sync integrations
rcr sync github
rcr sync airtable
rcr sync raindrop
rcr sync readwise
rcr sync feedbin
rcr sync adobe
rcr sync browsing # Arc + Dia browser history (macOS)
rcr sync twitter
rcr sync agents # Claude, Codex, Cursor histories
rcr sync avatars # Transfer avatars to R2
rcr sync embeddings # Generate embeddings for records
rcr sync # Run all daily syncsNotes:
- Outputs compact JSON by default; pipe to
jqfor formatting. - Unknown flags are rejected (strict parsing).
- Most ID-based commands accept multiple IDs for parallel execution.
- Use
--format=tablefor human-readable output. - Use
--debugto fetch data without writing to the database (outputs to.temp/). - Use
--to stop option parsing when needed.
bun run build- Upload the
distfolder to your server and start the Bun server. - Ensure all environment variables from
.envare configured on your host.
bun run dev # Start development server
bun run build # Build for production
bun check # Lint (oxlint) + type-check (tsgo) + format (oxfmt) — fast, run often
bun run lint # Lint + type-check only
bun run format # Format only
bun run db:studio # Open Drizzle Studio
bun run db:migrate # Run migrationsAll backup/restore operations go through db-manager.sh (or the rcr db CLI wrapper):
rcr db backup <prod|dev> # Backup (creates prod-{timestamp}.dump or dev-{timestamp}.dump)
rcr db restore <prod|dev> # Restore most recent backup
rcr db restore dev --file path.dump # Restore a specific backup file
rcr db seed dev # Seed predicates + core records
rcr db reset dev # Drop & recreate DB with extensions
rcr db clone-prod-to-dev # Clone production → developmentFlags: --dry-run (-n) prints commands without executing. -D operates on data only (no schema). -c does a clean restore (drop & recreate first). --file (-f) restores from a specific file instead of auto-discovering. Restores terminate existing connections before running pg_restore.
Backup files are named by environment label (prod-, dev-), not database name. Restore auto-discovers the most recent .dump file in the backup directory.
Reset workflow (squash migrations while preserving data):
rcr db backup dev --data-onlyrcr db reset devrm -rf migrations/main/*(optional)bun run db:generate(if step 3 was done)NODE_ENV=development bunx drizzle-kit migratercr db seed devrcr db restore dev --data-only
- Ensure PostgreSQL is running:
pg_ctl statusorbrew services list(macOS) - Check your
DATABASE_URLformat and credentials - Verify the database exists:
psql -U postgres -l
- Clear cache:
rm -rf node_modules bun.lock dist && bun install - Check Node version: Should be v24+ as specified in
.nvmrc - Run lint and type check:
bun run lint
- Verify API keys are correct and have proper permissions
- Check rate limits for external services
- Run with
--debugto test API connectivity without writing to the database:rcr sync github --debug # Outputs raw API data to .temp/
- Ensure Arc browser is installed and has been used
- The integration reads from:
~/Library/Application Support/Arc/User Data/Default/History - May require security permissions in System Preferences
- Ensure Dia browser is installed and has been used
- The integration reads from:
~/Library/Application Support/Dia/ - May require security permissions in System Preferences
- The sync runs Arc and Dia sequentially under one integration run
- Each browser maintains its own sync timestamp per hostname
- The browser sync works for any Chromium-based browser with path configuration
MIT