Skip to content

Profiles & environment

A pack declares what it needs; a profile decides which provider satisfies each capability; environment variables supply the credentials and endpoints. Full variable list: environment reference.

A profile is a TOML file with a single closed [profile] table — unknown keys are a hard error, so typos fail loudly:

[profile]
extends = "secure.toml" # one-level inheritance, cycle-checked
[profile.bindings] # capability -> operator (the dispatch seam)
"storage.put" = "s3/put"
"redact.text" = "redact/text"
"audit.sink" = "audit/store"
[profile.backends] # cross-cutting toggles / choosers
durable_runtime = "in_process"
memory = true
[profile.mcp_servers.github] # MCP servers available to templates
transport = "http"
url = "https://..."
[profile.config."dataset/pii_scan"] # per-operator config partials
backend = "presidio"
# policies / knowledge: attach-lists (omit = inherit all, [] = strict empty)
policies = ["high-risk-action-policy"]

On extends, bindings/backends/MCP shallow-merge child-over-parent; the per-operator config tables deep-merge; policies/knowledge attach-lists replace when present.

ProfileExtendsPurpose
local-devzero-config dev: everything bound to in-tree local operators
securelocal-devadds redaction + hash-chained audit at the LLM egress
productionsecurefail-closed gates: model/gate rejects stub results and requires a benchmark; PII scan pinned to the Presidio backend
prodproductionwhat a production box points at (adds the memory backend)
prod-infisicalsecureproduction with Infisical-backed secrets
e2esecureintegration matrix: S3 artifacts + Slack notify

Select the active profile with OBS_PROFILE (a name or a path). Unset means no overrides. The console’s Environments view edits these same files — bindings, backends, policy and knowledge attach-lists — through the UI.

The engine’s deployer-facing surface is env-driven; cairn doctor reports what’s set and reachable. The load-bearing ones:

VariableControls
OBS_STORAGErun/config store backend: postgres://… or sqlite:///path (unset = per-store SQLite / in-memory)
OBS_RUNTIMEinprocess (default) / dbos / temporal
OBS_CHECKPOINTERHITL durability: memory (default) / sqlite / postgres (+ OBS_CHECKPOINTER_DSN)
OBS_JWT_SECRETsetting it enables server auth; unset = open dev server. OBS_REQUIRE_AUTH=1 makes it mandatory
OBS_TRIGGER_PORTserver port (default 9090)
OBS_PROFILEactive profile
OBS_BUSevent bus: inprocess (default) or postgres://… (durable, replayable)
OBS_AUDIT_LOGhash-chained audit log path (default ./audit.jsonl)
OPENAI_API_KEYthe one key required for real inference (Anthropic/Gemini optional)
LLM_GATEWAY / LLM_GATEWAY_URLroute model calls through a proxy (e.g. the LiteLLM compose profile) instead of direct
MLFLOW_TRACKING_URIenables MLflow model registry (else a local JSONL registry)
OBS_TEMPORAL_ADDRESSTemporal cluster (default localhost:7233)

Secrets follow the same reference discipline everywhere: connections and stores hold references; values live in the secret store (env backend by default, Infisical optional) and are never printed.