Skip to content

Configuration Reference

All federation stack configuration lives in fed.toml at the workspace root. This file is the single source of truth for services, tasks, ports, health checks, and environment injection.

File Structure

fed.toml has three top-level sections:

[global]
# Timeouts, polling intervals, workspace-level settings

[services.<name>]
# Service definitions (one block per service)

[tasks.<name>]
# Task definitions (one block per task)

Global Settings

[global]
timeout_seconds = 90          # Health check timeout per service
poll_interval_seconds = 2     # Interval between health check polls
env_file = ".local-workspace.env"  # Workspace-level env overrides file

Service Configuration

Each service is defined as a [services.<name>] block. The <name> is used to reference the service in depends_on, managed_by, and cross-service templates.

Service Fields

Field Type Required Description
label string Yes Human-readable name for console output
dir string Yes Working directory. Supports ${VAR:default} templates
port integer Yes Listening port. Use 0 for non-networked services (e.g. workers)
optional boolean No If true, skip silently when the directory does not exist. Default: false
depends_on list No Service names that must be healthy before this service starts
health_type string No Health check type: graphql, http, tcp, compose_service
health_path string No URL path for HTTP/GraphQL health checks (e.g. /health, /graphql)
compose_up string No Docker Compose command to start the service
compose_down string No Docker Compose command to stop the service
managed_by string No Parent service whose compose command also starts this one
compose_service_name string No Docker Compose service name for compose_service health checks
required_env_keys list No Environment variable keys that must exist in the service's .env
seed_check string No GraphQL query to verify data readiness after health check passes
seed_check_token string No Auth token template for the seed check query
pre_tasks list No Task names to run before startup
post_tasks list No Task names to run after startup
env_inject table No Environment variables injected into the compose subprocess

Example: Full Service Definition

[services.model-api]
label = "Model API"
dir = "${MODEL_API_DIR:cred-model-api}"
port = 3000
optional = false
depends_on = []
health_type = "http"
health_path = "/health/ready"
compose_up = "docker compose up -d"
compose_down = "docker compose down"
required_env_keys = ["API_TOKEN"]
seed_check = "{ company(id: 450931) { id name } }"
seed_check_token = "${env:commercial-api:CRED_MODEL_API_TOKEN}"

[services.model-api.env_inject]
WEB_PORT = "3000"
WEB_DEBUG_PORT = "9223"
API_REDIS_PORT = "6380"
API_TOKEN = "${env:commercial-api:CRED_MODEL_API_TOKEN}"
COMMERCIAL_JWT_SECRET = "${env:commercial-api:JWT_SECRET}"
CRED_COMMERCIAL_API_URL = "http://host.docker.internal:${port:commercial-api}"

Health Check Types

Type What It Does
graphql POSTs a { __typename } introspection query to the health path
http Sends a GET request and expects an HTTP 2xx response
tcp Attempts a TCP socket connection to the port
compose_service Checks docker compose ps status for the named compose service

Managed Services

Services with managed_by are started by their parent's compose_up command. They do not have their own compose_up but still get independent health checks. For example, the Apollo Router and commercial worker are managed by commercial-api:

[services.router]
label = "Apollo Router"
dir = "${COMMERCIAL_API_DIR:cred-api-commercial}"
port = 4000
managed_by = "commercial-api"
health_type = "graphql"
health_path = "/graphql"

Task Configuration

Tasks are one-shot commands that run at specific lifecycle points during startup. They are referenced by services' pre_tasks or post_tasks lists.

Task Fields

Field Type Required Description
label string Yes Human-readable name for console output
dir string Yes Working directory. Supports ${VAR:default} templates
command string Yes Shell command to execute
skip_check string No Shell command for idempotency -- exit 0 means "already done"
env table No Environment variable overrides for the command subprocess

Example: Task with Idempotency

[tasks.db-init]
label = "Database Init (build + migrate + seed)"
dir = "${COMMERCIAL_API_DIR:cred-api-commercial}"
command = "docker compose exec -T web yarn prep-db"
skip_check = "docker compose exec -T db psql -U cred -d cred_commercial -tAc \"SELECT EXISTS(SELECT 1 FROM pg_tables WHERE tablename = 'User')\" | grep -q t"

[tasks.db-init.env]
DATABASE_URL = "postgres://cred@db:5432/cred_commercial"

The skip_check runs before the task. If it exits 0, the task is skipped. This prevents expensive operations from re-running on every ./fed start.

Template Systems

There are two separate template systems, resolved at different times.

Dir Templates (Config Load Time)

Syntax: ${VAR_NAME:default}

Used in service and task dir fields. Resolved when fed.toml is loaded.

Resolution priority (highest first):

  1. Exported shell environment variables (export COMMERCIAL_API_DIR=...)
  2. Values in .local-workspace.env
  3. Inline default after the colon

Example:

dir = "${COMMERCIAL_API_DIR:cred-api-commercial}"

If COMMERCIAL_API_DIR is not set in the environment or .local-workspace.env, this resolves to cred-api-commercial (relative to workspace root).

Cross-Service Templates (Runtime)

Syntax: ${env:svc:VAR}, ${port:svc}, ${dir:svc}

Used in env_inject blocks and task env fields. Resolved at startup time by federation/env.py.

Pattern Resolves To Example
${env:service-name:VAR_NAME} Value of VAR from the named service's .env ${env:commercial-api:JWT_SECRET}
${port:service-name} Port number from the named service's config ${port:model-api} resolves to 3000
${dir:service-name} Absolute path to the named service's directory ${dir:model-api}

Cross-Service Environment Injection

The env_inject mechanism is how secrets and configuration flow between services at startup without duplicating values across .env files.

How it works:

  1. The fed CLI reads each service's .env file
  2. For each service with env_inject, it resolves template expressions against other services' .env files and config
  3. The resolved key-value pairs are passed as environment variables to the docker compose subprocess
  4. Docker Compose merges these with the service's own .env, with env_inject values taking precedence

Example flow -- JWT secret injection:

commercial-api/.env          model-api/.env
┌─────────────────┐          ┌────────────────────────┐
│ JWT_SECRET=abc   │ ──────> │ COMMERCIAL_JWT_SECRET   │
└─────────────────┘          │ (injected at startup)   │
                             └────────────────────────┘

In fed.toml:

[services.model-api.env_inject]
COMMERCIAL_JWT_SECRET = "${env:commercial-api:JWT_SECRET}"

Empty String = Unset

Setting a value to "" (empty string) in env_inject means "remove this variable from the subprocess environment." This is used, for example, to clear APOLLO_KEY so the router runs in offline mode locally.

Shared Secrets Reference

Secret Commercial API Model API Filter API Agent AI
JWT HMAC key JWT_SECRET COMMERCIAL_JWT_SECRET COMMERCIAL_JWT_SECRET JWT_SECRET
Model API static token CRED_MODEL_API_TOKEN API_TOKEN -- --
Redis REDISCLOUD_URL REDISCLOUD_URL REDISCLOUD_URL REDIS_URL
  • The JWT HMAC key must be identical across services. Services use it to validate each other's tokens.
  • The model API static token is how commercial API authenticates to model API. CRED_MODEL_API_TOKEN in commercial's .env must equal API_TOKEN in model-api's .env.
  • The fed CLI handles injection of COMMERCIAL_JWT_SECRET and API_TOKEN to model-api and filter-api automatically. You only need to set the base values in cred-api-commercial/.env.

Path Resolution Priority

When fed.toml is loaded, directory paths resolve in this order:

  1. Shell environment variables -- e.g. export MODEL_API_DIR=/custom/path
  2. .local-workspace.env -- workspace-level overrides
  3. Inline default in fed.toml -- the fallback value after the colon

At startup, cross-service templates (${env:...}, ${port:...}, ${dir:...}) are resolved from the live service configuration and .env files.

Environment Variable Flow

The full lifecycle of environment variables through the system:

.local-workspace.env          <-- Dir overrides (COMMERCIAL_API_DIR, etc.)
        | (config load time)
        v
fed.toml dir fields            <-- ${VAR:default} -> resolved relative paths
        |
        v
service .env files             <-- Per-repo secrets (JWT_SECRET, DB URLs, etc.)
        | (startup time)
        v
env_inject templates           <-- ${env:svc:VAR}, ${port:svc} -> resolved values
        |
        v
docker compose subprocess     <-- Controlled env: env_inject + filtered system env