Configuration
On this page
- The Two Files, and Who Wins
- CLAUDE.md vs system.md
- Agent Home (agent.cwd)
- Full resonant.yaml Reference
- identity
- server
- auth
- agent
- orchestrator
- handoff
- hooks (the write-gate)
- voice
- discord / telegram
- integrations
- command_center
- cors
- Full .env Reference
- Integration Enable Map
- Theming
- .mcp.json — External MCP Tools
- Security, As It Actually Exists
Configuration lives in two files: resonant.yaml (everyday settings) and .env (secrets and machine overrides). Both are copies of the templates in the repo, and both are gitignored.
The Two Files, and Who Wins
Precedence, weakest to strongest: built-in default → resonant.yaml → .env. Environment variables always win.
The loader searches, in order: ./resonant.yaml, then ./resonant.yml, then ./config/resonant.yaml. Paths inside resonant.yaml resolve from the project root, not your shell’s working directory.
CLAUDE.md vs system.md
Two levers for identity and framing — see Identity for the full picture:
CLAUDE.md(agent.claude_md_path, default./CLAUDE.md) — who your AI is. Hot-reloaded every message, no restart.system.md(agent.system_prompt_file, default empty) — a thin, optional operating-frame layer above the identity. Nothing exists at this path until you create one. When set, it replaces the standard Claude Code agent foundation beneathCLAUDE.mdinstead of stacking on top of it.
Agent Home (agent.cwd)
Defaults to . (the app’s own folder), but the recommended pattern is an absolute path outside the repo — a sibling folder:
/home/you/resonant/ ← the app
/home/you/companion/ ← agent.cwd points here
.claude/skills/ native skills, scanned from <agent.cwd>/.claude/skills/*/SKILL.md
.claude/commands/ custom slash commands, from <agent.cwd>/.claude/commands/*.md
The code’s own framing: “the organs live in the app’s repo (the body); the soul lives in the agent’s home.” This is also where shared/ lives (anything written there auto-shares into the conversation) and the default root for the write-gate.
There is no top-level skills/ folder at the repo root, no quarantine mechanism, and no shipped default skills — skills only exist once you or your AI create them under <agent.cwd>/.claude/skills/.
Full resonant.yaml Reference
identity
identity:
companion_name: "Echo"
user_name: "Alex"
timezone: "Europe/London"
profile_path: ./identity/companion.profile.yaml
companion_md_path: ./identity/companion.md
resonant.example.yaml ships user_name: "Alex" and timezone: "Europe/London" as illustrative starting values. Note this is different from the code’s own built-in default (before any yaml is applied), which is user_name: "User" and timezone: "UTC" — two real, separate layers, not a contradiction.
server
server:
port: 3099 # env PORT
host: 127.0.0.1 # env HOST
db_path: ./data/resonant.db # env DB_PATH
auth
auth:
password: "" # env APP_PASSWORD — fail-closed; empty = 503 "Auth not configured"
AUTH_DEV_OPEN=true is a dev-only escape, only meaningful with no password set — never use it on a network-reachable instance.
agent
agent:
cwd: .
claude_md_path: ./CLAUDE.md
system_prompt_file: ""
mcp_json_path: ./.mcp.json
model: claude-sonnet-4-6 # env AGENT_MODEL — interactive messages
model_autonomous: claude-sonnet-4-6 # background wakes + built-in helper subagents
orchestrator
orchestrator:
enabled: true # master switch for all autonomous activity
wake_prompts_path: ./prompts/wake.md # legacy, auto-migrated
wake_prompts_dir: ./prompts/wakes # real source
schedules: {} # map of wakeType to cron
failsafe:
enabled: false
gentle_minutes: 120
concerned_minutes: 720
emergency_minutes: 1440
Full detail on routines, timers, triggers, and failsafe: Background Agents.
handoff
handoff:
enabled: false # midnight (12:10am local) continuity carry — Settings toggle overrides live
hooks (the write-gate)
hooks:
context_injection: true
safe_write_prefixes: [] # legacy
workspace_root: "" # env WORKSPACE_ROOT
vault_path: "" # env VAULT_PATH
extra_write_paths: [] # env EXTRA_WRITE_PATHS, comma-separated
The agent can always write inside its own agent.cwd home; these grant additional access.
voice
voice:
enabled: false
elevenlabs_voice_id: "" # env ELEVENLABS_VOICE_ID
That’s the whole shape — only these two keys live in resonant.yaml. There are no nested stt:/tts:/prosody: blocks and no apiKey fields here; provider secrets (ELEVENLABS_API_KEY, GROQ_API_KEY, HUME_API_KEY) live only in .env.
discord / telegram
discord:
enabled: false
owner_user_id: ""
telegram:
enabled: false
owner_chat_id: ""
These are top-level keys, not nested under a channels: wrapper. Bot tokens (DISCORD_BOT_TOKEN, TELEGRAM_BOT_TOKEN) live in .env only — there’s no token: field in the yaml for either. See Channels.
integrations
integrations:
life_api_url: ""
mind_cloud:
enabled: false
mcp_url: "" # API key set via Settings UI, stored in the database
google:
enabled: false # legacy master gate — Workspace mount is always present and self-gates per app now
client_id: "" # env GOOGLE_CLIENT_ID
client_secret: "" # env GOOGLE_CLIENT_SECRET
command_center
command_center:
enabled: false
default_person: "user"
currency_symbol: "$"
care_categories:
toggles: [breakfast, lunch, dinner, snacks, medication, movement, shower]
ratings: [sleep, energy, wellbeing, mood]
counters:
- { name: water, max: 10 }
cors
cors:
origins: [] # same-origin only unless set
Full .env Reference
APP_PASSWORD= AUTH_DEV_OPEN=
ANTHROPIC_API_KEY= CLAUDE_CODE_OAUTH_TOKEN= CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST=
COMPANION_NAME= USER_NAME= TZ=
AGENT_CWD= AGENT_MODEL= RESONANT_NAMESPACE=
PORT= HOST= DB_PATH= RESONANT_PORT=
WORKSPACE_ROOT= VAULT_PATH= EXTRA_WRITE_PATHS=
INTERNAL_TOKEN=
ELEVENLABS_API_KEY= ELEVENLABS_VOICE_ID= GROQ_API_KEY= HUME_API_KEY=
DISCORD_BOT_TOKEN= TELEGRAM_BOT_TOKEN= GIPHY_API_KEY=
GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= GOOGLE_TOKEN_SECRET=
VAPID_PUBLIC_KEY= VAPID_PRIVATE_KEY= VAPID_CONTACT=
NODE_ENV= BUILD_ID= MEASURE_THINKING=
The Mind service’s API key is deliberately not an env var — it’s set once, via Settings UI, and stored in the database.
Integration Enable Map
| Integration | What it needs |
|---|---|
| Claude (Anthropic) — required | Claude Code login, or ANTHROPIC_API_KEY |
| ElevenLabs — TTS | voice.enabled: true + ELEVENLABS_API_KEY + ELEVENLABS_VOICE_ID |
| Groq — STT (Whisper) | voice.enabled: true + GROQ_API_KEY |
| Hume — optional prosody | voice.enabled: true + HUME_API_KEY |
| Google Workspace | Desktop OAuth client, GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET, connect via Settings |
| Discord | discord.enabled: true + owner_user_id in yaml, DISCORD_BOT_TOKEN in .env/Settings |
| Telegram | telegram.enabled: true + owner_chat_id, TELEGRAM_BOT_TOKEN, optional GIPHY_API_KEY |
| Web push (VAPID) | npx web-push to generate a keypair, then the three VAPID vars |
| Mind memory | integrations.mind_cloud.enabled + mcp_url; key via Settings/DB |
| Command Center | command_center.enabled: true |
Theming
Settings → Appearance edits design tokens live — no rebuild, no restart. Changes are saved to the database and drawn from a curated allowlist (300-character cap per value):
- Backgrounds:
--bg-primary,--bg-secondary,--bg-input - Text:
--text-primary,--text-secondary,--text-muted - Border:
--border - Accents:
--amber,--amber-bright,--lavender,--lavender-bright,--gold,--status-active - Fonts:
--font-serif,--font-body,--font-mono
Ready-made themes ship in examples/themes/: gold-hud.css, warm-earth.css.
.mcp.json — External MCP Tools
Starts as { "mcpServers": {} }. Two shapes:
- Remote:
"type": "http"plusurl, and optionalheaders - Local-launched:
commandplusargsandenv
The one real gotcha: it must be "type": "http" exactly — a bare url with no type, or "type": "url", is silently accepted and converted, but "http" is authoritative. SSE servers use "type": "sse". Resonant reads .mcp.json only and ignores any global ~/.claude MCP config. A restart is required after editing.
There’s no resonant.config.yaml anywhere in the repo — only resonant.yaml / resonant.example.yaml. There’s also no bin field in package.json, so there’s no globally-invokable resonant CLI and no resonant security subcommand.
Security, As It Actually Exists
- Fail-closed auth — an empty
APP_PASSWORDrefuses to serve at all. - The write-gate — the agent can always write inside its own
agent.cwd;workspace_root,vault_path, andextra_write_pathsgrant it access anywhere else. - Secrets stay local —
resonant.yamland.envare gitignored; nothing in them ships with the repo or leaves the machine. - Same-origin by default —
cors.originsis empty unless you set it.