System Architecture of OpenSEO: Cloudflare-Native SaaS with Durable Objects and Workflows

OpenSEO implements a serverless SaaS architecture built on Cloudflare Workers that leverages Durable Objects for stateful chat agents, Workflows for resilient background processing, and a unified database abstraction supporting both D1 (SQLite) and Postgres.

The every-app/open-seo repository delivers an SEO analytics platform engineered entirely for Cloudflare's edge infrastructure. Its architecture eliminates traditional server management by combining TanStack React Start for the UI layer with Cloudflare-native primitives for compute, storage, and background job processing.

Core Infrastructure Components

Cloudflare Workers and the Entry Point

Every HTTP request enters through src/server.ts, which exports the main fetch handler. This function creates a per-request Postgres client using withPgClient and dispatches to handleFetch for routing. The same file exports Durable Object classes (OnboardingChatAgent and SamChatAgent) and defines the scheduled handler for cron-triggered workflows.

export function fetch(request: Request, env: Env, ctx: ExecutionContext) {
  // Scope a per-request PG client (no-op when using D1)
  return withPgClient(() => Promise.resolve(handleFetch(request, env, ctx)));
}

Database Abstraction Layer

The system supports two storage backends selected via the DATABASE_PROVIDER environment variable. Cloudflare D1 provides a SQLite-based solution for lightweight deployments, while Postgres via Hyperdrive handles production workloads. The src/db/provider.ts module determines the active provider, with src/db/pg/client.ts and src/db/d1/client.ts implementing the connection logic. All repository operations (e.g., ProjectRepository, RankTrackingRepository) consume this abstraction via the withPgClient wrapper to ensure transactional scope.

Authentication with Better Auth

User and organization authentication uses Better Auth as configured in src/lib/auth.ts. The implementation dynamically selects Drizzle adapters based on the database provider—SQLite for D1 or Postgres for Hyperdrive—while supporting both hosted OAuth and self-hosted modes. The configuration includes optional Turnstile captcha validation and API key plugins for service-to-service authentication.

function createAuth() {
  const database =
    getDatabaseProvider() === "postgres"
      ? drizzleAdapter(pgDb, { provider: "pg", schema: pgSchema })
      : drizzleAdapter(d1Db, { provider: "sqlite", schema: d1Schema });

  const auth = betterAuth({
    baseURL: baseUrl,
    secret: getHostedSecret(),
    database,
    plugins: [tanstackStartCookies(), /* captcha & API-key plugins */],
  });

  return auth;
}

Request Routing and Agent Handling

Incoming requests follow a structured pipeline defined in the OpenSEO source code. First, requestWithPublicOrigin establishes the public origin context. Then getAuthMode in src/lib/auth-mode.ts determines whether to route to the hosted OAuth provider or the self-hosted MCP handler.

Chat agents utilize Durable Objects for persistent state. Requests to /agents/* route through routeChatAgents, which invokes authorizeChatAgent before upgrading the connection to a WebSocket with the appropriate Durable Object instance.

Background Workflows

Long-running, fault-tolerant background jobs implement Cloudflare Workflows using the pgStep helper for transactional atomicity. The src/server/workflows/RankCheckWorkflow.ts demonstrates this architecture through a multi-step SEO rank checking process that validates configurations, prepares keywords, executes live or queued checks, and finalizes results with automatic retry logic.

export class RankCheckWorkflow extends WorkflowEntrypoint<Env, RankCheckParams> {
  async run(event, step) {
    return withPgClient(() => this.runScoped(event, step));
  }

  private async runScoped(event, step) {
    const { isActive } = await pgStep(step, "check-active", {}, async () => {
      const cfg = await RankTrackingRepository.getConfigById({
        configId: event.payload.configId,
      });
      return { isActive: cfg?.isActive ?? false };
    });
    if (!isActive) return;

    const { keywords } = await pgStep(step, "prepare", {}, async () =>
      prepareRankCheckKeywords({ ...event.payload })
    );
    
    // Execute live or queued checks based on trigger type
    if (event.payload.trigger === "scheduled") {
      await runQueuedCheck(step, { keywords, ...event.payload });
    } else {
      await runLiveCheck(step, { keywords, ...event.payload });
    }
  }
}

MCP Server and AI Integration

The platform exposes a Machine Control Protocol (MCP) server in src/server/mcp/ that enables AI agents (Claude Code, OpenClaw, Hermes) to query SEO data programmatically. The MCP route (MCP_ROUTE) integrates into the main request router and supports both hosted OAuth flows and self-hosted authentication via handleSelfHostedOpenSeoMcpRequest.

Server Functions and UI Integration

The TanStack React Start frontend communicates with the backend through typed server functions located in src/serverFunctions/*. Files like keywords.ts and rank-tracking.ts export functions that the UI invokes directly, providing type-safe access to database repositories and workflow triggers.

Deployment Architectures

OpenSEO supports two distinct deployment modes documented in the repository:

Docker (Local Development)

  • Configures AUTH_MODE=local_noauth to bypass external OAuth requirements
  • Uses local SQLite or Postgres containers
  • Documented in docs/SELF_HOSTING_DOCKER.md

Cloudflare (Production)

  • Deploys to Cloudflare Workers with D1, KV, and Durable Objects bindings
  • Configures Hyperdrive for Postgres connection pooling
  • Implements Cloudflare Access for organization authentication
  • Documented in docs/SELF_HOSTING_CLOUDFLARE.md

Summary

  • The system architecture of OpenSEO eliminates server management by building entirely on Cloudflare's edge platform—Workers for compute, Durable Objects for state, and Workflows for background processing.
  • Database portability is achieved through a provider pattern in src/db/provider.ts that abstracts D1 (SQLite) and Postgres implementations behind a unified interface.
  • Real-time AI agents maintain persistent connections via Durable Objects (OnboardingChatAgent, SamChatAgent), while resistant background jobs use Cloudflare Workflows with transactional pgStep helpers.
  • Flexible deployment supports both hosted SaaS operation and self-hosted Docker configurations, with authentication adapting via getAuthMode to either external OAuth or local no-auth setups.

Frequently Asked Questions

What runtime does OpenSEO use?

OpenSEO executes on Cloudflare Workers using the V8 isolate runtime. The application bundles a TanStack React Start frontend that renders at the edge, while backend logic runs in lightweight isolates with stateful operations handled by Durable Objects and long-running tasks delegated to Cloudflare Workflows.

Can OpenSEO run entirely outside of Cloudflare?

While Docker self-hosting is documented in docs/SELF_HOSTING_DOCKER.md, the architecture fundamentally relies on Cloudflare-specific primitives. Durable Objects are required for chat agent state persistence, and Workflows handle background job resilience. Full functionality requires Cloudflare infrastructure, though the Docker mode supports local development with limited feature sets.

How does the database layer switch between D1 and Postgres?

The src/db/provider.ts module reads the DATABASE_PROVIDER environment variable to return the appropriate client. When using Postgres, the system connects via Hyperdrive for connection pooling; for D1, it uses Cloudflare's SQLite edge database. The Drizzle ORM adapters in src/lib/auth.ts normalize schema differences, allowing repositories to remain provider-agnostic.

What is the MCP server used for in OpenSEO?

The Machine Control Protocol (MCP) server exposes SEO data APIs that AI coding agents can invoke. Implemented in src/server/mcp/, this layer allows tools like Claude Code to perform keyword research, check rankings, and audit sites programmatically. The MCP handler integrates with the main request router and supports both hosted OAuth authentication and self-hosted API key modes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →