Lattice is a real-time collaborative document editor built with a Go microservice backend and a Next.js frontend. It combines Yjs CRDTs for conflict-free editing, Redis Pub/Sub for fan-out, and PostgreSQL for durable storage.
- Real-time document collaboration over WebSockets
- Role-based access control for document sharing
- Stateless HTTP services with a dedicated sync service
- Redis-backed broadcast and a persistence worker
- Print-ready editor layout and A4 page styles
Lattice is split into a set of focused services that scale independently. The real-time sync service manages WebSocket connections, while a background worker persists CRDT updates and compacted snapshots.
For a deeper write-up, see docs/flow.md.
| Service | Purpose | Port | Notes |
|---|---|---|---|
| api-gateway | Auth, user search, health checks | 8080 | Swagger UI at /swagger/index.html |
| doc-service | Document CRUD and permissions | 8081 | Swagger UI at /swagger/index.html |
| sync-service | WebSocket collaboration | 8082 | WebSocket endpoint /ws/:id |
| persist-worker | Persist CRDT updates | n/a | Consumes Redis Pub/Sub |
| frontend | Next.js web app | 3000 | UI and editor |
| postgres | Primary data store | 5432 | Users, documents, permissions |
| redis | Pub/Sub and buffers | 6379 | Fan-out and persistence queues |
| minio | Object storage (optional) | 9000 | Local S3-compatible storage |
Core tables are created on startup by the backend services:
users: accounts and display namesdocuments: document metadata and compacted snapshotsdocument_permissions: per-user access (viewer/editor)document_updates: persisted CRDT updates
- Frontend: Next.js 16, React 19, TypeScript, Tailwind CSS
- Editor: ProseMirror, y-prosemirror, Yjs
- Backend: Go 1.25, Echo, gorilla/websocket
- Data: PostgreSQL, Redis
- Docs: Swagger/OpenAPI
From the backend directory:
cd backend
docker compose --env-file ./.env -f deployments/docker-compose.yaml up --buildThen open:
- Frontend: http://localhost:3000
- API health: http://localhost:8080/health
- Doc service Swagger: http://localhost:8081/swagger/index.html
From the repo root:
make infra-upStop the stack:
make infra-downClean volumes:
make infra-clean- Create
backend/.env.localwith host-based settings:
DATABASE_URL=postgres://lattice_user:lattice@localhost:5432/lattice?sslmode=disable
DB_USER=lattice_user
DB_HOST=localhost
DB_PASSWORD=lattice
DB_PORT=5432
DB_NAME=lattice
REDIS_ADDR=localhost:6379
JWT_SECRET=lattice_secret- Load the env file and run services:
cd backend
set -a && source .env.local && set +a
make run-api
make run-doc
make run-sync
make run-worker- Frontend (separate terminal):
cd frontend
npm install
npm run devThe frontend uses these env vars when running locally:
NEXT_PUBLIC_API_BASE_URL(defaulthttp://localhost:8080)NEXT_PUBLIC_DOCS_BASE_URL(defaulthttp://localhost:8081)NEXT_PUBLIC_SYNC_BASE_URL(defaultws://localhost:8082)
See SECURITY.md for reporting guidelines.






