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):
- Exported shell environment variables (
export COMMERCIAL_API_DIR=...) - Values in
.local-workspace.env - 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:
- The
fedCLI reads each service's.envfile - For each service with
env_inject, it resolves template expressions against other services'.envfiles and config - The resolved key-value pairs are passed as environment variables to the
docker composesubprocess - Docker Compose merges these with the service's own
.env, withenv_injectvalues 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_TOKENin commercial's.envmust equalAPI_TOKENin model-api's.env. - The
fedCLI handles injection ofCOMMERCIAL_JWT_SECRETandAPI_TOKENto model-api and filter-api automatically. You only need to set the base values incred-api-commercial/.env.
Path Resolution Priority
When fed.toml is loaded, directory paths resolve in this order:
- Shell environment variables -- e.g.
export MODEL_API_DIR=/custom/path .local-workspace.env-- workspace-level overrides- 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