Open-source, drop-in TypeScript library that streams SQL database changes to web clients via WebSockets.
LiveSQL attaches to your existing PostgreSQL (MySQL planned) as a sidecar — no database migration, no vendor lock-in, no architecture changes. A developer can add real-time sync to an existing SQL table in under 10 lines of code with end-to-end latency under 100ms.
| Layer | Choice | Why |
|---|---|---|
| Language | TypeScript (strict) | One language for server + client; contributor accessibility |
| Package manager | pnpm workspaces | Monorepo with independent versioning/publishing |
| Runtime | Node.js 20+ | LTS, native WebSocket support |
| Build | tsup | Fast, zero-config TypeScript bundler |
| Test | vitest | Fast, ESM-native, compatible with TypeScript |
| Lint | eslint + prettier | @typescript-eslint/eslint-plugin |
| Git hooks | husky + lint-staged | Pre-commit formatting and linting |
| Versioning | changesets | Monorepo-aware version management |
| Docs | Docusaurus (Phase 2+) | Used by React Native, Supabase |
| License | Apache 2.0 | Permissive + patent protection |
livesql/
├── packages/
│ ├── core/ # Shared types, wire protocol, ChangeProvider interface
│ ├── server/ # Node.js CDC engine + WebSocket server
│ ├── client/ # Framework-agnostic client SDK
│ ├── react/ # React hooks (useLiveQuery, useLiveTable)
│ ├── vue/ # Vue composables (Phase 2)
│ └── svelte/ # Svelte stores (Phase 2)
├── apps/
│ ├── demo/ # Reference dashboard application
│ └── docs/ # Docusaurus documentation site (Phase 2)
├── tests/
│ ├── integration/ # End-to-end sync correctness tests
│ └── chaos/ # Network partition & failover tests
├── docs/ # Architecture, specs, tracking (for development)
├── CLAUDE.md # This file
├── pnpm-workspace.yaml
└── package.json
These are settled. Do not revisit unless explicitly asked.
-
PostgreSQL WAL logical replication — not LISTEN/NOTIFY. WAL provides guaranteed delivery, historical replay, and no missed events. LISTEN/NOTIFY is unreliable (8KB payload limit, at-most-once delivery, global commit lock). Use LISTEN/NOTIFY only as an optional lightweight signaling complement. See docs/decisions.md ADR-001.
-
WebSockets with offset-based resumption — not HTTP polling or SSE. Persistent connections provide sub-100ms latency. Clients track their last offset and send it on reconnect to resume from the exact position. Server remains stateless (no per-client session storage). See ADR-002.
-
Auth outside the sync engine — not inside it. Follow ElectricSQL's middleware pattern: developers use their existing auth middleware to filter subscriptions before they reach the sync engine. Supabase's model of running RLS per subscriber per change creates N database reads for N subscribers. Separation enables independent scaling. See ADR-003.
-
Server-authoritative model — no CRDTs for v1. The server's state is always canonical. Last-write-wins for conflicts. CRDTs add massive complexity with marginal benefit for most use cases. See ADR-004.
-
ChangeProvider interface from day one — abstract CDC mechanism from transport. Future MySQL support is a matter of implementing the interface, not restructuring. See ADR-005.
-
TypeScript monorepo — one language for both server and client. Maximizes contributor accessibility and enables shared type definitions across the wire protocol. See ADR-006.
strict: truein all tsconfig filesexactOptionalPropertyTypes: truenoUncheckedIndexedAccess: true- Target: ES2022, Module: NodeNext
- Use
interfacefor object shapes,typefor unions/intersections - Export types explicitly — no
export * - Prefer
async/awaitover raw Promises - Use
BigIntfor offsets and LSNs (not number)
- Files:
kebab-case.ts(e.g.,slot-health.ts,live-sql-client.ts) - Classes:
PascalCase(e.g.,PostgresProvider,LiveSQLClient) - Functions/variables:
camelCase - Constants:
UPPER_SNAKE_CASEfor true constants - Interfaces:
PascalCase, noIprefix - Types:
PascalCase
packages/<name>/
├── src/
│ ├── index.ts # Public API exports only
│ ├── <feature>.ts # Implementation files
│ └── __tests__/ # Co-located unit tests
├── package.json
├── tsconfig.json
└── tsconfig.build.json
- NEVER execute client-provided SQL against the database
- NEVER expose raw database ports (5432, 3306) to clients
- ALWAYS validate filter expressions against a strict allowlist of column names and operators
- ALWAYS require JWT authentication on WebSocket handshake
- ALWAYS check table-level permissions on subscribe
- ALWAYS check row-level permissions on every change event
- ALWAYS set
REPLICA IDENTITY FULLon watched tables - ALWAYS set
max_slot_wal_keep_sizein PostgreSQL config
# Install dependencies
pnpm install
# Build all packages
pnpm build
# Build a specific package
pnpm --filter @livesql/core build
# Run all tests
pnpm test
# Run tests for a specific package
pnpm --filter @livesql/server test
# Lint
pnpm lint
# Format
pnpm format
# Type check
pnpm typecheck
# Start demo app
pnpm --filter demo dev
# Start dev PostgreSQL (Docker)
docker compose -f docker-compose.test.yml up -dUse conventional commits:
feat(core): add ChangeEvent wire protocol types
fix(server): prevent WAL slot leak on disconnect
test(integration): add reconnection backfill test
docs: update architecture diagram
chore: configure pnpm workspace
feat/<short-description>
fix/<short-description>
docs/<short-description>
chore/<short-description>
- Make atomic commits — each commit should be a single logical change
- Do NOT add co-author lines (no
Co-Authored-By) - Do NOT auto-commit — only commit when explicitly asked
- Do NOT amend unless explicitly asked — always create new commits
- Do NOT push unless explicitly asked
- Run
git statusandgit diffbefore committing to verify changes - Stage specific files by name — avoid
git add -Aorgit add .
v1.0.0-beta.1 Release
Phases 0–3 are complete. All features implemented, 163 tests passing (unit, integration, chaos), k6 load tested at 1,000 concurrent clients with p95 96ms.
Current sprint: publish v1.0.0-beta.1 to npm, collect community feedback for 2–4 weeks, then promote to v1.0.0 stable.
See docs/implementation-plan.md for all phases. See docs/progress.md for current task tracking.
- Autonomy: Make reasonable implementation decisions and explain your choices. Only ask for major architectural direction changes.
- Before coding: Always read the relevant existing files first. Check docs/progress.md for current status.
- New sessions: Read this file first, then docs/progress.md to understand where we left off.
- Architecture reference: See docs/architecture.md for system design, docs/api-spec.md for API contracts.
- Known dangers: See docs/failure-modes.md before touching CDC, WebSocket, or reconnection code.
- Testing: Every feature needs tests. Use vitest. Integration tests need Docker PostgreSQL.
- Don't over-engineer: Ship the simplest thing that works for the current phase. No premature abstractions.
- Package boundaries: core has zero dependencies. server depends on core + pg + ws. client depends on core. react/vue/svelte depend on client.
- Run
pnpm installat the root - Run
docker compose -f docker-compose.test.yml up -dfor a test PostgreSQL instance - Each package builds independently:
pnpm --filter @livesql/<package> build - Tests require a running PostgreSQL with
wal_level=logical - See docs/architecture.md for system overview before diving into code
| Doc | Purpose |
|---|---|
| docs/architecture.md | System design, wire protocol, data flow |
| docs/api-spec.md | Full API specification for all packages |
| docs/implementation-plan.md | Phased roadmap with deliverables |
| docs/progress.md | Current task tracking (checkboxes) |
| docs/decisions.md | Architecture Decision Records |
| docs/failure-modes.md | Production failure modes & mitigations |