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.
Profile format
Section titled “Profile format”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 / choosersdurable_runtime = "in_process"memory = true
[profile.mcp_servers.github] # MCP servers available to templatestransport = "http"url = "https://..."
[profile.config."dataset/pii_scan"] # per-operator config partialsbackend = "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.
Shipped profiles
Section titled “Shipped profiles”| Profile | Extends | Purpose |
|---|---|---|
local-dev | — | zero-config dev: everything bound to in-tree local operators |
secure | local-dev | adds redaction + hash-chained audit at the LLM egress |
production | secure | fail-closed gates: model/gate rejects stub results and requires a benchmark; PII scan pinned to the Presidio backend |
prod | production | what a production box points at (adds the memory backend) |
prod-infisical | secure | production with Infisical-backed secrets |
e2e | secure | integration 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.
Environment variables
Section titled “Environment variables”The engine’s deployer-facing surface is env-driven; cairn doctor reports
what’s set and reachable. The load-bearing ones:
| Variable | Controls |
|---|---|
OBS_STORAGE | run/config store backend: postgres://… or sqlite:///path (unset = per-store SQLite / in-memory) |
OBS_RUNTIME | inprocess (default) / dbos / temporal |
OBS_CHECKPOINTER | HITL durability: memory (default) / sqlite / postgres (+ OBS_CHECKPOINTER_DSN) |
OBS_JWT_SECRET | setting it enables server auth; unset = open dev server. OBS_REQUIRE_AUTH=1 makes it mandatory |
OBS_TRIGGER_PORT | server port (default 9090) |
OBS_PROFILE | active profile |
OBS_BUS | event bus: inprocess (default) or postgres://… (durable, replayable) |
OBS_AUDIT_LOG | hash-chained audit log path (default ./audit.jsonl) |
OPENAI_API_KEY | the one key required for real inference (Anthropic/Gemini optional) |
LLM_GATEWAY / LLM_GATEWAY_URL | route model calls through a proxy (e.g. the LiteLLM compose profile) instead of direct |
MLFLOW_TRACKING_URI | enables MLflow model registry (else a local JSONL registry) |
OBS_TEMPORAL_ADDRESS | Temporal 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.