Skip to content

Environment Configuration

Managing configuration cleanly is critical for any application that talks to external APIs. This chapter covers how to structure environment variables for your Gaia-powered application across backend and frontend, handle secrets safely, and manage environment-specific overrides.


Configuration Architecture

A typical Gaia application has two separate configuration surfaces:

Layer Technology Config Mechanism Prefix
Backend Python / FastAPI .env file + Pydantic BaseSettings None
Frontend React / Vite .env file (build-time injection) VITE_

Never expose backend secrets to the frontend

The backend .env contains your GAIA_API_KEY and other secrets. The frontend .env only contains public configuration like the backend API URL. Vite embeds VITE_-prefixed variables into the JavaScript bundle at build time — anything there is visible to end users.


Backend Configuration

Pydantic Settings Model

Use Pydantic's BaseSettings to create a typed, validated configuration object that reads from environment variables and .env files automatically:

Python
# backend/app/config.py
from pydantic_settings import BaseSettings
from pydantic import Field


class Settings(BaseSettings):
    """Application settings loaded from environment variables."""

    # ── Required ──
    gaia_api_key: str = Field(..., description="Cohesity Gaia API key")
    gaia_base_url: str = Field(
        default="https://helios.cohesity.com/v2/mcm/gaia",
        description="Gaia API base URL",
    )

    # ── Optional Gaia settings ──
    gaia_verify_ssl: bool = Field(default=True, description="Verify SSL certificates")
    gaia_security_ctx: str | None = Field(
        default=None, description="Security context for multi-tenant operations"
    )

    # ── Application settings ──
    request_timeout_seconds: int = Field(default=60, description="HTTP request timeout")
    allow_cors_origin: str = Field(
        default="http://localhost:5173", description="Allowed CORS origin"
    )
    db_path: str = Field(default="./data/sessions.db", description="SQLite database path")
    session_ttl_minutes: int = Field(default=60, description="Session time-to-live")

    model_config = {
        "env_file": ".env",
        "env_file_encoding": "utf-8",
        "case_sensitive": False,
    }


settings = Settings()

Why Pydantic BaseSettings?

  • Validation at startup — missing or malformed variables fail fast with clear error messages.
  • Type coercion"true" becomes True, "60" becomes 60.
  • Defaults — sensible defaults mean fewer variables to configure in development.
  • Documentation — the Field(description=...) metadata serves as living documentation.

Variable Reference

Required Variables

Variable Type Description
GAIA_API_KEY str Your Cohesity Gaia API key. Obtained from the Cohesity Helios portal.
GAIA_BASE_URL str Base URL of the Gaia API. Defaults to https://helios.cohesity.com/v2/mcm/gaia.

Optional Variables

Variable Type Default Description
GAIA_VERIFY_SSL bool true Set to false only in development environments with self-signed certificates. Never disable in production.
GAIA_SECURITY_CTX str None Security context header for multi-tenant Cohesity clusters.
REQUEST_TIMEOUT_SECONDS int 60 Timeout for HTTP requests to the Gaia API.
ALLOW_CORS_ORIGIN str http://localhost:5173 Origin allowed for CORS. Set to your frontend domain in production.
DB_PATH str ./data/sessions.db Path to the SQLite database for session storage.
SESSION_TTL_MINUTES int 60 How long sessions remain valid before requiring re-authentication.

Backend .env.example

Bash
# backend/.env.example
# ──────────────────────────────────────────────────────────────────────
# Copy this file to .env and fill in the required values.
# ──────────────────────────────────────────────────────────────────────

# ── Required ──────────────────────────────────────────────────────────
GAIA_API_KEY=your-api-key-here
GAIA_BASE_URL=https://helios.cohesity.com/v2/mcm/gaia

# ── Optional: Gaia connection ─────────────────────────────────────────
# GAIA_VERIFY_SSL=true
# GAIA_SECURITY_CTX=

# ── Optional: Application ────────────────────────────────────────────
# REQUEST_TIMEOUT_SECONDS=60
# ALLOW_CORS_ORIGIN=http://localhost:5173
# DB_PATH=./data/sessions.db
# SESSION_TTL_MINUTES=60

Frontend Configuration

Vite exposes environment variables to client code through the import.meta.env object. Only variables prefixed with VITE_ are included in the build.

Frontend .env.example

Bash
# frontend/.env.example
# ──────────────────────────────────────────────────────────────────────
# Copy this file to .env and adjust as needed.
# ──────────────────────────────────────────────────────────────────────

# URL of the backend API (no trailing slash)
VITE_API_BASE_URL=http://localhost:8000

# Application title shown in the browser tab
VITE_APP_TITLE=Gaia App

Accessing Variables in React

TypeScript
// frontend/src/config.ts
export const config = {
  apiBaseUrl: import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8000",
  appTitle: import.meta.env.VITE_APP_TITLE ?? "Gaia App",
} as const;

Build-time vs. runtime

Vite replaces import.meta.env.VITE_* references at build time. If you need to change configuration without rebuilding, inject values into window.__ENV__ via a script tag in index.html and read them at runtime.


Environment-Specific Overrides

Maintain separate .env files for each environment and load the appropriate one at deploy time.

Bash
# .env.development
GAIA_API_KEY=dev-api-key
GAIA_BASE_URL=https://dev-cluster.internal/v2/mcm/gaia
GAIA_VERIFY_SSL=false
ALLOW_CORS_ORIGIN=http://localhost:5173
DB_PATH=./data/sessions.db
SESSION_TTL_MINUTES=120
Bash
# .env.staging
GAIA_API_KEY=staging-api-key
GAIA_BASE_URL=https://staging.cohesity.example.com/v2/mcm/gaia
GAIA_VERIFY_SSL=true
ALLOW_CORS_ORIGIN=https://staging-app.example.com
DB_PATH=/var/data/sessions.db
SESSION_TTL_MINUTES=60
Bash
# .env.production
GAIA_API_KEY=prod-api-key
GAIA_BASE_URL=https://helios.cohesity.com/v2/mcm/gaia
GAIA_VERIFY_SSL=true
ALLOW_CORS_ORIGIN=https://app.example.com
DB_PATH=/var/data/sessions.db
SESSION_TTL_MINUTES=30

Loading Environment-Specific Files

You can point Pydantic to a specific .env file using an environment variable:

Python
import os
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    # ... fields ...

    model_config = {
        "env_file": os.getenv("ENV_FILE", ".env"),
        "env_file_encoding": "utf-8",
    }

Then at launch:

Bash
ENV_FILE=.env.staging uvicorn app.main:app --host 0.0.0.0

Secrets Management Best Practices

Never commit secrets to version control

Add .env to your .gitignore. Only commit .env.example files with placeholder values.

The .gitignore Entry

Text Only
# Ignore all .env files except examples
.env
.env.*
!.env.example
!.env.*.example

Graduated Approach to Secrets

Environment Approach Notes
Local dev .env file Simplest option. Each developer maintains their own.
CI / Staging CI platform secrets GitHub Actions secrets, GitLab CI variables, etc. Injected as env vars at runtime.
Production Secrets manager AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, or Kubernetes Secrets.

Docker Secrets at Runtime

When deploying with Docker Compose, pass secrets through environment variables rather than baking them into images:

YAML
# docker-compose.yml (excerpt)
services:
  backend:
    environment:
      - GAIA_API_KEY=${GAIA_API_KEY}
      - GAIA_BASE_URL=${GAIA_BASE_URL}

The host's environment (or a .env file in the same directory as docker-compose.yml) supplies the actual values.

Twelve-Factor App

This approach follows the Twelve-Factor App methodology: store config in the environment, never in code. It keeps your application portable across environments without code changes.

Validating Configuration at Startup

The Pydantic BaseSettings model validates all configuration when Settings() is instantiated. Add an explicit startup check in your FastAPI app:

Python
# backend/app/main.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from app.config import Settings


@asynccontextmanager
async def lifespan(app: FastAPI):
    settings = Settings()  # Validates all env vars — fails fast if misconfigured
    app.state.settings = settings
    yield


app = FastAPI(lifespan=lifespan)

If GAIA_API_KEY is missing, the application will refuse to start with a clear ValidationError — far better than discovering the problem at runtime when a user makes a request.


Next Steps