Configuration Reference
Cacophony uses a YAML configuration file per node, stored at $CACOPHONY_DIR/config.yaml. The same configuration on every node is sufficient for cluster operation.
Splitting a config across files
A config can import other YAML files, so larger setups usually split it up: config.yaml as the entrypoint, projects.yaml for the projects: map, shared project defaults in project.yaml, role-scoped agent snippets under agents/ (for example pi-common.yaml for Pi runtime mixins, persistent-observer.yaml for observers and controllers), tui.yaml for named themes, and shared template values in values.yaml via values.imports.
- Later keys win. Inline keys override imported defaults, so don't re-declare a shared block such as
agent_defaultsunless you mean to replace it. - Missing imports warn. The loader skips a missing file and reports a structured warning; add
required: trueto an import to make it a hard error. - Missing profiles don't block startup. A persistent declaration naming an unknown profile is skipped and reported by
caco config validaterather than failing the daemon. - Check the result, not the fragments:
caco config values,caco config eval '<expr>'andcaco config validate --strictshow the fully resolved config.
This fleet's configuration lives in the separate cacophony-config repository. The operator's $CACOPHONY_DIR/config.yaml (default ~/.cacophony/config.yaml) imports daemon/checkouts/cacophony-config/config.yaml directly. The in-repo .cacophony/config.yaml is a temporary compatibility forwarder: retain it until runtime roots and bootstrap consumers have cut over. Profiles, flows, Pi overlays, and the self-contained .cacophony/config.ci.yaml policy fixture remain here, and the caco binary embeds the profiles, the Cacophony project flows and the Pi overlays, so agents need no Cacophony canonical checkout for them.
Fleet source changes ship through a Git PR in cacophony-config.
The daemon's config refresher fetches the configured branch about once a minute, exports the fetched commit to a temporary tree, and runs the activation preflight, config load and strict validation on it; a valid commit is checked out under the canonical writer fence and hot reload applies it, while an invalid or deferred commit leaves the checkout on its last good commit.
caco checkout refresh --project cacophony-config runs the refresher on demand, and the private config generations it replaced are retired. Product schema/runtime/profile changes need a linked product PR. Use separately owned source worktrees, never edit the home wrapper or daemon-owned checkout to simulate rollout, and distinguish source merge, refreshed config checkout, compatibility deferral/activation, running daemon config, and loaded agent resources.
Only when an explicitly authorized bootstrap change needs distribution, distribute the home import wrapper, not a materialized config or the config repository's root file with checkout-relative imports. Preview with caco config distribute --json before applying with --yes.
Config File Selection
--config Flag
Every caco executable accepts an explicit config path, for example CONFIG="/path/to/config.yaml"; caco --config "$CONFIG" status. This flag is parsed before subcommand dispatch, so it works uniformly across caco, caco_daemon, caco_bd, and all alias binaries.
Resolution Order
CONFIG="/path/to/config.yaml"; caco --config "$CONFIG" ...(if provided, authoritative for entire invocation)$CACOPHONY_DIR/config.yaml$CACOPHONY_DIR/config.yml- Error (no silent synthesis)
caco processes must pass the resolved config path explicitly. Child processes must not fall back to ambient environment discovery.
Recursive Schema Browser
The complete field reference is generated from the current runtime schema and split into lightweight Pages under Recursive Configuration Schema. It lists every top-level section and recursive field path exposed by caco config schema --json, including deeply nested maps such as tui.graphics, tui.themes.<name>.graphics, project remotes/integration, GitHub SSH routing policy, dynamic-node relay settings, speech routing, release channels, image generation, and persistent-agent declarations.
Open the generated schema index for the current per-section pages and row counts; they are regenerated with just docs-build and are not duplicated here.
Example Config Skeleton
version: 1
node:
default: localhost # Semantic node name (not transport)
nodes:
- name: localhost
host: localhost # Network address for this node
ssh:
username: operator # Also accepts "user" as alias
port: 22
key:
path: $HOME/.ssh/id_ed25519
tls:
cert: $HOME/.cacophony/pki/nodes/localhost.crt
key: $HOME/.cacophony/pki/nodes/localhost.key
identities:
- name: personal
user:
name: Example User
email: user@example.com
auth:
key:
path: $HOME/.ssh/id_ed25519
projects:
- name: cacophony
remote: ssh://git@github.com/someorg/cacophony.git
default_branch: main
identity: personal # References identities[].name
github_ssh_routing: auto # auto | ssh_over_443 | default_port
beads:
enabled: true
branch: beads # Git branch for bead journal
worktree:
base_dir: $HOME/.cacophony/agents/cacophony
agent_defaults:
type: codex
profile: agent
resources:
vcpu: 1.0
memory: 8GiB
disk: 40GiB # Isolation backend writable work disk/overlay ceiling
isolation: # Optional; host is the default backend
backend: host # host | cloud-hypervisor | firecracker | kata | oci
required: false
unavailable: fallback-host # fail | fallback-host
mounts:
checkout: host-rw # host-rw | guest-private | none
cache: ro # none | ro | cow
artifacts: host-dir # host-dir | git-bundle | tarball
network:
mode: daemon-proxy # daemon-proxy | nat-allowlist | none
test:
command: cargo test
maxParallel: 1
maxRuntimeSecs: 1200
# Fork-and-PR topology (bd-a00003). Optional. When present, the
# daemon configures these named remotes inside the canonical
# checkout in addition to `origin = remote` above, and refuses
# to place a `push: forbidden` URL at `origin`.
remotes:
fork:
url: git@github.com:example-user/project.git
push: allowed
default_branch: user/main
upstream:
url: git@github.com:example-org/project.git
push: forbidden
default_branch: main
pr_target: true # PRs land here
integration:
reintegrate_target: fork/user/main
pr_base: upstream/main
# Short-term fallback while PR auto-merge is unproven:
# use direct + local_merge, then restore review + pull_request.
default_intent: direct # direct | review | artifact_only | none
backend: local_merge # local_merge (default) | pull_request
services:
caco-daemon:
- node: localhost
port: 11100 # Local HTTP listener
cluster_port: 12100 # Cluster HTTPS listener
sidecar: 11200 # Sidecar HTTP
sidecar_cluster_port: 12200 # Sidecar HTTPS
agents:
transient_compute: # Optional provider-neutral short-lived agent scheduler
default: fake # Used by --compute default
max_parallel: 4 # Global active transient-job ceiling
max_runtime_secs: 3600 # Hard runtime ceiling
launch_spec_ttl_secs: 600 # Launch-spec validity, capped by runtime
max_retries: 1 # Global retry-attempt ceiling
reaper_grace_secs: 30 # Grace before terminal-resource reaping
budget_cents_per_day: 2000 # Admission budget, not a billing system
providers:
fake: # Deterministic daemon/CLI test provider
type: fake
enabled: true
dynamic_node_template: transient-worker
max_parallel: 2
max_runtime_secs: 600
launch_spec_ttl_secs: 120
max_retries: 1
sku: test-small
allowed_skus: [test-small, test-medium]
workload_profile: burst
allowed_workload_profiles: [burst]
cost_per_minute_cents: 0
artifact_uri: fake://artifacts
providers:
openai:
base_url: https://api.openai.com/v1
api_key_file: /run/secrets/openai-api-key
models:
- gpt-5.6-sol
- gpt-5.4-mini
anthropic:
base_url: https://api.anthropic.com
auth_token_file: /run/secrets/anthropic-auth-token
models:
- claude-sonnet-4-6
- claude-haiku-4-6
agents.transient_compute configures the provider-neutral scheduler used by caco agent new --compute PROVIDER and caco bd dispatch --compute PROVIDER. --compute local preserves the ordinary local tmux-backed path, --compute default resolves the configured default provider, and the built-in fake provider exercises launch-spec hashing plus admission/budget logic without creating cloud resources. Runtime ceilings, launch-spec TTLs, retry ceilings, SKU/workload-profile allow-lists, reaper grace, and orphan quarantine are enforced before provider submission or cleanup. Real dynamic-node providers plug in behind the same schema.
Model availability is configured canonically under providers.*.models and augmented by model_discovery. Legacy per-agent catalogs such as agents.claude.models and agents.codex.models are removed.
speech:
tts:
model: gpt-4o-mini-tts # also: gemini-3.1-flash-tts-preview, azure/speech/azure-tts
# command_template: some_tool --prompt "$BODY" --out "$OUTPUT_FILE"
voice: sage
lang: # Ordered TTS language preferences; first non-blank = default
- en-US # default synthesis language
- en-GB # alternates/fallbacks surfaces may offer
- ja-JP
# lang omitted or empty resolves to en-US; resolved default persists in TTS daemon state
voice_filter: # Sox-based post-TTS voice filter
preset: telephone # Named preset (see table below)
rotation: # Or rotate filters per agent
presets:
- telephone
- hall
- lofi
- radio
- cave
combine_with_voice: true # Combine with voice rotation
profiles: # Runtime-selectable named TTS bundles
mission-control:
model: azure/speech/azure-tts
voice: Mai-Voice-Finetuned-1
voice_filter:
preset: radio
local-fixture:
model: fixture-tts
command_template: 'printf "%s" "$BODY" > "$OUTPUT_FILE"'
personas: # Per-event speak categories
alert:
profile: mission-control
voice: nova
voice_filter:
preset: radio
director_note: crisp urgent status
hrtf: # Disabled/no-op unless fully configured
enabled: false
dataset_path: builtin-test
dataset_license: test fixture only
backend: identity_stereo
stt:
model: gpt-4o-mini-transcribe
# command_template: stt_tool "$AUDIO_FILE" > "$TRANSCRIPT_FILE"
vad: # top-level VAD config (bd-a00004)
enabled: true # enable VAD pre-processing for any STT backend
engine: silero # silero, webrtc, etc.
threshold: 0.5 # speech probability threshold (0.0–1.0)
min_speech_duration_ms: 250 # discard segments shorter than this
min_silence_duration_ms: 300 # silence before segment ends
speech_pad_ms: 100 # padding before/after segments
model: silero-v6.2.0 # VAD model name
scribble: # local Whisper STT via scribble
model: large-v3-turbo-q8_0 # whisper model name
acceleration: default # default | metal | vulkan | cuda | coreml | cpu
language: en
vad:
enabled: true # scribble-specific VAD override (inherits stt.vad if default)
model: silero-v6.2.0 # Silero VAD model name
agent: # Per-target STT overrides
rewriting: false
smart_append: false
enter_after_completed: false
scratchpad:
rewriting: true
smart_append: true
terminal:
rewriting: false
text_field:
rewriting: true
bead:
rewriting: true
io:
output:
mode: local-default # local-default | local-device | pulse-default
device: null
input:
mode: local-default
device: null
inputs: # Named input sources (bd-a00005)
primary_mic:
type: alsa
device: "hw:0,0"
remote_pulse:
type: pulse
server: "192.0.2.10:4713"
device: default
remote_ssh:
type: ssh_pulse
node: example-audio-node
device: default
tui:
reload_config: live # live | on_tui_restart
graphics:
batchUploads: true # Batch kitty APC writes; false restores legacy per-surface flushing
detailedPerfTelemetry: true # Richer cache/traffic telemetry; false restores coarse perf accounting
imageSourceCache: true # Cache decoded/scaled background images; false restores legacy decode/resize path
surfaceUploadDedupe: true # Skip unchanged kitty surface reuploads; false restores pointer-only invalidation
activeAnimationOnly: true # Skip idle animation wakeups unless animated kitty surfaces are active
disableOnResize: true # Suppress kitty graphics while terminal/frame geometry stabilizes
resizeDebounceMs: 1000 # Debounce before re-enabling graphics after resize
renderScale: 0.5 # raster scale multiplier; 2 = HiDPI, 1 = normal, 0.5 = half-scale
scaledBackgroundCache: true # bucket procedural background cache sizes
scaledBackgroundCacheMaxPx: 1280 # longest cached procedural-background edge
gpuRendering: false # headless wgpu compositing for eligible surfaces (default: false)
gpuBackend: default # default | cpu | vulkan | metal | dx12
sparkline: # Kitty graphics sparkline config
style: gradient_fill_line # bar | line | gradient_fill_line
opacity: 0.8
fadeOpacity: 0.0
levels: 10
button: # Button chrome style layers
# ... GraphicsStyleLayers fields
buttonPadding: 1 # Uniform, or { top, left, bottom, right }
spanGlowBleedX: 2 # Horizontal glow bleed columns
spanGlowBleedY: 1 # Vertical glow bleed rows
profiles:
- name: agent
hooks:
on_start:
- type: command
command: caco agent status set "starting"
timeout: 10
on_complete: []
reintegration:
mode: direct
Required Fields
This short table highlights the minimum shape for most clusters. For all nested required fields, use the generated recursive schema browser.
| Field | Description |
|---|---|
version | Config schema version (currently 1) |
nodes | At least one node entry |
projects | At least one project entry (for nodes running agents) |
services.caco-daemon | Daemon listener configuration |
Common Optional Fields
The table below calls out high-value operational fields. It is intentionally not the exhaustive list; the generated recursive schema browser is the comprehensive reference for every optional nested field.
| Field | Description |
|---|---|
node.default | Default node name for resolution |
profiles | Agent profile definitions |
providers | Provider base URLs and secret file paths |
speech | TTS/STT model and audio I/O defaults |
macos.colors | Native macOS app color defaults, including foreground/background and palette entries. The app reads these from effective daemon config; user ColorPicker choices remain local app overrides until Settings resets to configured defaults. |
webhooks / projects[].webhooks | Token-authenticated HTTP ingress served at /hooks/<scope>/<id> on a dedicated listener: bead/bash/action handlers, per-hook or section token/token_env/token_file, bind_host/bind_port, and opt-in funnel. Distinct from project lifecycle hooks; validation requires a resolvable token for public ingress. |
services.caco-daemon-extra-env | Map of KEY: VALUE applied to the daemon process environment before the Tokio runtime starts, so spawned git/subprocess children inherit it (e.g. GIT_LFS_SKIP_SMUDGE, PULSE_SERVER). Portable non-Nix sibling of the Home Manager extraEnv; config-only, no rebuild. |
Per-project agent_defaults | Default agent type, profile, merge policy, resources, and optional isolation backend request |
projects[].friend_projects and scoped friend_projects | Declare related configured projects for agents. Project-level entries inherit into agents, while agent_defaults, config-profile, and persistent-declaration entries can narrow or disable them. Each entry names the target project, branch or pinned revision, permission strings such as checkout_read / checkout_write / beads_read / beads_create, and optional checkout materialization. checkout_write requires a materialized branch checkout, enables first-party friend-project reintegration/PR flows, and still does not grant blanket all-project authority. When materialize is true, setup creates or backfills an agent-owned friend-checkouts/<project>/ clone; friend bead permissions become named-project bead-only token authority, pending/ready/failed state is surfaced in refresh/status/log evidence, and stale extras are preserved until explicit cleanup. |
projects[].remotes | Optional named git remotes for fork-and-PR workflows. Each remote declares url, push (allowed or forbidden) and optional pr_target: true. |
projects[].integration | Optional project reintegration policy: reintegrate_target, pr_base, default_intent (direct, review, artifact_only, none), and backend (local_merge or pull_request). For -style PR rollout fallback, keep the fork/upstream refs declared but set default_intent: direct and backend: local_merge until PR auto-merge / agent-merge is proven reliable. |
projects[].github_ssh_routing | Optional daemon-managed GitHub SSH routing policy for implicit GitHub SSH remotes with configured identities: auto, ssh_over_443, or default_port. Node-level policy wins when present. |
Per-project hooks | Project-level lifecycle hooks (checkout_updated, tag_pushed) |
Per-node ssh | SSH overrides for cert distribution and remote ops |
nodes[].github_ssh_routing | Node-local override for daemon-managed GitHub SSH routing, useful when one host should keep the configured default endpoint while others use SSH-over-443. |
sync.state | Cluster state replication triggers. push.* controls proactive peer broadcasts; pull.on_connect.enabled controls startup/rejoin catch-up; pull.periodic.enabled is the only switch that starts the periodic pull timer. |
shell.shell_rc | Global inline shell init for agent sessions (layered with project and profile shell_rc) |
tui.reload_config | Config reload mode: live (default, hot-reload on change) or on_tui_restart (defer until restart) |
tui.themes / tui.theme_name | Named TUI theme registry and selected default theme. Theme entries can import fragments and define explicit colors.foreground_color, background_color, and color0–color15 palette slots plus optional graphics overrides. |
nodes[].tui.theme_name | Per-node TUI theme override layered on top of the global TUI config, useful for assigning distinct registered enterprise variants to static nodes. |
speech.io.inputs | Named audio input sources for STT routing (map of name → alsa, pulse, or ssh_pulse source) |
speech.tts.lang | Ordered list of preferred TTS language codes. The first non-blank entry is the default synthesis language; later entries are alternates/fallbacks surfaces may offer. An omitted or all-blank list resolves to en-US. The resolved default is persisted in TTS daemon runtime state so it survives daemon restarts for session resumption alongside the active voice and speed. Node-level speech overrides win over the global value on merge. |
speech.tts.fallback_5xx_threshold | Provider-5xx threshold for implicit substitute-model fallback. 0 is authoritative disabled policy: it wins fail-closed over missing or stale positive process environment, preserves the configured primary model/voice, and uses the bounded TTS-down notification instead. Positive values explicitly opt into fallback; inspect effective model/provider/threshold/source and process generation with caco tts status --json. |
speech.tts.voice_filter | Sox-based post-TTS voice filter: preset, custom sox chain, or rotation pool |
speech.tts.n | Maximum simultaneous agent-speech playback slots for the TTS daemon. Default/effective 1; values <=1 keep sequential FIFO playback, values >1 allow that many concurrent slots. |
speech.tts.overlap_secs | Optional cap on intentional overlap between concurrent TTS daemon clips. Omitted allows full-slot overlap, 0 disables intentional overlap, and positive values such as 7 delay new playback until active clips are near their expected end. |
speech.tts.profiles | Named runtime-selectable TTS profile/effect bundles. Each profile uses the ordinary speech.tts shape; command-template profiles advertise profile.model or the map key in audio capabilities and are selectable with caco tts set-profile --profile <name>. |
speech.tts.personas | Named speak-event categories used by caco msg speak --persona <name>. Persona defaults may provide profile/model/voice/speed/filter/director-note fields, while explicit per-event overrides still win; unknown persona names are rejected. |
speech.tts.hrtf | Disabled-by-default decoded-audio processing seam. Processing requires enabled, dataset path, and license/provenance metadata; missing or unknown backends are no-ops. |
speech.{tts,stt}.command_template | Shell command template for ad-hoc speech adapters. TTS templates receive env vars such as BODY/OUTPUT_FILE; STT templates receive AUDIO_FILE/TRANSCRIPT_FILE. |
speech.stt.language | Default STT language hint for backends that support it (for example en). |
speech.stt.vad | Top-level VAD config for generic STT chunking. Applied before any STT backend (whisper, scribble, etc.). Scribble inherits this if its own vad block is at defaults. |
speech.stt.{agent,terminal,scratchpad,text_field,bead} | Per-target STT overrides for rewriting, smart append, auto-Enter, and VAD sensitivity |
tui.graphics.batchUploads | Batch kitty graphics upload writes within one pass; set false to restore legacy per-surface flushing |
tui.graphics.detailedPerfTelemetry | Enable richer cache-category and estimated kitty wire-traffic telemetry; set false to restore coarse perf accounting |
tui.graphics.imageSourceCache | Cache decoded/scaled background image intermediates beneath the PNG cache, keyed by path plus file identity so same-path replacements invalidate stale cached bitmaps; set false to restore legacy decode/resize behavior |
tui.graphics.surfaceUploadDedupe | Skip unchanged kitty surface reuploads even when a fresh allocation is registered; set false to restore pointer-only invalidation |
tui.graphics.activeAnimationOnly | Skip idle animation redraw wakeups unless at least one visible kitty surface is currently animated; set false to restore the legacy always-wake behavior |
tui.graphics.disableOnResize | Suppress kitty graphics during terminal or frame-geometry changes while the ratatui text baseline continues; default true |
tui.graphics.resizeDebounceMs | Milliseconds to wait before re-enabling kitty graphics after resize/frame geometry stabilizes; default 1000 |
tui.graphics.renderScale | Control kitty bitmap raster scale; default 2 preserves HiDPI fidelity, and fractional values such as 0.5 enable half-scale rendering when throughput matters more than sharpness |
tui.graphics.scaledBackgroundCache | Bucket procedural/scalable background render sizes so solid, gradient, glow, and scanline backgrounds reuse high-resolution cached assets across nearby panel dimensions |
tui.graphics.scaledBackgroundCacheMaxPx | Longest edge in pixels for scaled procedural background cache buckets; default 1280 |
tui.graphics.sparkline | Kitty graphics sparkline rendering style, opacity, and quantisation levels |
tui.graphics.button | Button chrome style layers for reusable Button widgets |
tui.graphics.buttonPadding | Button padding (uniform integer or per-side map) |
tui.graphics.spanGlowBleedX/Y | Extra columns/rows for span-level glow bleed beyond text cells |
tui.graphics.gpuRendering | Enable headless wgpu compositing for eligible bitmap surfaces; falls back to CPU compositor on init failure |
tui.graphics.gpuBackend | GPU backend: default (Metal/Vulkan/DX12 per platform), cpu, vulkan, metal, dx12 |
projects[].test.maxRuntimeSecs | Default hard runtime ceiling for queued test jobs (default: 600). Jobs exceeding 80% emit a warning; Cargo/Rust commands append target-lock/process diagnostics, and hitting the limit kills the isolated child process group with an explicit timeout error. |
projects[].build.maxRuntimeSecs | Default hard runtime ceiling for queued build jobs (default: 600). Build jobs share the same 80% warning and Cargo/Rust silence diagnostics, while request-level caco build run --timeout <seconds> can override one submission. |
agents.heartbeat / projects[].agents.heartbeat / nodes[].agents.heartbeat | Global defaults with project, node, and per-agent overlays. Fields are enabled, selector (all, persistent, workers, none), interval_secs, jitter_secs, max_backoff_secs, allow_runtime_opt_out, conditions, and overrides.<agent>. Conditions are OR timer gates: claimed_beads (authoritatively owned in-progress/merging work), unmerged_prs (locally linked open/returned PR work), and pending_inbox (visible unread inbox items, without consuming them). Omission inherits; [] clears inherited conditions for unconditional controller/task timers. Each daemon schedules only local agents; existing enablement/runtime controls and lifecycle exclusions still apply. heartbeat on does not bypass conditions. Unmatched/unknown ticks advance the normal interval/jitter without a wake or feed/inbox event. Runtime status includes conditions and the historical last-due-tick matches/unknowns, alongside controls, suppression and schedule. |
agents.agent_directory_warn_age_secs | Positive integer warning window for caco doctor to flag old local managed-agent directories (default 604800 seconds / 7 days). CACO_AGENT_DIRECTORY_WARN_AGE_SECS can override it for one doctor run. This is diagnostic-only and points operators to prune previews; it is not a retention or cleanup policy. |
projects[].agent_defaults.resources | Optional per-agent ceilings. vcpu and memory constrain host-backed agents where supported and act as admission/enforcement inputs for isolation backends; disk caps writable work disks or overlays for isolation backends. |
projects[].agent_defaults.warmup_cache_injection / profile warmup_cache_injection | Disabled-by-default advisory prompt-injection policy for warmup-cache startup hints. Fields are exactly enabled, max_age_seconds (default 86400, clamped to at least 1), and max_chars (default 2048, clamped to at least 1); unknown keys are rejected during config/profile validation. When enabled, only the newest valid cache artifact matching project/profile/model may contribute a clearly labeled, bounded advisory block; missing, invalid, stale, or disabled cache data is a non-fatal skip. |
projects[].agent_defaults.completed_retention_bytes | Parseable byte-size string capping disk retention of completed agent checkouts |
updates.launcher_archive_count | Number of previous launcher binaries to retain for rollback (default: 1). On failed health checks, caco restart walks the archive newest-first; live archived launcher binaries are retained beyond the cap until their process exits. |
updates.android | Configured Android companion APK update targets for caco update android; entries name an adb serial plus optional host node, remote-host installs copy the APK to that adb host before installation, and installs remain explicit/dry-run by default. |
interactive_defaults | Default runtime type, profile, provider, model, and goal for caco claude / caco pi interactive sessions |
dynamic_nodes | Template-based node definitions for container/cloud deployments. Each template defines services, relay config, and peer allowlists for nodes that register dynamically. |
Per-node health_expectations | Mark expected peer-health drift (expected_unreachable, expected_mismatch, expected_degraded) with an operator-facing note. Probing stays intact; these are shown separately in health summaries and demoted in caco doctor so advisory peers do not look like unexplained incidents. |
Per-project agent_defaults.persistent_agent_idle_timeout_secs | Idle timeout before the daemon auto-restarts a frozen persistent agent |
projects[].checkout_bootstrap | Shell commands to run inside fresh project checkouts (e.g. install git hooks) |
services.cacophony.configTemplateEnv | Home Manager: non-secret values consumed by config.yaml template expressions |
services.cacophony.extraEnv | Home Manager: runtime-only process environment variables |
error_logging | Error logging configuration. auto_file_beads (default false) controls whether the audit watcher files draft beads for daemon-level errors and crashes. default_project sets the target project for those beads. Optional oracle_dispatch is another disabled-by-default gate for future oracle dispatch adapter invocation; enabled defaults false and adapter only names the future adapter implementation. |
files | Project file-cache limits and image defaults. max_file_size defaults to 5MB and validates the final stored blob; image_handling.default_behavior is preserve, compress, or reject, with optional auto_compress, compression_format (jpeg, png, webp), and compression_quality (1-100). caco file add can override these defaults for one file, while caco file sync publishes/pulls the cache through the project state branch. |
default_project | Default project for node-scoped persistent agents, daemon-level operations, and project-scoped CLI commands that omit --project after explicit CLI/env resolution. Individual agents can override via their project: field. Omitted project flags resolve to one project rather than global aggregation; cross-project results require an explicit command opt-in such as --all-projects. Interactive runtime shorthand commands remain stricter and do not silently use default_project. |
State Sync Trigger Controls
sync.state separates local snapshot persistence from mesh traffic. The daemon may continue writing local snapshots for UI health even when all peer push triggers are disabled, but it only sends peer full-state pushes for enabled sync.state.push triggers such as push.on_connect, push.on_peer_connect, or push.periodic. Request-driven catch-up is similarly trigger-keyed: sync.state.pull.on_connect.enabled governs startup/rejoin full-state pulls, while sync.state.pull.periodic.enabled is the switch that starts the periodic incremental pull loop.
Periodic pull mode changes the request shape. mode: full omits per-peer since watermarks and requests a full supported-family snapshot; mode: delta sends watermarks so peers can filter supported history families by timestamp. Use the generated sync schema page for the full trigger/family matrix.
Node Resolution
The current node is resolved in this order:
--nodeflagCACO_NODEenvironment variableconfig.node.default- Error
Port Conventions
If cluster-facing ports are omitted:
cluster_port = port + 1000sidecar_cluster_port = sidecar + 1000
| Family | Range |
|---|---|
| Local service APIs | 111xx |
| Local sidecars | 112xx |
| Cluster service APIs | 121xx |
| Cluster sidecars | 122xx |
Config Validation
caco config validate
Validates:
- Schema correctness
- Unique node names
- Unique project names
- Unique service listener tuples (no port conflicts)
- Each project references an existing identity
- TLS file paths exist when cluster listeners are enabled
- Provider secret file paths exist when configured
- Project-local profile files parse successfully, and persistent declarations do not compose conflicting profile stacks such as incompatible reintegration modes
- When run from the Cacophony source repository, checked-in repository policy: the project test budget; active-theme, theme-registry, and Termux-TTS safety values; persistent observer composition; shipped profile frontmatter and prompt guardrails; and regeneration of
docs/profiles.htmlfrom.cacophony/profiles/**
The source-repository checks belong to caco config validate rather than Rust tests that read the live .cacophony/ tree. This makes them run for config-only reintegrations; the merge gate keeps new findings fatal while --demote-preexisting-vs <target-ref> demotes findings already present on the target.
Strict Validation
caco config validate --strict
Treats null-resolving template expressions as errors, catching undefined variables that would silently resolve to empty strings in normal mode. Intended for pre-merge smoke tests in agent before_reintegration hooks.
Project Config Directory Override
caco config validate --project-config-dir /path/to/checkout/.cacophony/
caco config show --project-config-dir /path/to/checkout/.cacophony/ --section projects
Validates or inspects config using a project config directory from an agent checkout instead of the live daemon checkout. This lets agents validate and review their config changes before reintegration without modifying the real project config. Both commands validate that the override directory exists before loading config, so a typo cannot silently fall back to the operator's default config. For config show, if the project config directory contains config.yaml or config.yml, that file is rendered directly so the output matches --config /path/to/checkout/.cacophony/config.yaml; config show still rejects mixing --config and --project-config-dir because the source would be ambiguous. Agents should make project config edits only in their managed checkout and land them through reintegration, not by editing $CACOPHONY_DIR/config.yaml, ~/.cacophony/config.yaml, or $CACOPHONY_DIR/daemon/checkouts/<project> directly; stale, dirty, or broken canonical checkouts are operator/controller recovery evidence, not a prompt for worker-side resets or hand-written live-config repair. Can also be set via the CACOPHONY_PROJECT_CONFIG_DIR environment variable.
Resolved YAML Output
caco config validate --show-resolved
Prints the fully template-resolved YAML with line numbers for debugging template expressions.
Template expression inspection
caco config eval 'values.providers.default'
caco config eval --strict 'deepMerge values.base values.overlay'
caco config values --json
caco config eval evaluates one template expression against the same resolved values and globals context used while rendering config and reports the stable YAML value type plus any null-resolution diagnostics. --strict turns those null diagnostics into a command failure. caco config values dumps the merged values: namespace with the same diagnostics, which is useful when imported value files and inline overrides interact.
Materialized Config Output
caco config validate --show-materialized
Prints the fully materialized config after template rendering. Useful for inspecting the final config the daemon would see. When combining --show-materialized with --project-config-dir, the CLI warns which source won: with no explicit --config it materializes the project overlay from CACOPHONY_PROJECT_CONFIG_DIR; with both flags present it materializes the explicit --config content and tells you to point --config at <project-config-dir>/config.yaml if you want that wrapper rendered directly.
Config Comparison
caco config diff
Compares the local config hash against hashes reported by peer daemons.
Reports mismatches so you can identify nodes with outdated or divergent configuration.
A mismatch is an operator-visible drift signal rather than automatic proof that the peer daemon API is unreachable: when the lightweight API probe is healthy, remote forwarding and read-only inspection should still try the real request, while unreachable or degraded API probes return structured peer-unreachable failures.
If a peer's /api/v1/config/hash probe is still slow or failing but a lightweight data-plane probe such as /api/v1/node succeeds, peer visibility should promote from first-probe-pending or unreachable to reachable/degraded-with-diagnostics instead of claiming the node is still merely settling.
The same command also has a local config VCS mode: caco config diff <tag> compares the current local config repository against a checkpoint tag/ref and cannot be combined with --node. caco config history --limit N lists recent config snapshot commits plus config/... tags, caco config checkpoint --reason "..." [--tag TAG] commits and tags the current local config VCS state, and caco config restore TAG --yes restores tracked local config files from a checkpoint and commits the restore. Restore is intentionally confirmation-gated; after restoring, inspect caco config diff and restart/distribute only if that local config state is the intended runtime configuration.
Execution Modes
Mode rule when expressions support boolean combinators (all, any, not), comparisons, and named predicates. Temporal counter predicates add rolling-window checks for automation policies: rate_exceeds: [counter, threshold] compares the counter rate over the default five-minute window in units per minute, trending_up: counter and trending_down: counter classify the rolling trend over the default fifteen-minute window, and stable: counter requires samples to remain within the default tolerance over the five-minute stability window. Validation rejects unknown counter names or malformed temporal operands, and the daemon records counter samples before evaluating each mode cycle so these predicates react to trends rather than just the instantaneous snapshot. The daemon now also feeds resource, cluster, validation-queue, failure/retry, certificate, and UTC time signals into the mode state: examples include cpu_available_parallelism, system_load1_milli, known_peers, reachable_peers, cluster_degraded, is_beads_primary, test_queue_queued, build_queue_running, validation_queue_busy, agent_recovery_active, certificate_expiry_warnings, utc_hour, utc_weekday, is_weekday, and is_business_hours.
Config Generation
caco config generate
Emits a valid YAML config to stdout. The default generated config uses the resolved node name as node.default with a single-node setup including valid listener ports.
Credential Management
config.yaml, flake.nix, or committed profile files. Use secret file paths suitable for secret managers like sops-nix.
The daemon reads secret files at startup, trims trailing newlines, and exports the resolved tokens as environment variables for child processes. Secret contents are never persisted to config hashes, feed events, logs, or frontend APIs.
Voice Filters
Post-TTS voice filters apply sox audio effects to the synthesized speech output before playback, giving each agent or session a distinctive sound character.
Filter Modes
Only one mode should be active. Priority: rotation > preset > custom.
Preset
speech:
tts:
voice_filter:
preset: telephone
| Preset | Effect |
|---|---|
telephone | Bandpass 300–3400 Hz, slight distortion, compression |
hall | Large reverb, slight echo, wet mix ~40% |
lofi | Bitcrush, mild saturation, narrow bandpass |
underwater | Heavy low-pass, slow flanger, reverb |
radio | Bandpass 500–4000 Hz, compression, slight overdrive |
whisper | Soft compression, high-pass, reverb tail |
cave | Long reverb, low-pass, echo |
megaphone | Hard compression, bandpass, overdrive |
robot | Ring modulation, flanger, slight reverb |
vintage | Vinyl crackle, narrow EQ, tape saturation |
Custom
speech:
tts:
voice_filter:
custom: "reverb 50 highpass 300"
Raw sox effect chain string, passed directly to the sox processor.
Rotation
speech:
tts:
voice_filter:
rotation:
presets:
- telephone
- hall
- lofi
- radio
- cave
combine_with_voice: true
Assigns filters from the pool to agents in rotation. When combine_with_voice is true (default), filter rotation is paired with voice rotation to create unique (voice, filter) identity pairs per agent.
Per-Target STT Overrides
STT behavior can be customized per transcription target type. Each target type supports the following overrides:
| Field | Default | Description |
|---|---|---|
rewriting | false | Enable smart rewriting of transcribed text |
smart_append | false | Enable per-chunk cleanup when appending |
enter_after_completed | false | Send Enter keypress after transcription completes (agent/terminal targets only) |
Target Types
| Target | Description |
|---|---|
agent | Agent tmux panes |
terminal | Terminal/shell panes |
scratchpad | Scratchpad notes |
text_field | Text fields and chat compose areas |
bead | Bead create form fields |
speech:
stt:
agent:
rewriting: false
enter_after_completed: false
scratchpad:
rewriting: true
smart_append: true
text_field:
rewriting: true
TUI Theme Registry
tui:
theme_name: high
themes:
enterprise-blueprint:
imports:
- themes/enterprise-blueprint.yaml
colors:
foreground_color: "#eceff4"
background_color: "#0f1318"
color0: "#2e3440"
# ... color1 through color15
graphics:
# optional GraphicsConfig overrides
nodes:
- name: ms-mac
tui:
theme_name: high
graphics:
footer:
background_color: "#5e81ac"
The current theme contract is an explicit registry of named themes. Prefer explicit palette slots (foreground_color, background_color, and color0–color15) plus optional graphics overrides over older preset-only colorscheme snippets. Node-local tui.theme_name values select from the same registry without duplicating theme definitions; node-local tui.graphics.footer.background_color can tint the status/footer chrome for node identity. Use nord-avatars for avatar-image backgrounds, while the default quality tiers keep avatar image lists empty.
Graphics Configuration
Background caches
tui.graphics.imageSourceCache defaults to true and reuses decoded/resized image intermediates beneath the PNG cache. Image background render keys and decoded/resized source caches include the file length and modification-time identity in addition to the path, so replacing an asset in place invalidates stale cached bitmaps without disabling the cache. Procedural backgrounds also default tui.graphics.scaledBackgroundCache to true, with tui.graphics.scaledBackgroundCacheMaxPx (default 1280), to render solid, gradient, glow, and scanline backgrounds once per high-resolution aspect bucket and scale them into nearby panel sizes; image backgrounds keep exact sizing for crop/anchor correctness. Set scaledBackgroundCache: false only for rollback or exact-raster diagnostics.
Sparklines
The TUI can render telemetry sparklines as kitty graphics bitmaps instead of Unicode block characters. Configured under tui.graphics.sparkline:
tui:
graphics:
sparkline:
style: gradient_fill_line # bar | line | gradient_fill_line
opacity: 0.8 # Line/fill opacity (0.0–1.0)
fadeOpacity: 0.0 # Bottom gradient opacity (gradient_fill_line only)
levels: 10 # Quantisation levels for segment atlas
| Style | Description |
|---|---|
bar | Filled bars per cell, height proportional to data value |
line | Line graph connecting consecutive values |
gradient_fill_line | Line graph with gradient fill fading from line to bottom (default) |
Requires kitty graphics capability. Falls back to standard ratatui Sparkline widget when graphics are unavailable. The segment atlas is pre-computed on first render and re-uploaded on theme change.
Button Chrome
Reusable button widgets support configurable chrome via tui.graphics.button (standard GraphicsStyleLayers fields) and padding via tui.graphics.buttonPadding:
tui:
graphics:
buttonPadding: 1 # Uniform padding (all sides)
# Or per-side:
buttonPadding:
top: 0
left: 1
bottom: 0
right: 1
Span Glow Bleed
Controls how far glow effects extend beyond text cell boundaries:
| Field | Default | Description |
|---|---|---|
spanGlowBleedX | 2 | Extra columns on each side |
spanGlowBleedY | 1 | Extra rows on each side |
Minimum Provider Environment Variables
| Provider | Base URL | Secret |
|---|---|---|
| OpenAI-compatible | OPENAI_BASE_URL | OPENAI_API_KEY |
| Anthropic-compatible | ANTHROPIC_BASE_URL | ANTHROPIC_AUTH_TOKEN |