Configuration
Settings live in the database config table and are edited from the Config
surface in the TUI or GUI, or with PATCH /api/config. Secrets are separate:
credentials are stored encrypted (see Credentials).
GitHub
Section titled “GitHub”| Key | Purpose |
|---|---|
github_repo | owner/name the agent manages. |
pickup_labels | Comma-separated labels an issue must carry to be picked up. Empty means every issue. |
pr_merge_method | Preferred gh pr merge method: squash (default) or rebase. |
| GitHub App settings | App id / installation when authenticating as a bot (see the GitHub App guide). |
steelframe:needs_human always skips pickup, even when a pickup label is also
present: a parked task stays parked until a human responds.
Coding runtime
Section titled “Coding runtime”| Key | Values | Default |
|---|---|---|
coding_runtime | pi (legacy claude, grok, codex, opencode and aliases resolve to pi) | pi |
coding_runtime_binary | Path or name of the Pi CLI binary (ignored for a legacy coding_runtime unless it names pi) | pi on PATH |
coding_runtime_model | steelframe/<id> runs the local OpenAI-compatible endpoint; <provider>/<id> passes through to Pi’s provider catalog | steelframe/qwen3.8 |
coding_runtime_base_url | Local OpenAI-compatible base URL for steelframe/* models | http://127.0.0.1:8080/v1 |
coding_runtime_context | Advertised context tokens for steelframe/* models (Pi contextWindow) | 65536 |
coding_runtime_output | Advertised max output tokens for steelframe/* models (Pi maxTokens) | 16384 |
Workers and one-shot decisions spawn Pi through the runtime facade; workflows
never spawn a CLI directly. Workers set an isolated PI_CODING_AGENT_DIR in
their sandbox so host Pi config cannot override these limits. A catalog model
authenticates from the model_<provider> credential, host Pi auth
(pi auth login --provider <id>), or the provider’s ambient env var; with
none it fails closed before the spawn.
Conversation intelligence
Section titled “Conversation intelligence”Clef and Clairic share one cloudflare_ai credential: the Cloudflare account
ID in the URL field and a Workers AI API token in the secret field. See
Conversation classification.
| Key | Default | Purpose |
|---|---|---|
cloudflare_account_id | credential URL | Fallback account ID when the credential has none. |
clef_model | clef | Decision model (clef or clef-flash). |
clairic_model | clairic | Extraction model. |
Discord
Section titled “Discord”The bot is off without a discord_bot credential (or
STEELFRAME_DISCORD_BOT_TOKEN). See Discord.
| Key | Default | Purpose |
|---|---|---|
discord_bot_enabled | 1 | 0 keeps the gateway off. |
discord_allowed_channels | empty | Comma-separated channel/thread IDs. |
discord_allowed_users | empty | Comma-separated Discord user IDs. |
discord_conversation_channels | empty | Channel IDs where Clef decides whether each human message is for the bot (requires Message Content + cloudflare_ai). |
discord_intake_reaction | 📥 | Reaction that files an Intake. |
discord_allow_dms | 0 | 1 answers direct messages. |
Delivery
Section titled “Delivery”| Key | Values | Purpose |
|---|---|---|
delivery_profile | live (default) or source | Run the staging/production path, or merge when green. |
external_url | URL | Base URL used in links the agent writes. |
Capacity and safety
Section titled “Capacity and safety”| Key | Default | Effect |
|---|---|---|
dispatch_min_available_mb | 1024 | Do not spawn a worker below this MemAvailable. 0 disables. |
db_memory_limit | auto | DuckDB memory_limit. auto gives it 35% of physical RAM (max 1.5 GB, min 256 MB); off restores DuckDB’s own default (80% of RAM, written back to the running instance); or a size with a unit (768MB, 1GiB — a bare number counts as bytes). |
queue_stall_minutes | 120 | Alarm (audit + Discord) when a run has waited for capacity this long: a stuck queue is named instead of waiting silently. 0 disables the alarm. |
max_compile_slots | 1 | Flock around cargo/cargo-clippy. 0 disables. |
max_parallel_tasks | 3 | Engine limit on concurrent task runs. Read at agent startup, so a change applies on restart. 0 or invalid falls back to 3. |
repo_max_in_flight | 0 | Cap on running task runs for the repo, enforced at pickup and WaitingForWorker. 0 disables. |
dispatch_overlap | serialize | How tasks whose triage touches intersect dispatch: serialize admits one member of an overlap group at a time; order runs implementation in parallel and records the group and creation-order position for the landing queue. Enforced at pickup and WaitingForWorker. |
overlap_unknown | ignore | What an empty touches prediction means: ignore treats the task as non-overlapping (more parallelism); serialize puts it in a per-repo unknown group that runs alone. |
repo_merge_strategy | steelframe_landing | How PRs land: steelframe_landing serializes them through the per-repo landing queue (rebase → verify → merge, in order); github_merge_queue enables auto-merge and lets GitHub’s merge queue serialize instead, exactly as before phase 3. |
landing_max_attempts | 3 | Landing-queue work episodes (a rebase worker, a failing-checks poll, a merge conflict or error) per PR before the row fails and the task parks with steelframe:needs_human. |
github_rate_floor | 500 | github_sync skips pickup and external PR reviews for a cycle when remaining core API requests are below this. 0 disables. |
worker_model_stall_timeout_secs | 600 | Stop a tracked Pi worker after this many seconds without model-turn progress; 0 disables. Positive values are clamped to 30–86,400 seconds. Tool execution and compaction are excluded; the existing overall worker timeout still applies. |
worker_memory_max | auto | Per-worker cgroup cap: auto, an explicit size like 8G, or off. |
memory_recycle_mb | auto | Recycle the agent process when its RSS crosses this size while idle (no workers, no task runs): auto sizes it at 60% of the cgroup’s memory.max with a 2.5G floor, an explicit size like 6G is floored the same way, and off/0 disables. The watchdog checks every 5 minutes, posts a ♻️ Discord note, and exits; systemd Restart=always brings the agent back with an empty heap. |
auto_update_minutes | 5 | Release-channel poll interval. 0 disables. |
update_channel | stable | stable reads latest.json; beta opts into prereleases from beta.json. STEELFRAME_UPDATE_CHANNEL overrides the config. |
host_exposure_minutes | 60 | Host posture scan interval. Flags services listening on 0.0.0.0/:: that are not in the allowlist and not the agent itself. 0 disables. |
host_exposure_allow_ports | 22,80,443 | Public TCP ports the agent host is expected to serve. Anything else on a wildcard address alerts and is reported to Discord once per change. |
System units that use worker_memory_max need Delegate=yes; a user unit must
leave its service cgroup before memory limits can be attached.
Docker-published ports bypass ufw: docker-proxy inserts its DNAT rules
before ufw’s, so -p 5432:5432 is reachable from the network even when ufw status shows only SSH. Publish dev services on loopback (-p 127.0.0.1:5432:5432) or add the port to host_exposure_allow_ports when it is
intentional. The scan runs on the agent host — ephemeral sandboxes skip it.
See Host exposure.
Sandbox provider
Section titled “Sandbox provider”Workers run as local host processes by default. Set sandbox_provider=e2b to run them inside E2B-compatible remote sandboxes — the provider keys are sandbox_endpoint, sandbox_template, sandbox_work_dir, sandbox_idle_timeout_secs, sandbox_cpu, sandbox_memory_mb, sandbox_max_concurrency, and sandbox_reap_minutes. The API key is the sandbox credential, not a config key. See Sandbox providers.
GUI branding
Section titled “GUI branding”The GUI paints the selected project’s logo and theme colors. Both are optional: an unset key falls back to the shipped Steelframe mark or palette.
| Key | Values | Default |
|---|---|---|
brand_logo | data:image/<type>;base64,… for the uploaded logo (PNG, JPEG, WebP, GIF, or SVG, at most 512 KiB) | unset — Steelframe mark |
brand_accent | Hex color for primary actions, active navigation, and focus | #e8ff3a |
brand_background | Hex color for the window, lists, and inputs | #030806 |
brand_surface | Hex color for the sidebar, panels, popovers, and table headers | #07140e |
brand_elevated | Hex color for hover, selected rows, and raised buttons | #0f3a22 |
brand_text | Hex color for body text | #d7e3d4 |
brand_muted | Hex color for metadata and secondary labels | #7d8e80 |
Set them from Config → Settings → Branding in the GUI. They are API-only because the TUI cannot paint a logo or a custom palette. Hairlines, selection, and overlay shades derive from the accent and background with fixed alpha, so a custom palette keeps the shipped relationships.
| Key | Values | Default |
|---|---|---|
mcp_enabled | true/1 on, false/0 off | unset = off |
Turns on the operator MCP surface at /mcp. Edit it from Config → Settings.
STEELFRAME_MCP in the environment wins: 1/true on, 0/false off. See
MCP.
Workflow intervals
Section titled “Workflow intervals”Singleton workflows run on intervals (for example harness_converge_minutes,
default 15). Each interval is a config key; inspect the Config surface for
the current set.
Environment-only
Section titled “Environment-only”Some settings are environment, not config: bind address/port, database URL, and the master key. See Install and Auth.
Operator access (auth_allowed_users, auth_github_org) is managed from
Config → Users and GET/PUT /api/access, not PATCH /api/config.