# OpenSEO Authentication Modes: Hosted vs. Self-Hosted Deployment Guide

> Explore OpenSEO authentication modes for hosted vs self-hosted deployments. Understand hosted, cloudflare_access, and local_noauth options to secure your application.

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

---

**OpenSEO supports three authentication modes—`hosted`, `cloudflare_access`, and `local_noauth`—that determine how users authenticate in hosted SaaS versus self-hosted Docker or Cloudflare deployments.**

The `every-app/open-seo` repository implements a flexible authentication system defined in [`src/lib/auth-mode.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth-mode.ts). Whether you're running the managed SaaS product or deploying your own instance, understanding these modes ensures proper security configuration for your environment.

---

## The Three OpenSEO Authentication Modes

OpenSEO's authentication architecture centers on the `AUTH_MODE` environment variable. The system validates this value through a Zod schema and falls back to `cloudflare_access` when undefined or invalid.

Here's how each mode maps to deployment types:

| Mode | Deployment Target | Authentication Behavior |
|------|-------------------|------------------------|
| **`hosted`** | Every App's managed SaaS | Cloudflare Access managed by the hosted service; built-in login UI |
| **`cloudflare_access`** | Self-hosted on Cloudflare Workers | Your own Cloudflare Access organization controls authentication |
| **`local_noauth`** | Self-hosted Docker/local dev | No authentication required; requests pass through unverified |

The selection logic in `getAuthMode()` demonstrates this fallback behavior:

```typescript
export function getAuthMode(value: string | null | undefined): AuthMode {
  const parsed = authModeSchema.safeParse(value);
  if (parsed.success) return parsed.data;
  // fallback to cloudflare_access for invalid or missing values
  return "cloudflare_access";
}

```

---

## Hosted OpenSEO Authentication Mode

The **`hosted`** mode is exclusive to Every App's managed SaaS offering. When `AUTH_MODE=hosted` is set during build time, OpenSEO renders the Cloudflare Access login flow and enables hosted-specific features like billing and user profiles.

The helper `isHostedAuthMode()` in [`src/lib/auth-mode.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth-mode.ts) (lines 32-34) identifies this runtime state:

```typescript
import { getAuthMode, isHostedAuthMode } from "@/lib/auth-mode";

const authMode = getAuthMode(process.env.AUTH_MODE);
if (isHostedAuthMode(authMode)) {
  // Hosted SaaS – enforce Cloudflare Access login
}

```

Client-side code uses `isHostedClientAuthMode()` (lines 36-43) to conditionally render UI components. This check ensures the client build matches the server runtime—preventing mismatched authentication states.

---

## Self-Hosted OpenSEO: Two Authentication Paths

Self-hosted deployments choose between authentication based on your infrastructure and security requirements.

### Docker Self-Hosting: `local_noauth` Mode

For simple Docker deployments or local development, **`local_noauth`** eliminates authentication entirely. Set `AUTH_MODE=local_noauth` or omit the variable entirely:

```bash

# Docker deployment with no authentication

echo "AUTH_MODE=local_noauth" >> .env

```

In this mode, [`src/server.ts`](https://github.com/every-app/open-seo/blob/main/src/server.ts) (lines 143-151) skips authentication middleware and passes request headers unchanged. The [`src/middleware/ensure-user/resolve.ts`](https://github.com/every-app/open-seo/blob/main/src/middleware/ensure-user/resolve.ts) (lines 14-19) allows all requests through without user resolution.

This path suits internal tools or development environments where network isolation provides sufficient security.

### Cloudflare Self-Hosting: `cloudflare_access` Mode

For production self-hosting on Cloudflare Workers, **`cloudflare_access`** integrates with your Cloudflare Access organization:

```bash

# Cloudflare deployment with Cloudflare Access authentication

echo "AUTH_MODE=cloudflare_access" >> .env

```

Users authenticate through your Cloudflare Access policy before reaching OpenSEO. The same JWT tokens authorize API calls. The MCP transport layer in [`src/server/mcp/transport.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/transport.ts) (lines 64-78) uses this mode to validate incoming requests.

Documentation for these paths lives in:
- [`web/content/docs/self-hosting/docker.md`](https://github.com/every-app/open-seo/blob/main/web/content/docs/self-hosting/docker.md) — Docker/`local_noauth` setup
- [`web/content/docs/self-hosting/cloudflare.md`](https://github.com/every-app/open-seo/blob/main/web/content/docs/self-hosting/cloudflare.md) — Cloudflare/`cloudflare_access` configuration

---

## Client-Side Authentication Detection

Client builds must align with server authentication modes. Use `isHostedClientAuthMode()` to gate hosted-specific functionality:

```typescript
import { isHostedClientAuthMode } from "@/lib/auth-mode";

if (isHostedClientAuthMode()) {
  // Show hosted-only UI components (billing, user profile, etc.)
}

```

Telemetry also varies by mode: [`src/client/lib/posthog.ts`](https://github.com/every-app/open-seo/blob/main/src/client/lib/posthog.ts) (lines 49-51) disables analytics when not in `hosted` mode, respecting self-hosted privacy expectations.

---

## Authentication Mode Reference: Complete Comparison

| Aspect | `hosted` | `cloudflare_access` | `local_noauth` |
|--------|----------|---------------------|----------------|
| **Infrastructure** | Every App managed | Your Cloudflare Workers | Your Docker/container |
| **Identity provider** | Cloudflare Access (Every App org) | Cloudflare Access (your org) | None |
| **Build requirement** | `AUTH_MODE=hosted` | `AUTH_MODE=cloudflare_access` | `AUTH_MODE=local_noauth` or omitted |
| **Login UI** | Automatic | Cloudflare Access pages | None |
| **API authentication** | CF Access JWT | CF Access JWT | None (pass-through) |
| **Billing features** | Enabled | Disabled | Disabled |
| **Telemetry** | PostHog enabled | PostHog disabled | PostHog disabled |

---

## Summary

- **Three modes govern OpenSEO authentication**: `hosted` for SaaS, `cloudflare_access` for secure self-hosting, `local_noauth` for simple deployments
- **Configuration is environment-driven** via `AUTH_MODE`, parsed by `getAuthMode()` in [`src/lib/auth-mode.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth-mode.ts)
- **Hosted mode enables additional features** like billing and telemetry that are suppressed in self-hosted instances
- **Self-hosted Cloudflare deployments** inherit the default fallback behavior when `AUTH_MODE` is unspecified
- **Client and server builds must agree** on authentication mode; helpers like `isHostedClientAuthMode()` enforce this alignment

---

## Frequently Asked Questions

### What happens if I don't set AUTH_MODE in a self-hosted deployment?

The system defaults to `cloudflare_access`. The `getAuthMode()` function in [`src/lib/auth-mode.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth-mode.ts) falls back to this value when the environment variable is missing, invalid, or unparsable. This ensures self-hosted Cloudflare deployments work without explicit configuration while requiring intentional choice for unauthenticated operation.

### Can I switch between authentication modes without rebuilding?

Server-side mode changes require a restart since `process.env.AUTH_MODE` is read at bootstrap time in [`src/server.ts`](https://github.com/every-app/open-seo/blob/main/src/server.ts). Client-side detection is build-time determined—changing hosted features requires recompiling with the correct `AUTH_MODE` environment variable.

### Does `local_noauth` expose my deployment to security risks?

Yes, if reachable from untrusted networks. The `local_noauth` mode is designed for local development or Docker deployments where network isolation restricts access. For internet-facing self-hosted instances, use `cloudflare_access` with an appropriate Cloudflare Access policy to enforce authentication.

### How does authentication affect MCP (Model Context Protocol) functionality?

The MCP transport layer in [`src/server/mcp/transport.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/transport.ts) (lines 64-78) consults the current `authMode` to determine credential handling. In `hosted` and `cloudflare_access` modes, it validates Cloudflare Access tokens; in `local_noauth`, requests proceed without authentication checks.