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.
flowchart TD
Browser["Browser Client\nSvelteKit SSR · Svelte 5 Runes\nTipTap · Yjs · Hocuspocus"]
subgraph Node["Node.js Application (port 3000)"]
SK["SvelteKit\nHTTP Routes / API"]
HC["Hocuspocus\nWebSocket /collab"]
BQ["BullMQ Workers\n(background jobs)"]
end
Browser -- "HTTP / SSR" --> SK
Browser -- "WebSocket" --> HC
SK -- "Drizzle ORM" --> PG[("PostgreSQL 16\npg_trgm · full-text search")]
HC -- "Yjs state" --> PG
BQ -- "jobs" --> PG
SK -- "sessions · cache · rate-limit" --> RD[("Redis 7")]
BQ -- "queue" --> RD
SK -- "S3 SDK (optional)" --> S3[("S3 / Object Storage\nuploads · exports")]
BQ -- "SMTP" --> Mail["Mail server\n(SMTP)"]
BQ -- "HTTP POST" --> Hooks["Outbound Webhooks\n& Publishing channels"]
Request lifecycle (simplified):
- Browser sends HTTP request → Traefik (TLS termination) → Node.js
- SvelteKit route handler authenticates session (Redis) and checks permissions
- 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
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