Docs/Resonant/Configuration

Configuration

On this page

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 beneath CLAUDE.md instead 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

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

IntegrationWhat it needs
Claude (Anthropic) — requiredClaude Code login, or ANTHROPIC_API_KEY
ElevenLabs — TTSvoice.enabled: true + ELEVENLABS_API_KEY + ELEVENLABS_VOICE_ID
Groq — STT (Whisper)voice.enabled: true + GROQ_API_KEY
Hume — optional prosodyvoice.enabled: true + HUME_API_KEY
Google WorkspaceDesktop OAuth client, GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET, connect via Settings
Discorddiscord.enabled: true + owner_user_id in yaml, DISCORD_BOT_TOKEN in .env/Settings
Telegramtelegram.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 memoryintegrations.mind_cloud.enabled + mcp_url; key via Settings/DB
Command Centercommand_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" plus url, and optional headers
  • Local-launched: command plus args and env

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_PASSWORD refuses to serve at all.
  • The write-gate — the agent can always write inside its own agent.cwd; workspace_root, vault_path, and extra_write_paths grant it access anywhere else.
  • Secrets stay localresonant.yaml and .env are gitignored; nothing in them ships with the repo or leaves the machine.
  • Same-origin by defaultcors.origins is empty unless you set it.