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

> Explore OpenSEO's cloudflare native SaaS architecture. Discover how Durable Objects and Workflows power stateful agents and resilient background tasks for your applications.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: architecture
- Published: 2026-09-05

---

**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`](https://github.com/every-app/open-seo/blob/main/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.

```typescript
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`](https://github.com/every-app/open-seo/blob/main/src/db/provider.ts) module determines the active provider, with [`src/db/pg/client.ts`](https://github.com/every-app/open-seo/blob/main/src/db/pg/client.ts) and [`src/db/d1/client.ts`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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.

```typescript
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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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.

```typescript
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`](https://github.com/every-app/open-seo/blob/main/keywords.ts) and [`rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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.