# How OpenSEO Validates Self-Host Configurations: Team Domains, API Keys, and Telemetry

> Learn how OpenSEO validates self-host configurations. Discover how it checks team domains, API keys, and telemetry before startup, ensuring a smooth deployment.

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

---

**OpenSEO validates self-host configurations using a dedicated pre-flight system that checks environment variables—such as team domains, API keys, and authentication settings—before the application starts, aborting startup immediately if critical values are malformed or missing.**

When deploying OpenSEO (from the `every-app/open-seo` repository) in a self-hosted environment, the application relies on a strict validation layer to prevent runtime failures caused by misconfigured environment variables. This system combines a pure validation library with an orchestration script that produces a uniform report consumable by both the container startup process and the `/api/health` endpoint.

## Core Validation Architecture

The validation system splits responsibilities between two primary components in the codebase.

**[`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts)** contains stateless, pure functions that validate individual configuration values. These functions perform format checks and pattern matching without side effects.

**[`src/lib/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/selfhost-preflight.ts)** orchestrates the full validation suite. It imports the pure checkers, executes them against the runtime environment, and aggregates results into a `PreflightResult` object. If any check returns a `fail` level, the container aborts startup with a concise error message rather than attempting to boot with invalid configuration.

## Validating Critical Configuration Values

The pure validation library handles the most common configuration mistakes by enforcing strict format requirements on essential variables.

### Team Domain Format

The `validateTeamDomain` function ensures that `TEAM_DOMAIN` is a complete HTTPS URL required for Cloudflare Access authentication. According to the source code in [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts) (lines 11-32), the validator checks that the string starts with `https://` and contains a valid hostname structure.

```typescript
import { validateTeamDomain } from "@/shared/selfhost-checks";

const result = validateTeamDomain(process.env.TEAM_DOMAIN ?? "");
if (!result.ok) {
  console.error("Invalid TEAM_DOMAIN:", result.message);
  // startup will abort with a [FAIL] badge
}

```

If this check fails, the pre-flight script exits immediately, preventing the middleware in [`src/middleware/ensure-user/cloudflareAccess.ts`](https://github.com/every-app/open-seo/blob/main/src/middleware/ensure-user/cloudflareAccess.ts) from attempting to authenticate against an invalid domain.

### DataForSEO API Key Structure

The `looksLikeDataForSeoKey` function (lines 34-43 of [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts)) validates that `DATAFORSEO_API_KEY` is a base-64-encoded string representing a `login:password` pair. This catches common encoding errors before the application attempts to communicate with the DataForSEO service.

```typescript
import { looksLikeDataForSeoKey } from "@/shared/selfhost-checks";

if (!looksLikeDataForSeoKey(process.env.DATAFORSEO_API_KEY ?? "")) {
  console.warn(
    "DATAFORSEO_API_KEY does not decode to a login:password pair – double‑check the base64 encoding."
  );
}

```

### Telemetry Opt-Out Detection

The `isTelemetryOptOutValue` function (lines 45-53 of [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts)) interprets the `OPENSEO_TELEMETRY_DISABLED` environment variable. Any value other than `"0"`, `"false"`, `"no"`, or `"off"` is treated as an opt-out, giving administrators explicit control over data collection.

```typescript
import { isTelemetryOptOutValue } from "@/shared/selfhost-checks";

const telemetryDisabled = isTelemetryOptOutValue(process.env.OPENSEO_TELEMETRY_DISABLED);
if (telemetryDisabled) {
  console.info("Telemetry is disabled per OPENSEO_TELEMETRY_DISABLED");
}

```

## Authentication and Optional Feature Checks

Beyond basic format validation, the pre-flight script performs complex cross-variable validation for authentication and optional integrations.

### Auth Mode Validation

The `checkAuthMode` function in [`src/lib/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/selfhost-preflight.ts) (lines 35-101) validates that the selected `AUTH_MODE` has all required dependencies. It checks for the presence of `BETTER_AUTH_URL`, `BETTER_AUTH_SECRET`, `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, and `POLICY_AUD` according to the specific authentication mode configured. Missing required variables for the selected mode trigger a `fail` level result.

### Optional Integrations

The `checkOptionalFeatures` function (lines 56-104 of [`src/lib/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/selfhost-preflight.ts)) performs non-blocking validation of Google Search Console credentials and OpenRouter AI keys. When these values are partially configured, the function emits warnings rather than failures, allowing the application to start with reduced functionality while alerting administrators to incomplete setups.

## Running the Self-Host Pre-Flight Check

The `runSelfhostPreflight` function serves as the entry point for the validation workflow. As implemented in [`src/lib/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/selfhost-preflight.ts), it accepts an `EnvRecord`, executes all checks via `runSelfhostChecks(env)`, and returns a `PreflightResult` containing an array of `PreflightItem`s and a boolean `failed` flag.

```typescript
// src/lib/selfhost-preflight.ts
export function runSelfhostPreflight(env: EnvRecord): PreflightResult {
  const items = runSelfhostChecks(env);
  // … add runtime checks for ALLOWED_HOST, scheduled jobs, etc.
  return { items, failed: items.some(i => i.level === "fail") };
}

```

During container startup, the script at [`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts) invokes this function and formats the output for console visibility:

```typescript
import { runSelfhostPreflight, formatPreflightReport } from "@/lib/selfhost-preflight";

const result = runSelfhostPreflight(process.env);
console.log(formatPreflightReport(result));
if (result.failed) process.exit(1);

```

The same validation results are exposed via the `/api/health` endpoint through [`src/server/lib/setup-status.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/setup-status.ts), providing runtime visibility into configuration health without requiring container restart.

## Summary

- **Pure validation functions** in [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts) handle format-specific checks for team domains, API keys, and telemetry settings.
- **Orchestration logic** in [`src/lib/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/selfhost-preflight.ts) runs comprehensive checks via `runSelfhostPreflight`, validating authentication modes and optional features.
- **Early failure** prevents the application from starting with invalid configuration, avoiding cryptic runtime errors minutes into deployment.
- **Health endpoint exposure** at `/api/health` surfaces the same `PreflightResult` objects for operational monitoring.

## Frequently Asked Questions

### What happens if the TEAM_DOMAIN environment variable is malformed?

OpenSEO aborts container startup immediately. The `validateTeamDomain` function returns a failure result when the value is not a complete HTTPS URL, and the pre-flight script exits with code 1 before the application begins serving traffic.

### How does OpenSEO verify the DataForSEO API key format?

The `looksLikeDataForSeoKey` function attempts to base64-decode the string and verifies it contains a colon-separated `login:password` pair. This catches encoding errors early but does not verify credentials against the live API.

### Can OpenSEO start if optional features like Google Search Console are partially configured?

Yes, but with warnings. The `checkOptionalFeatures` validator emits warning-level `PreflightItem`s for partially configured optional integrations, allowing startup while alerting administrators via console logs and the `/api/health` endpoint.

### Where can I view the validation results after the container starts?

The [`src/server/lib/setup-status.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/setup-status.ts) module exposes the pre-flight results at the `/api/health` endpoint, returning the same `PreflightResult` JSON that was generated during container initialization.