Caret’s backend is composed of five independently deployed services. Four are written in TypeScript and run on Node.js using the Bun runtime; the fifth is a Python FastAPI application managed withDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/arrozet/caret/llms.txt
Use this file to discover all available pages before exploring further.
uv. Each service owns a single domain of responsibility and exposes its own API documentation endpoint. All services share the same Supabase Cloud PostgreSQL database and validate Supabase JWTs for request authentication.
Service Overview
| Service | Stack | Port | Role |
|---|---|---|---|
api-gateway | Express 5 + TypeScript | 3000 | Public REST entrypoint, CORS, rate limiting, proxy routing |
auth-service | Express 5 + TypeScript | 3001 | Auth API surface and OpenAPI metadata |
document-service | Express 5 + TypeScript + Drizzle | 3002 | Workspaces, folders, documents, members, versions |
collab-service | Node HTTP + ws + Y.js | 3003 | Direct WebSocket collaboration and awareness |
ai-service | FastAPI + SQLAlchemy async + PydanticAI | 8000 | AI chat, SSE streaming, suggestions, embeddings/RAG |
API Gateway (port 3000)
The API Gateway is the single public entrypoint for all REST and SSE traffic from the frontend. It is the only service exposed to the public internet viaapi.caret.page.
Stack: Express 5 + TypeScript, built and run with Bun.
Responsibilities:
- Apply CORS headers and rate limiting before traffic reaches downstream services.
- Proxy incoming requests at
/api/v1/...to the correct downstream service based on path prefix. - Serve the aggregated OpenAPI spec and docs UI so API consumers have one URL to visit.
| Path prefix | Downstream service |
|---|---|
/api/v1/auth | auth-service (port 3001) |
/api/v1/documents | document-service (port 3002) |
/api/v1/workspaces | document-service (port 3002) |
/api/v1/folders | document-service (port 3002) |
/api/v1/ai | ai-service (port 8000) |
/openapi.json, interactive docs UI at /docs.
WebSocket connections for collaboration are not routed through the API Gateway. The frontend opens a direct WebSocket connection to the collab-service. See Auth & Security for how the JWT is passed on the WebSocket handshake.
Auth Service (port 3001)
The Auth Service is intentionally lightweight. Supabase Auth is the real authentication engine; this service exists to provide a consistent auth API surface behind the gateway and to expose OpenAPI documentation for auth-related contracts. Stack: Express 5 + TypeScript, built and run with Bun. Responsibilities:- Respond to health check requests from the gateway and infrastructure probes.
- Expose OpenAPI metadata for auth-related endpoints (login, logout, token refresh contracts).
- Mount error-handling middleware and shared auth controller stubs.
SUPABASE_JWT_SECRET.
API docs: Interactive docs UI at /docs.
Document Service (port 3002)
The Document Service is the primary CRUD service for all user content organization. It owns workspaces, folders, documents, document memberships, and document version history. Stack: Express 5 + TypeScript + Drizzle ORM, built and run with Bun. Responsibilities:- Manage workspace creation, membership, and settings.
- Manage hierarchical folder trees within workspaces.
- Manage document metadata, visibility, sharing permissions, and lifecycle status.
- Create and retrieve immutable document version snapshots (ProseMirror JSON + plain text).
- Enforce workspace-scoped and document-scoped RBAC through service logic and RLS policies.
workspaces, workspace_members, folders, documents, document_members, document_versions, user_profiles. See Database for the full schema.
Migrations: Drizzle-managed SQL migrations live in src/db/migrations/. Run bun run drizzle commands from the service directory.
API docs: Interactive docs UI at /docs.
Collab Service (port 3003)
The Collab Service enables real-time collaborative editing using the Y.js CRDT protocol. It is the only service that accepts WebSocket connections from the frontend and the only service not behind the API Gateway. Stack: Node.js HTTP server +ws WebSocket library + Y.js, built and run with Bun.
WebSocket endpoint: ws://localhost:3003/document/{doc_id}?token={jwt}
(production: wss://ws.caret.page/document/{doc_id}?token={jwt})
Responsibilities:
- Validate the Supabase JWT on WebSocket handshake before allowing connection.
- Maintain an in-memory
Y.Docper active document room. - Implement Y.js sync protocol (message type
0) and awareness protocol (message type1). - Persist Y.js update payloads to
document_collab_updateswhenDATABASE_URLis configured. - Run
SnapshotSchedulerto periodically save full-state snapshots todocument_collab_snapshots. - Keep empty rooms alive in memory briefly so short disconnects don’t lose in-flight state.
| Component | Responsibility |
|---|---|
RoomManager | Creates and tracks in-memory Y.Doc instances per document |
ConnectionHandler | Handles WebSocket frame parsing, Y.js message routing, and update persistence |
CollabRepository | All SQL access for document_collab_updates and document_collab_snapshots |
CollabPersistenceService | Reconstructs a Y.Doc from the latest snapshot plus incremental updates |
SnapshotScheduler | Periodically snapshots active in-memory rooms to the database |
/asyncapi.json, AsyncAPI docs UI at /docs.
Persistence is partially wired. When
DATABASE_URL is set, incoming Y.js updates are saved and periodic snapshots are scheduled. However, RoomManager currently initializes new rooms from a fresh Y.Doc and does not yet call CollabPersistenceService.loadDocument on startup. This means persisted state is not restored when a server restarts or when a room is created for the first time after a crash.AI Service (port 8000)
The AI Service handles all agentic capabilities: conversational chat, document-aware suggestions, SSE token streaming, and vector embedding for RAG retrieval. It is written in Python and runs separately from the Node services. Stack: Python + FastAPI + PydanticAI + SQLAlchemy async + Alembic, managed withuv.
Responsibilities:
- Manage AI conversation sessions and message history.
- Stream AI responses token-by-token over SSE to the frontend.
- Generate and store document chunk embeddings in
document_embeddings(pgvector). - Perform workspace-scoped semantic search to inject RAG context into agent prompts.
- Manage the suggestion lifecycle (proposed → applied / dismissed / superseded).
| Router | Path prefix | Responsibility |
|---|---|---|
ai_router | /ai/conversations | Conversation CRUD, message history, and SSE streaming (/{conversation_id}/stream) |
meta_router | /ai/models | List available LLM models; no authentication required |
embedding_router | /ai/embeddings | Index, search, and delete document embeddings |
suggestion_router | /ai/suggestions | Suggestion create, list, and status transitions |
| Agent | Purpose |
|---|---|
general_agent | Writing assistance, paraphrasing, drafting, summarization |
analyst_agent | Document analysis, metrics, structured data extraction |
src/db/migrations/versions/.
API docs: FastAPI native docs at /docs (Swagger UI) and /redoc.
The AI service never writes directly to document content tables. The consistency contract is:
- AI streams text chunks to the frontend via SSE.
- The frontend applies chunks or suggestions through Tiptap editor transactions.
- Tiptap / Y.js propagates the collaborative state to all connected clients.