Project Architecture
OpenQuok's architecture — project layout and key files for the social scheduler.
Connect your agent today
Draft from chat, review in your calendar, and publish only what you approve.
Overview
The repository is a pnpm monorepo: the root holds the workspace manifest, shared tooling, and top-level packages.
The customer-facing SvelteKit app lives in web/. It serves the marketing site, and authenticated product UI. The docs runtime (content loading, navigation, search) lives under web/src/lib/docs/.
Tech stack
- PNPM workspace
- Supabase DB & Auth
- Express.js
- Svelte 5
- Tailwind CSS
- DaisyUI
- Redis
- Stripe
- Resend
- Sentry
- Vercel
Runtime architecture
OpenQuok has three main services, programmatic clients, and four external systems. The web app and API talk over HTTP; with bullmq transport, the API enqueues Flowcraft runs to Redis and orchestrator workers execute them. External systems usually run outside the app process.
- Web — SvelteKit UI; talks to the backend API.
- Backend API — Express API, Supabase assets, and workflow enqueue.
- Orchestrator — BullMQ workers for posts, email, and token refresh.
- Programmatic clients — CLI, SDK, and MCP against the public API.
- Supabase — Postgres, Auth, and optional Storage buckets.
- Redis / BullMQ — Job queues and shared cache between API and workers.
- Storage — User media (Supabase Storage, R2, or local disk in self-host).
- Resend — Transactional email when EMAIL_ENABLED is enabled.
Maintenance mode — When MAINTENANCE_MODE=freeze_writes is set, mutations are blocked while public SEO pages stay live. See Maintenance mode.
Web
The web app is what users see in the browser — workspace, admin, and in-app docs.
Backend API
The backend coordinates product logic: REST API for the web and public clients, Supabase migrations and RLS, and uploads.
Orchestrator
Orchestrator workers run Flowcraft graphs backed by BullMQ. They handle:
- Publishing scheduled content to social platforms
- Refreshing OAuth tokens for connected integrations
- Sending notification and digest email
- Reconciling missing scheduled posts
Programmatic clients
The CLI, Node SDK, and hosted MCP server call /api/v1/public/*. The CLI may use the device-flow auth server in agent/server/ for login — see Auth server architecture.
Project Layout
Repository layout at the root:
- LICENSE
- README.md
- .backups/
- README.md
- migration/
- .cursor/
- rules/
- agent/
- server/
- skills/
- src/
- backend/
- common/
- infra/
- self-host/
- docker-compose.yml
- orchestrator/
- web/
- sdk/
- scripts/
- package.json
- pnpm-workspace.yaml
- railway.toml
- vercel.backend.json
- vercel.web.json
- agent/ — Published as @openquok/auto-cli: the programmatic CLI, agent skills under skills/, and the OAuth2 device-flow auth server in server/. See Getting Started - CLI and Configuration - Agent.
- .backups/ See Supabase backup.
- backend/ — Supabase project assets (migrations, RLS, modules) and the Express API that talks to Supabase (database + auth, and Storage).
- common/ — Shared workspace package (`openquok-common`): types and small utilities imported by backend/ and orchestrator/ (for example notification email types).
- .github/ — CI workflows (for example release automation under workflows/).
- infra/ — Docker Compose and self-host env templates. Dev dependencies live in infra/docker-compose.yml; the full operator stack is under infra/self-host/. See Docker Compose.
- orchestrator/ — Workspace package: Flowcraft blueprints, BullMQ adapters, and worker entrypoints. See Orchestrator workflows, Configuration - Worker, and Railway (workers).
- .railway/ — Railway infrastructure-as-code (railway.ts); local CLI backups under this folder are gitignored. Worker deploy also uses per-flavor orchestrator/railpack.*.json configs.
- sdk/ — Published as @openquok/node-sdk: a typed Node.js client for the programmatic API.
- scripts/ — Monorepo automation: Vercel env sync/deploy helpers (vercelSync*.mjs, vercelDeploy*.mjs), Railway worker service setup (railwaySetupWorkerService.mjs), and prod-backup/ for Supabase export/restore scripts.
- web/ — SvelteKit frontend; public static files live under web/static/.
- .cursor/ — Cursor rules that encode repository conventions. Contributors and agents should follow the matching .cursor/rules/*.mdc files when editing code or using them as chat context in each area.
Key Directories
backend/
- backend/
- api/
- handler/
- app.ts
- config/
- connections/
- controllers/
- data/
- emails/
- errors/
- guards/
- integrations/
- mcp/
- middlewares/
- public/
- repositories/
- routes/
- publicApi/
- scripts/
- services/
- supabase/
- swagger/
- tests/
- types/
- utils/
- api/ and handler/ — HTTP entrypoints shaped or types for Vercel. Use them as the deployment shell.
- services/ — Domain orchestration and use-cases; this layer is also where caching belongs when you need to reuse or shorten expensive work across requests (in-memory, keyed stores, or upstream cache). Services may also call `openquok-orchestrator` to enqueue Flowcraft runs.
- supabase/ — Database source of truth: modular SQL under db/ tables, RLS, functions, seeds, and migration files.
- integrations/ — Social provider adapters (OAuth, publish, analytics) consumed by services and orchestrator activities.
- routes/publicApi/ — Programmatic API surface mounted at /api/v1/public/* (SDK, CLI, MCP clients).
- mcp/ — Hosted MCP server tools and auth wired into the API process.
- swagger/ — OpenAPI JSDoc sources merged into /api/v1/openapi.json for docs and SDK alignment.
- repositories/, controllers/, routes/ — Persistence adapters, request/response handling, and route tables; prefer Supabase clients and SQL in migrations over ad hoc SQL in the web app.
- middlewares/, errors/, connections/, config/, types/, utils/, data/, guards/ — Cross-cutting behavior (including maintenanceMode.ts write-freeze), Supabase/client wiring, shared types, helpers, and supporting data fixtures or reference payloads.
- emails/ — Transactional templates and send flows.
- scripts/ and tests/ — Migration aggregation (aggregate_migrations_all.mjs), one-off backend scripts, and automated tests.
- Storage — User or system files go through Supabase Storage (buckets and policies live with the rest of the backend).
orchestrator/
- orchestrator/
- adapters/
- activities/
- blueprints/
- flows/
- nodes/
- stores/
- worker/
- scripts/
- index.ts
- blueprints/ and nodes/ — Flowcraft graph definitions (scheduled social posts, notification email, integration token refresh).
- flows/ — Workflow implementations and reconciliation helpers (for example missing scheduled post rescans).
- adapters/ — BullMQ transport: enqueue helpers used from the API and worker bootstrap code.
- worker/ — Process entrypoints plus health checks and Sentry init.
- activities/ — Side-effecting steps invoked from flows (publish, email send, OAuth refresh).
- stores/ — Redis-backed state for rate limits and notification digests.
- scripts/ — Operator utilities (env validation, queue cleanup, Railway env setup).
When config/orchestratorFlows.ts keeps transport on in_process, the API runs flows inline; with bullmq, these workers execute jobs from Redis queues shared with the API.
agent/
- agent/
- src/
- commands/
- server/
- app.ts
- skills/
- openquok-core/
- tests/
- package.json
- tsup.config.ts
- src/
- src/ — CLI implementation published as @openquok/auto-cli (`openquok` binary): auth, posts, integrations, analytics, uploads, and config commands.
- server/ — Standalone OAuth2 device-flow auth server (Postgres-backed device codes, token polling). Deployed separately from the main API; the web app proxies browser routes under web/src/routes/(public)/cli/device.
- skills/ — Agent skill packs (for example openquok-core) with channel recipes, provider settings, and command references for MCP clients.
- tests/ — Vitest unit tests and CLI e2e helpers.
See Auth server architecture for the device-login sequence and endpoint map.
infra/
Docker and self-host operator assets:
- infra/
- docker-compose.yml
- self-host/
- docker-compose.yml
- .env.example
- infra/docker-compose.yml — Contributor development environment only.
- infra/self-host/ — full stack: Redis, API, web, BullMQ workers, uploads volume; optional `cli` profile for Postgres + agent server. Default UI: http://localhost:4007.
- infra/self-host/.env.example — Template for self-host env vars; operators copy to `.env` beside the Compose file.
User-facing bring-up steps live under Installation (especially Docker Compose — open the UI at http://localhost:4007 after up —build).
web/
The SvelteKit app root:
- web/
- package.json
- web-config.json
- static/
- src/
- content/
- data/
- docs.ts
- icons.ts
- lib/
- area-admin/
- area-protected/
- area-public/
- core/
- ui/
- …
- params/
- routes/
- (auth)/
- (docs)/
- (legal)/
- (protected)/
- (public)/
- maintenance/
- …
- styles/
- tests/
- src/routes/ — File-based routing. Route groups (public), (auth), (protected), (docs), (legal) share layouts and auth boundaries without affecting the URL prefix. maintenance/ is the write-freeze landing page when MAINTENANCE_MODE=freeze_writes (see src/lib/maintenance/ and hooks.server.ts).
- src/data/ — Small typed registries and config imported from $data/…(e.g. `docs.ts`, `icons.ts`).
- src/lib/core/ — HttpGateway, cookies, shared presenters that sit next to I/O. DTOs from the API are parsed here and in repositories, not in `.svelte` files.
- src/lib/area-admin/, src/lib/area-protected/, src/lib/area-public/ — Page-level presenters, including admin console, signed-in app, public/marketing and etc. Routes import singletons from these indexes.
- src/lib/ui/ — Reusable UI components (buttons, dialogs, docs chrome, DaisyUI-styled patterns). Feature routes pass view models and callbacks into these components.
- Theming (DaisyUI) — Styling favors semantic DaisyUI + Tailwind tokens (bg-base-100, text-base-content, border-base-300, primary, and etc.) so theme presets swap via CSS variables instead of hand-maintained color pairs per component.
- src/content/ — Markdown sources for the in-app docs site.
- static/ — Public assets (favicon, PWA icons, README images).
Presenters, repositories, and tests
We keep Svelte focused on layout and inputs, and push behavior into layers you can test without the DOM or a real API:
| Layer | Role | Typical files |
|---|---|---|
| UI (Svelte) | Render view models; forward user actions via callbacks. Parent routes own the page presenter. | *.svelte under routes/ and $lib/ui/ |
| Presenter | View-specific state (status, toasts and etc), actions that call repositories. | *.presenter.svelte.ts |
| Repository | Domain state as programmer models; maps DTOs ↔ domain; calls the gateway. | *.repository.svelte.ts |
| Gateway / infrastructure | HTTP and other I/O; DTOs at the boundary. | $lib/core/ (e.g. HttpGateway) |
Why separate repository and presenter?
Repositories encode business rules and data shape (what the app believes is true). Presenters encode how a screen behaves (loading, errors, which *Vm the template sees).
Splitting them means you can unit test presenters with a stubbed repository and unit test repositories with a stubbed gateway—asserting on state and return values instead of rendering components or running end-to-end tests for every branch. That matches the goal: test view-model and domain behavior, not the DOM.
Convention reference: The full rules (page vs child components, toast wiring, *Pm / *Vm naming, Get* presenters) live in the repo at .cursor/rules/web-repository-presenter-architecture.mdc for contributors.
Document Directories
src/content/docs/
This is where in-app documentation markdown lives. Each .md file becomes a page; URLs follow the folder path.
Sidebar tabs and section order are declared in src/lib/docs/constants/config.ts as docsTabs: General, Cloud, Self-hosting, CLI, MCP, Public API, Contributing.
- src/content/docs/
- getting-started/
- channels/
- creating-posts/
- posts-management/
- settings/
- platforms/
- automations/
- cloud/
- getting-started-for-dev/
- installation/
- configuration-backend/
- configuration-web/
- configuration-worker/
- configuration-agent/
- admin/
- social-integration/
- getting-started-for-cli/
- cli-usages/
- cli-examples/
- agent-setup-guides/
- other-skills/
- getting-started-for-mcp/
- mcp-examples/
- mcp-references/
- mcp-setup-guides/
- getting-started-for-public-api/
- public-api-providers/
- oauth2-for-apps/
- apis-integrations/
- apis-posts/
- apis-analytics/
- apis-notifications/
- apis-uploads/
- developer-guidelines/
- contribution-opportunities/
- publish-listings/
- documentation-contribution/
- General tab — Product usage under getting-started/, channels/, creating-posts/, posts-management/, settings/, platforms/, and automations/.
- Self-hosting tab — Operator install and config: installation/ (including maintenance-mode.md), configuration-*, admin/, and social-integration/.
- Public API tab — getting-started-for-public-api/, public-api-providers/, apis-*, and oauth2-for-apps/.
- Contributing tab — developer-guidelines/, contribution-opportunities/, publish-listings/, and documentation-contribution/.
src/lib/docs/
The documentation engine:
- constants/config.ts — Defines docs site metadata,
docsTabs(General, Cloud, Self-hosting, CLI, MCP, Public API, Contributing), i18n, and assembles `docsConfig` - content.ts — Content loader that discovers and parses markdown files
- navigation.ts — Generates sidebar navigation and maps paths/slugs to the active tab
- types.ts — TypeScript types for docs, navigation, and config
- utils/ui/tocState.svelte.ts — Table of contents state management
src/lib/ui/components/docs/
Documentation UI (layouts, MDX helpers, search, nav):
- layout/ — Header, footers, sidebars
- nav/ — Breadcrumbs, keyboard nav, social links
- search/ — Command palette search
- mdx/ — Callout, tabs, cards, and other markdown components
Configuration Files
Root (monorepo)
| File | Purpose |
|---|---|
| package.json | Workspace scripts (pnpm filters), shared devDependencies, packageManager pin |
| pnpm-workspace.yaml | Workspace package globs |
| railway.toml | Railway deploy config for workers and related services |
| vercel.backend.json / vercel.web.json | Vercel project wiring templates for API and web packages |
| .dockerignore | Excludes local artifacts from Docker build context |
Backend
| File | Purpose |
|---|---|
| package.json | Scripts, dependencies, and workspace metadata for the API package |
| vercel.json | Vercel deployment: routes, builds, and serverless/function wiring |
| tsconfig.json / tsconfig.build.json / tsconfig.tsup.json | TypeScript: editor vs production compile targets |
| tsup.config.ts | Bundles the deployable API surface |
| eslint.config.js | ESLint rules for the backend source |
| jest.config.js | Jest entry; see also jest.*.cjs / jest.*.js env files and babel.config.jest.cjs for test transforms |
Orchestrator
| File | Purpose |
|---|---|
| package.json | Worker scripts and openquok-orchestrator package metadata |
| tsconfig.json / tsconfig.build.json | TypeScript compile targets for workers |
| Dockerfile | Production worker image (one image; Compose overrides command per queue) |
| railpack.*.json | Railway buildpack configs per worker flavor |
| jest.config.js / jest.bullmq.config.js | Unit and BullMQ integration test entrypoints |
| babel.config.jest.cjs | Jest transform config |
Agent
| File | Purpose |
|---|---|
| package.json | CLI package (@openquok/auto-cli) scripts and dependencies |
| tsconfig.json | TypeScript for CLI sources |
| tsup.config.ts | Bundles the openquok CLI binary |
| vitest.config.mjs / run-vitest.mjs | Vitest runner for CLI tests |
| server/package.json | Auth server dependencies and start scripts |
| server/tsconfig.json | TypeScript for the device-flow server |
| server/vercel.json | Vercel deployment for the hosted auth server |
| server/Dockerfile | Optional self-host cli profile image |
Common
| File | Purpose |
|---|---|
| package.json | openquok-common workspace package metadata |
| tsconfig.json | TypeScript compile settings for shared types and utilities |
SDK
| File | Purpose |
|---|---|
| package.json | @openquok/node-sdk publish metadata and scripts |
| tsconfig.json | TypeScript compile settings |
| tsup.config.ts | Bundles the published SDK entrypoint |
Infra
| File | Purpose |
|---|---|
| infra/docker-compose.yml | Contributor Redis (+ optional CLI Postgres) for local dev |
| infra/self-host/docker-compose.yml | Full self-host stack (API, web, workers, Redis; optional cli profile) |
| infra/self-host/.env.example | Operator env template (Supabase keys, Redis, OAuth apps, self-host defaults) |
Web
| File | Purpose |
|---|---|
| svelte.config.js | SvelteKit + MDSvex configuration |
| vite.config.ts | Vite + Tailwind CSS setup |
| package.json | Scripts and dependencies for the SvelteKit app |
| web-config.json | Web package metadata consumed by tooling |
| vercel.json | Vercel adapter and deployment settings |
| tsconfig.json | TypeScript for the SvelteKit app |
| eslint.config.js | ESLint rules for web sources |
| src/lib/docs/constants/config.ts | Docs site title, sidebar tabs/sections, social links, locales; assembles docsConfig |
| src/data/docs.ts | Docs site social URLs and shared docs data |