# Where OpenSEO Handles Self‑Hosting Configuration Validation

> Discover where OpenSEO handles self-hosting configuration validation. Learn about runtime checks and reusable validation functions for robust self-hosting.

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

---

**TLDR:** OpenSEO performs self‑hosting configuration validation through a coordinated two‑layer system: [`src/lib/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/selfhost-preflight.ts) orchestrates runtime pre‑flight checks during container startup, while [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts) provides pure validation functions reused across the application including the `/api/health` endpoint.

When deploying **OpenSEO** from the `every-app/open-seo` repository in a self‑hosted environment, ensuring that environment variables and credentials are correctly configured prevents costly runtime failures. The codebase implements a robust validation pipeline that checks configuration at container startup and exposes results for continuous monitoring. Understanding exactly where this **self‑hosting configuration validation** occurs helps operators troubleshoot deployment issues and maintain healthy instances.

## Runtime Pre‑Flight Validation in [`selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/selfhost-preflight.ts)

The primary entry point for self‑hosting configuration validation is [`src/lib/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/selfhost-preflight.ts), which executes immediately when a Docker or self‑hosted container starts via the CLI wrapper at [`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts).

### The Orchestration Layer

The `runSelfhostPreflight` function serves as the central coordinator. It accepts an environment object (defaulting to `process.env` in production) and delegates to three specialized helper functions:

- `checkAuthMode` – validates the authentication configuration and required variables like `POLICY_AUD`
- `checkDataForSeo` – verifies that `DATAFORSEO_API_KEY` is present and properly formatted
- `checkOptionalFeatures` – inspects optional integrations including Google Search Console, AI features, and runtime host settings

### Structured Reporting and Failure Handling

Each check produces a `PreflightItem` that aggregates into a `PreflightResult` object. This structure contains an array of validation items and a boolean `failed` flag. If any item reports a `"fail"` level, the routine returns `failed: true`, causing the container startup script to abort after printing a concise report via `formatPreflightReport`.

## Shared Validation Logic in [`selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/selfhost-checks.ts)

Concrete validation rules reside in [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts) as pure, reusable functions. This separation ensures consistent validation logic between the startup sequence and runtime health checks without code duplication.

### Pure Validator Functions

The module exports specific validators used by the pre‑flight system:

- `validateTeamDomain` – confirms `TEAM_DOMAIN` contains a valid domain format
- `looksLikeDataForSeoKey` – verifies `DATAFORSEO_API_KEY` resembles a Base64 encoded `login:password` string
- `MIN_BETTER_AUTH_SECRET_LENGTH` – a constant defining minimum secret length requirements for authentication

### Cross‑Cutting Reusability

By isolating validation logic in [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts), OpenSEO prevents code drift. The same functions validate configuration during container startup and when responding to requests at the `/api/health` endpoint defined in [`src/routes/api/health.ts`](https://github.com/every-app/open-seo/blob/main/src/routes/api/health.ts), ensuring consistent behavior across deployment stages.

## The Self‑Hosting Configuration Validation Flow

The validation pipeline executes in a specific sequence to prevent misconfigured deployments from serving traffic:

1. **Container Initialization** – The Docker container executes [`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts), which imports the main runner from [`src/lib/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/selfhost-preflight.ts).

2. **Environment Ingestion** – `runSelfhostPreflight` reads the environment object.

3. **Feature Validation** – The system pushes a `PreflightItem` for each feature category: authentication mode, DataForSEO credentials, and optional features.

4. **Pure Validation** – Each feature check invokes the corresponding validator from [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts).

5. **Fatal Error Handling** – If any check returns level `"fail"`, the runner sets `failed: true` and the startup script exits with code 1.

6. **Telemetry Gate** – Only after successful validation does [`src/lib/selfhost-telemetry.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/selfhost-telemetry.ts) initialize, ensuring misconfigurations do not generate false metrics.

## Implementation Examples

### Running the Full Pre‑Flight Check

Use the main runner to validate complete environment configurations during custom deployment scripts:

```ts
import { runSelfhostPreflight, formatPreflightReport } from '@/lib/selfhost-preflight';

// Pass a mock environment object (process.env is the default in production)
const env = {
  AUTH_MODE: 'cloudflare_access',
  TEAM_DOMAIN: 'mycompany.com',
  POLICY_AUD: '12345',
  DATAFORSEO_API_KEY: 'bXljaX... (base64)',
  // optional vars omitted for brevity
};

const result = runSelfhostPreflight(env);
console.log(formatPreflightReport(result));

// The `result.failed` flag can be used to abort startup:
if (result.failed) {
  process.exit(1);
}

```

### Using Shared Validators Directly

Import pure functions from the shared module for targeted validation in tests or custom tooling:

```ts
import { validateTeamDomain, looksLikeDataForSeoKey } from '@/shared/selfhost-checks';

const domainCheck = validateTeamDomain('example.com');
console.log(domainCheck.ok ? 'Domain OK' : `Domain error: ${domainCheck.message}`);

const keyOk = looksLikeDataForSeoKey('bXJlYW...'); // Base64 string
console.log(keyOk ? 'Key looks valid' : 'Key format invalid');

```

## Summary

- **Primary orchestration** occurs in [`src/lib/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/selfhost-preflight.ts) via the `runSelfhostPreflight` function, which runs automatically during container startup through [`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts).
- **Core validation logic** resides in [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts) as pure functions like `validateTeamDomain` and `looksLikeDataForSeoKey`, ensuring reusability across the application.
- **Failure handling** uses a structured `PreflightResult` with a `failed` flag to abort deployment when critical configuration errors are detected.
- **Health monitoring** exposes identical validation results through the `/api/health` endpoint in [`src/routes/api/health.ts`](https://github.com/every-app/open-seo/blob/main/src/routes/api/health.ts) for real‑time UI status display.

## Frequently Asked Questions

### What happens if self‑hosting configuration validation fails during startup?

When `runSelfhostPreflight` detects a fatal configuration error, it returns `failed: true` in the `PreflightResult` object. The container startup script prints a formatted report via `formatPreflightReport` and exits with status code 1, preventing the application from serving traffic with invalid settings.

### Can I run the configuration validation manually without starting the full application?

Yes. Import `runSelfhostPreflight` from `@/lib/selfhost-preflight` and pass a custom environment object to validate specific configurations. This is useful for testing deployment scripts or validating infrastructure‑as‑code changes before container initialization.

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

The `looksLikeDataForSeoKey` function in [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts) validates that the `DATAFORSEO_API_KEY` environment variable contains a Base64‑encoded string matching the expected `login:password` format required by the DataForSEO API, without exposing the actual credential values.

### Why are validation rules separated between [`selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/selfhost-preflight.ts) and [`selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/selfhost-checks.ts)?

This separation follows the single‑responsibility principle. The [`selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/selfhost-checks.ts) module contains pure, stateless validation functions that can be imported by both the startup routine and the health endpoint. The [`selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/selfhost-preflight.ts) file handles orchestration, environment reading, and report formatting specific to the container lifecycle.