Project Architecture
RedPanda's architecture — project layout and key files for RedPanda.
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
RedPanda 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).
Marketing copy (programmatic SEO)
Routes under src/routes/(public)/ stay generic (one +page per surface). Hero, FAQ, meta strings, and hub blurbs live under web/src/lib/content/constants/. Import from the canonical module for each public URL (e.g. agents/index.ts, channels/index.ts) — not legacy root public*Config.ts shims. Tool-specific marketing config may also live under feature libs (e.g. web/src/lib/best-time-to-post/constants/).
| Kind | Edit when | Examples |
|---|---|---|
| Generic | Hub or shared builders for every slug on a surface | hubs/channels.ts, channels/api/_shared/publicApiCapabilityAudienceConfig.ts, channels/api/posting/general.ts |
| Tailored | One URL needs its own hero, meta, or feature copy | channels/catalog/{slug}.ts, channels/api/posting/platforms/{slug}.ts, agents/hosts/{slug}.ts |
Rule: match the public URL. Example: /social-media-posting-api/x → channels/api/posting/platforms/x.ts; the posting API hub → generic capability configs under channels/api/_shared/ and channels/api/index.ts.
Paths below are repo-relative from web/src/lib/content/constants/ unless noted.
| Public surface | Canonical import | Tailored per slug |
|---|---|---|
| /channels, /channels/{slug} | channels/index.ts, hubs/channels.ts | channels/catalog/{slug}.ts + channels/catalog/seeds.ts |
| /agents, host /agents/{slug} | agents/index.ts, hubs/agents.ts | agents/hosts/{slug}.ts + agents/seeds.ts |
| MCP client on /agents/{slug} | mcps/index.ts | mcps/hosts/{slug}.ts + mcps/seeds.ts |
| /agents/{host}/{channelSlug} | agents/channels/index.ts | channels/catalog/* + host files under agents/channels/ |
| /social-media-posting-api, /social-media-scheduling-api (+ /{slug}) | channels/api/index.ts, channels/api/_shared/* | channels/api/posting/platforms/{slug}.ts; scheduling hub in channels/api/scheduling/ |
| /tools/* | hubs/tools.ts | channels/tools/{tool}/general.ts, channels/tools/{tool}/faq.ts |
| /compare, /alternatives | competitors/index.ts, hubs/compare.ts | competitors/{product}.ts |
| /self-hosting | self-hosting/landing.ts, self-hosting/whoIsFor.ts | — |
| / and shared hub chrome | landing/index.ts (hero, breadcrumbs, who-is-for, setup-steps-footer) | — |
| Shared FAQ pool | faq/index.ts (PUBLIC_FAQ_ITEMS, appendPublicGeneralFaqItems, PUBLIC_*_FAQ_ITEM_IDS) | Tailored arrays in catalogs and hubs/*.ts |
Channels: register slugs in channels/catalog/seeds.ts (PUBLIC_CHANNEL_LANDING_PAGES). Optional fourth WhoIsFor card: channels/catalog/audience-tailored.ts. Feature bento IDs: channels/catalog/feature-bento.ts.
FAQs: tailored Q&A in each catalog or hub file; append shared rows with appendPublicGeneralFaqItems and the matching PUBLIC_*_FAQ_ITEM_IDS from faq/index.ts. Link helpers: web/src/lib/content/utils/publicFaqLinks.ts.
Agents: CLI install snippets → agents/cli-command-reference.ts; skill example JSON → agents/core-example-json.ts.
Route path helpers (not copy): web/src/lib/area-public/constants/getRootPathPublic*.ts.
Maintainer-only (do not import from app code, routes, or presenters): _dev/programmatic-landing/publicProgrammaticLandingRegistry.ts — surface list, page counts, estimateNewProviderMarketingPages(slug). Verify after catalog changes:
pnpm --filter ./web run test:pseo-registry Route-shaped catalogs also live under channels/api/, channels/catalog/, channels/tools/, hubs/, faq/, landing/, and self-hosting/.
Related Cursor rules: programmatic SEO (constants, templates, FAQ funnel, footer) — .cursor/rules/web-seo-pseo.mdc; FAQ ids and append sets — web-landing-faqs.mdc; adding channels / agents — add-social-provider-integration.mdc, add-agent.mdc.
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, Add RedPanda to your app, 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, Add RedPanda to your app, 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 |