Architecture
Overview
Section titled “Overview”Orimora is a full-stack monolith powered by SvelteKit. There are no microservices: SvelteKit server routes are the API, the same Node.js process serves the frontend, handles WebSocket connections for collaborative editing, and processes background jobs.
┌────────────────────────────────────────────────────┐│ Browser Client ││ SvelteKit SSR + Svelte 5 Runes ││ TipTap Editor / Yjs / Hocuspocus │└─────────────────────────┬──────────────────────────┘ │ HTTP / WebSocket┌─────────────────────────▼──────────────────────────┐│ Node.js Application ││ ┌──────────┐ ┌────────────┐ ┌────────────────┐ ││ │SvelteKit │ │ Hocuspocus │ │ BullMQ Worker │ ││ │ Routes │ │ WS Server │ │ (background) │ ││ └────┬─────┘ └─────┬──────┘ └───────┬────────┘ │└───────┼──────────────┼─────────────────┼────────────┘ │ │ │ ┌────▼──────────────▼─────────────────▼────┐ │ PostgreSQL 16+ │ │ Drizzle ORM + pg_trgm extension │ └───────────────────────────────────────────┘ │ ┌────▼──────────────────────┐ │ Redis │ │ Sessions · Cache · BullMQ│ └───────────────────────────┘Directory Structure
Section titled “Directory Structure”src/├── lib/│ ├── components/ # Svelte UI components│ │ └── editor/ # TipTap editor + extensions│ ├── server/ # Server-only code│ │ ├── db/ # Drizzle schema + migrations│ │ ├── services/ # Business logic│ │ └── policies/ # Authorization checks│ └── utils/ # Shared utilities├── routes/│ ├── (app)/ # Authenticated app routes│ ├── (auth)/ # Login / registration│ └── api/ # API endpoints (RPC-style + /v1/ REST)└── hooks.server.ts # Auth, rate-limit, security headersData Flow
Section titled “Data Flow”- Request arrives at
hooks.server.ts— rate-limit, auth session loaded - Route handler calls a service function (e.g.
getDocument,createDocument) - Service queries Drizzle ORM → PostgreSQL
- Response serialised as JSON (API routes) or rendered to HTML (page routes)
- Background effects (webhooks, notifications, backlinks) dispatched via BullMQ
Collaboration
Section titled “Collaboration”Real-time collaboration uses Yjs (CRDT) with Hocuspocus as the server:
- Each document has a unique room identified by its UUID
- The Yjs document is persisted to PostgreSQL via
@hocuspocus/extension-database - On connect, the server loads the current document content and hydrates the Yjs state
- On disconnect, the final Yjs state is flushed back to the documents table
Security
Section titled “Security”- Sessions: HTTP-only, Secure, SameSite=Lax cookies with random tokens
- CSRF: All mutating API requests from the browser require session auth
- API keys:
kb_prefix, encrypted at rest, scope-limited - CSP: Nonce-based policy managed by SvelteKit
- CORS: Only allowed on
/api/v1/*withAuthorizationheader support