OpenSEO
Audited from github.com/every-app/open-seo
OpenSEO is an open-source alternative to Semrush and Ahrefs for keyword research, rank tracking, backlinks, site audits and AI visibility. It runs on Cloudflare Workers with the user's own DataForSEO key and exposes an MCP server for AI agents.
- Language
- TypeScript
- Database
- Cloudflare D1
- Hosting
- Cloudflare
- License
- MIT
- Running for
- 7 months
- Team
- 2-5 people
Why this architecture
Running on Cloudflare Workers with D1, Workflows and Durable Objects makes it free-tier friendly to self-host and to scale when hosted. Supporting both D1 and Postgres through Drizzle, plus a Docker path, keeps the project portable beyond Cloudflare.
Tech stack by layer
19 technologies · audited Sep 25, 2026- ReactReact 19 UI for keyword research, rank tracking, backlinks, audits and reports
- TanStack StartFull-stack React framework with file routes (src/routes, routeTree.gen.ts) and server functions
- TanStack QueryClient data fetching and caching, alongside TanStack Form and TanStack Table
- Tailwind CSSStyling with Tailwind 4 plus daisyUI components, lucide icons and Recharts charts
- Cloudflare WorkersRuntime for the app worker (src/server.ts) and a separate site-audit worker (src/audit-worker.ts)
- Better AuthAuthentication with API keys, organizations and generated Drizzle schemas for D1 and Postgres
- ZodValidation for server functions, MCP tool inputs and configuration
- Cloudflare D1Default SQLite database on Cloudflare, migrated with wrangler d1 from drizzle/
- PostgreSQLAlternative database with its own migration set (drizzle-pg/) and a D1-to-Postgres runbook
- DrizzleORM and migration generator for both the SQLite (D1) and Postgres dialects
- CloudflareHosting via wrangler and Alchemy deploys, Workflows for audits/rank checks, cron triggers
- DockerSelf-host image (Dockerfile.selfhost) run by compose.yaml on workerd with local bindings
- PostHogProduct analytics on client and server, with source map upload in a CI workflow
- PlaywrightEnd-to-end tests in e2e/ plus a deliberately broken badseo fixture site
- Vercel AI SDKPowers SAM, the in-app SEO agent, via ai/@ai-sdk/react with an OpenRouter provider
- Model Context ProtocolMCP server (with Cloudflare OAuth provider) so external agents can query SEO data
- Cloudflare AgentsDurable Object chat agent (SamChatAgent) persisting each chat session in DO SQLite
OpenSEO architecture diagram
Open SVGDiagram as text
- Web app (TanStack Start · React) → App worker (Cloudflare Workers): server functions
- External agents (MCP) → App worker (Cloudflare Workers): MCP
- App worker (Cloudflare Workers) → Database (D1 or Postgres (Drizzle)): Drizzle
- App worker (Cloudflare Workers) → SAM agent (Durable Object · AI SDK): chat
- App worker (Cloudflare Workers) → Workflows (audits · rank checks): schedule
- Workflows (audits · rank checks) → Audit worker (site audits): run audit
- App worker (Cloudflare Workers) → DataForSEO (your API key): SEO data
- SAM agent (Durable Object · AI SDK) → OpenRouter (models): AI SDK
Key architectural decisions
6 decisions- 01
Cloudflare Workers as the primary runtime, Docker as a local fallback
wrangler.jsonc defines the app worker with smart placement, Workflows, Durable Objects and cron triggers; compose.yaml runs the same bundle in a container with CLOUDFLARE_INCLUDE_PROCESS_ENV so bindings come from environment variables for single-machine self-hosting.
- 02
Site audits split into a separate worker to avoid memory limits
Comments in wrangler.jsonc explain that multi-MB Lighthouse payloads OOMed the main worker, so the audit engine moved to open-seo-audit (wrangler.audit.jsonc, src/audit-worker.ts) and is reached through a service binding and a cross-script Workflow binding.
- 03
Dual database dialects with parallel Drizzle migrations
drizzle/ holds D1 (SQLite) migrations and drizzle-pg/ holds Postgres ones, with separate drizzle configs and Better Auth schema generators per dialect; runbooks/ and scripts/migrate-d1-to-postgres.ts cover moving a live install from D1 to Postgres.
- 04
Bring-your-own DataForSEO key instead of owning SEO data
SEO data comes from the DataForSEO API with the user's own key (docs/DATAFORSEO_API_KEY.md). The hosted version meters usage with autumn-js, as described in specs/0002-hosted-dataforseo-metering-with-autumn.md.
- 05
Agent-first surface through MCP, skills and an in-app agent
The MCP server and skills (plugins/openseo, .claude-plugin, .cursor-plugin) let external agents use the data, while SAM runs in-app as a Cloudflare Agents Durable Object built on the Vercel AI SDK with OpenRouter models.
- 06
Spec-driven development with a supply-chain delay
Features start as numbered design docs in specs/ (0001-0014). pnpm-workspace.yaml sets minimumReleaseAge so new package versions wait about eight days before install, and it lists security overrides with a GHSA reference for each.
How OpenSEO is built
How OpenSEO is structured
OpenSEO is a pnpm project (packageManager: pnpm@10.30.1) with one main app at the root and two satellite sites:
- Root app: TanStack Start with routes in
src/routes(generatedsrc/routeTree.gen.ts), server entrysrc/server.ts, server functions insrc/serverFunctions, and shared code insrc/lib,src/shared,src/dbandsrc/middleware. src/audit-worker.ts: the site-audit engine, deployed as its own worker viawrangler.audit.jsonc.web/: the marketing and docs site (TanStack Start + Fumadocs MDX, deployed to Workers).badseo/: a deliberately broken website used as a site-audit test fixture.
Product decisions are recorded as numbered specs in specs/, covering project scoping, metering, Search Console, onboarding agents, the audit crawl architecture, project memory, organizations, reports and share links. AGENTS.md and CLAUDE.md describe conventions for coding agents.
Frontend
The UI is React 19 on TanStack Start, with TanStack Query for data, TanStack Form for inputs and TanStack Table for large keyword and backlink grids. Styling uses Tailwind CSS 4 and daisyUI. Recharts draws charts, sonner shows toasts, and react-markdown renders agent output. The build runs on Vite 7 with @cloudflare/vite-plugin, and vite-plugin-lean-worker-bundle.ts trims the worker bundle.
Backend & APIs
Server logic runs as TanStack Start server functions inside a Cloudflare Worker. wrangler.jsonc declares:
- Workflows:
site-audit-workflow(in the audit worker) andrank-check-workflow. - Durable Objects:
SamChatAgent, the in-app SEO agent, which stores each chat session in Durable Object SQLite. - Service binding:
AUDIT_ENGINE, used to cancel audits and handle GDPR erasure. - Cron triggers for rank checks and reaping stale audits.
- Smart placement so the
fetchhandler runs close to Postgres (through Hyperdrive) and the SEO APIs.
Authentication is Better Auth with API keys and organizations. @cloudflare/workers-oauth-provider handles OAuth for the MCP server, built on @modelcontextprotocol/server. The agent layer uses the Vercel AI SDK with @openrouter/ai-sdk-provider. Crawling and parsing use htmlparser2, robots-parser, fast-xml-parser and tldts.
Data & persistence
Drizzle manages two schemas:
drizzle/: SQLite migrations for Cloudflare D1, applied withwrangler d1 migrations apply.drizzle-pg/: PostgreSQL migrations, applied withdrizzle-kit migrateand connected with thepostgresdriver.
Better Auth tables are generated per dialect (auth:generate:d1, auth:generate:pg). Migration tooling lives in runbooks/d1-to-postgres-*.md and scripts/migrate-d1-to-postgres.ts, and runbooks/gdpr-erasure.md plus scripts/erase-user-data.ts handle data removal.
Build, test & deploy
vite build && tsc --noEmitbuilds and typechecks. Linting uses oxlint with type-aware rules, and knip finds dead code.- Unit tests run on Vitest. Playwright covers end-to-end flows (
e2e/,playwright.free-tools.config.ts). - Deploys go through
wrangler deploy(main and audit workers) or Alchemy (opens in a new tab) (alchemy.run.ts) for self-host, preview and hosted-Postgres stages. - GitHub Actions:
ci.yml,docker-image.yml(publishesghcr.io/every-app/open-seo),pr-preview.ymlandsourcemaps.yml(uploads PostHog source maps).
Self-hosting notes
For personal use, copy .env.example, set DATAFORSEO_API_KEY and run docker compose up. The compose service binds to 127.0.0.1 and uses AUTH_MODE=local_noauth. For team use, docs/SELF_HOSTING_CLOUDFLARE.md covers deploying to your own Cloudflare account. OPENROUTER_API_KEY enables the SAM agent, and Google Search Console and GA4 connections are optional.
What to copy (and what not to)
What to copy
- Moving memory-heavy work (the Lighthouse-based audits) into its own worker behind a service binding, with the reason written in the config comments.
- Numbered specs in the repository as the design record for every major feature.
minimumReleaseAgeplus documented security overrides as a lightweight supply-chain policy.
What not to copy
- Two full migration trees (D1 and Postgres) double the schema work on every change. Pick one dialect unless portability is a hard requirement.
- Pinning pre-release infrastructure tools (Alchemy beta, Effect beta) in the deploy path adds upgrade risk.
Sources & repo audit
- package.json (TanStack Start, Better Auth, Drizzle, AI SDK, MCP)
- wrangler.jsonc (Workers, Workflows, Durable Objects, service bindings)
- compose.yaml (Docker self-hosting)
- specs/0009-site-audit-crawl-architecture.md
Independent analysis of repository at github.com/every-app/open-seo. Spotted an inaccuracy? Use the claim form to request a correction.
Maintainer? Add the architecture badge to your README
[](https://stackitfast.com/project/open-seo) Scaffold it with your agent
Paste this prompt into Claude Code, Cursor, Windsurf or AGY to start a project with OpenSEO's architecture.
- 1Copy the promptThe full markdown spec, with every layer and decision.
- 2Open your AI toolClaude Code, Cursor, Windsurf or Copilot, in a new repo.
- 3Paste and scaffoldUse it as the first instruction; review before you ship.
# MISSION: Scaffold "OpenSEO" Production Architecture
You are an expert Senior Staff Software Architect and Full-Stack Engineer. Your mission is to scaffold and implement a production-grade, highly reliable, and modular codebase following the proven architecture of **OpenSEO**.
---
## 1. PROJECT SPECIFICATIONS & BENCHMARK
- **Reference Architecture**: OpenSEO
- **What It Does**: OpenSEO is an open-source alternative to Semrush and Ahrefs for keyword research, rank tracking, backlinks, site audits and AI visibility. It runs on Cloudflare Workers with the user's own DataForSEO key and exposes an MCP server for AI agents.
- **Domain & Category**: Open-Source SEO Research Platform
- **Production Scale**: 2-5 people
- **Development Mode**: HYBRID
- **Architectural Rationale**: Running on Cloudflare Workers with D1, Workflows and Durable Objects makes it free-tier friendly to self-host and to scale when hosted. Supporting both D1 and Postgres through Drizzle, plus a Docker path, keeps the project portable beyond Cloudflare.
- **Live Website Reference**: https://openseo.so
- **Source Repository**: https://github.com/every-app/open-seo
---
## 2. PRODUCTION TECH STACK
- **Full Stack Array**: TypeScript, React, TanStack Start, TanStack Query, Tailwind CSS, Cloudflare, Cloudflare Workers, Cloudflare D1, PostgreSQL, Drizzle, Better Auth, Vercel AI SDK, Model Context Protocol, Zod, PostHog, Docker, Vite, Vitest, Playwright
- **Primary Language(s)**: TypeScript, MDX, JavaScript, CSS
- **License of the reference repo**: MIT
- **Frontend**: React — React 19 UI for keyword research, rank tracking, backlinks, audits and reports; TanStack Start — Full-stack React framework with file routes (src/routes, routeTree.gen.ts) and server functions; TanStack Query — Client data fetching and caching, alongside TanStack Form and TanStack Table; Tailwind CSS — Styling with Tailwind 4 plus daisyUI components, lucide icons and Recharts charts
- **Backend & APIs**: Cloudflare Workers — Runtime for the app worker (src/server.ts) and a separate site-audit worker (src/audit-worker.ts); Better Auth — Authentication with API keys, organizations and generated Drizzle schemas for D1 and Postgres; Zod — Validation for server functions, MCP tool inputs and configuration
- **Data & persistence**: Cloudflare D1 — Default SQLite database on Cloudflare, migrated with wrangler d1 from drizzle/; PostgreSQL — Alternative database with its own migration set (drizzle-pg/) and a D1-to-Postgres runbook; Drizzle — ORM and migration generator for both the SQLite (D1) and Postgres dialects
- **Infrastructure & deploy**: Cloudflare — Hosting via wrangler and Alchemy deploys, Workflows for audits/rank checks, cron triggers; Docker — Self-host image (Dockerfile.selfhost) run by compose.yaml on workerd with local bindings; PostHog — Product analytics on client and server, with source map upload in a CI workflow; Playwright — End-to-end tests in e2e/ plus a deliberately broken badseo fixture site
- **Tooling, testing & ops**: Vercel AI SDK — Powers SAM, the in-app SEO agent, via ai/@ai-sdk/react with an OpenRouter provider; Model Context Protocol — MCP server (with Cloudflare OAuth provider) so external agents can query SEO data; Cloudflare Agents — Durable Object chat agent (SamChatAgent) persisting each chat session in DO SQLite
---
## 3. KEY ARCHITECTURAL DECISIONS (audited from https://github.com/every-app/open-seo @ 0ffff93)
1. **Cloudflare Workers as the primary runtime, Docker as a local fallback**: wrangler.jsonc defines the app worker with smart placement, Workflows, Durable Objects and cron triggers; compose.yaml runs the same bundle in a container with CLOUDFLARE_INCLUDE_PROCESS_ENV so bindings come from environment variables for single-machine self-hosting.
2. **Site audits split into a separate worker to avoid memory limits**: Comments in wrangler.jsonc explain that multi-MB Lighthouse payloads OOMed the main worker, so the audit engine moved to open-seo-audit (wrangler.audit.jsonc, src/audit-worker.ts) and is reached through a service binding and a cross-script Workflow binding.
3. **Dual database dialects with parallel Drizzle migrations**: drizzle/ holds D1 (SQLite) migrations and drizzle-pg/ holds Postgres ones, with separate drizzle configs and Better Auth schema generators per dialect; runbooks/ and scripts/migrate-d1-to-postgres.ts cover moving a live install from D1 to Postgres.
4. **Bring-your-own DataForSEO key instead of owning SEO data**: SEO data comes from the DataForSEO API with the user's own key (docs/DATAFORSEO_API_KEY.md). The hosted version meters usage with autumn-js, as described in specs/0002-hosted-dataforseo-metering-with-autumn.md.
5. **Agent-first surface through MCP, skills and an in-app agent**: The MCP server and skills (plugins/openseo, .claude-plugin, .cursor-plugin) let external agents use the data, while SAM runs in-app as a Cloudflare Agents Durable Object built on the Vercel AI SDK with OpenRouter models.
6. **Spec-driven development with a supply-chain delay**: Features start as numbered design docs in specs/ (0001-0014). pnpm-workspace.yaml sets minimumReleaseAge so new package versions wait about eight days before install, and it lists security overrides with a GHSA reference for each.
---
## 4. NON-NEGOTIABLE ARCHITECTURAL GUARDRAILS
1. **Monorepo & Modular Separation**:
- Structure as a Turborepo monorepo with strict package boundaries:
- `apps/web`: Application UI, routing, layouts, and server endpoints.
- `packages/ui`: Shared design tokens, CSS variables, and Radix UI primitive components.
- `packages/db`: Database schemas, client singleton, declarative migrations, and seed scripts.
- `packages/config`: Shared TypeScript, ESLint, and build configurations.
2. **Strict Type Safety & Zero `any` Policy**:
- Enable `strict: true`, `noImplicitAny: true`, and `strictNullChecks: true`.
- Validate ALL external inputs, API request bodies, and query parameters with **Zod** schemas before execution.
3. **Frontend & Rendering Guidelines**:
- Utilize Vite 6 with TanStack Router for fully type-safe routing. Manage server state and caching via TanStack Query v5 with optimistic UI updates.
4. **Design System & Aesthetics**:
- Keep every color, radius, shadow and font in a single token file (CSS variables) and consume tokens everywhere; never hardcode hex values in components.
- Prefer crisp 1px borders and one subtle shadow scale over blurry default shadows. Pair one sans-serif for body/headings with one monospace for tags, badges, metrics, and code.
5. **Data Layer & Reliability**:
- Write declarative schema definitions with foreign keys, composite indexes on queried filters, and automated timestamp triggers.
- Use connection pooling and prepared statements for serverless database execution.
---
## 5. STEP-BY-STEP SCAFFOLDING ROADMAP
- **Phase 1: Workspace & Root Config**: Initialize package manager, monorepo configuration (`turbo.json`, `tsconfig.base.json`, `package.json`).
- **Phase 2: Database Schema & Client**: Set up the data layer (Cloudflare D1, PostgreSQL, Drizzle): client, connection pool, models, and migration scripts.
- **Phase 3: Design Tokens & UI Primitives**: Build accessible `Button`, `Input`, `Card`, `Badge`, and layout wrappers inside `packages/ui`.
- **Phase 4: Core Application Routes & Handlers**: Implement primary authentication, user session handling, and application routes.
- **Phase 5: Quality Assurance & Build Verification**: Run `tsc --noEmit`, ESLint, Prettier, and smoke test suites to ensure zero compilation or runtime errors.
---
## 6. EXECUTION INSTRUCTIONS
1. Review all specifications, architectural guardrails, and stack choices above.
2. Present the full monorepo directory tree structure.
3. Systematically generate the complete, production-ready codebase according to the 5-phase roadmap above — starting with the root workspace setup, followed by the database schema, UI design system package, and full-stack application routes until the repository is fully scaffolded and ready to run.Frequently asked about OpenSEO
What is OpenSEO built with?
OpenSEO is a TypeScript app built with TanStack Start and React 19, running on Cloudflare Workers. It stores data in Cloudflare D1 or PostgreSQL through Drizzle, uses Better Auth for accounts, and runs an in-app AI agent on the Vercel AI SDK inside a Durable Object.
Can I self-host OpenSEO?
Yes. The repository documents two paths: Docker with compose.yaml for personal use on one machine, and a Cloudflare deployment (which works on the free plan) for internet-facing or team use. Both need a DataForSEO API key.
Where does OpenSEO get its SEO data?
From the DataForSEO API, using the user's own key. Self-hosters pay DataForSEO directly per request. According to the README, the hosted service adds a margin on each request.
Can AI agents use OpenSEO?
Yes. OpenSEO exposes an MCP server and ships agent skills for tools such as Claude Code and Cursor, so agents can run keyword research, audits and rank checks against the user's data.
One email a month: new deep dives and stack trends
New source-audited architectures, head-to-head comparisons and the monthly stack report. No spam, unsubscribe anytime.
Similar architectures
- TypebotAI-agentConversational Form Builder · SoloShares TypeScript · React · Tailwind CSS
- DocumensoAI-agentE-signature SaaS · 2-5 peopleShares React · TypeScript · PostgreSQL
- Cal.comClassicScheduling SaaS · 6-20 peopleShares TypeScript · React · PostgreSQL
- TwentyHybridCRM SaaS · 6-20 peopleShares TypeScript · React · PostgreSQL
- TaxonomyAI-agentOpen-Source SaaS Template · 1-5 peopleShares React · TypeScript · Tailwind CSS
- OpenMAICClassicMulti-Agent Interactive AI Classroom · 6-20 peopleShares TypeScript · React · Tailwind CSS