# Flynn Configuration # Copy to ~/.config/flynn/config.yaml and customize # Log verbosity: debug | info | warn | error | silent (default: info) # Set to 'debug' to see model fallback details. # log_level: info telegram: bot_token: ${FLYNN_TELEGRAM_TOKEN} allowed_chat_ids: [] # Add your Telegram chat ID # require_mention: false # Default false: respond to all allowed group messages # Optional: Signal via signal-cli # signal: # account: "+15551234567" # signal_cli_path: signal-cli # allowed_numbers: [] # Empty = allow all DMs # allowed_group_ids: [] # Empty = no groups # require_mention: true # mention_name: flynn # poll_interval_ms: 5000 # send_timeout_ms: 15000 # Optional: Mattermost # mattermost: # server_url: ${MATTERMOST_SERVER_URL} # bot_token: ${MATTERMOST_BOT_TOKEN} # allowed_channel_ids: [] # Empty = allow all channels (pairing/mention rules still apply) # require_mention: true # mention_name: flynn # poll_interval_ms: 3000 # Optional: Microsoft Teams (Bot Framework) # teams: # app_id: ${TEAMS_APP_ID} # app_password: ${TEAMS_APP_PASSWORD} # allowed_conversation_ids: [] # Empty = allow all conversations # require_mention: true # Optional: Google Chat # google_chat: # service_account_key_file: ~/.config/flynn/google-chat-service-account.json # # or inline via env var expansion: # # service_account_json: ${GOOGLE_CHAT_SERVICE_ACCOUNT_JSON} # webhook_token: ${GOOGLE_CHAT_WEBHOOK_TOKEN} # allowed_space_names: [] # Empty = allow all spaces # require_mention: true # Optional: iMessage via BlueBubbles # bluebubbles: # endpoint: http://localhost:1234 # api_key: ${BLUEBUBBLES_API_KEY} # webhook_token: ${BLUEBUBBLES_WEBHOOK_TOKEN} # allowed_chat_guids: [] # Empty = allow all chats # require_mention: true # mention_name: flynn # Optional: LINE # line: # channel_access_token: ${LINE_CHANNEL_ACCESS_TOKEN} # channel_secret: ${LINE_CHANNEL_SECRET} # allowed_source_ids: [] # Empty = allow all users/groups/rooms # require_mention: true # mention_name: flynn # Optional: Feishu / Lark # feishu: # app_id: ${FEISHU_APP_ID} # app_secret: ${FEISHU_APP_SECRET} # webhook_token: ${FEISHU_WEBHOOK_TOKEN} # allowed_chat_ids: [] # Empty = allow all chats # require_mention: true # mention_name: flynn # endpoint: https://open.feishu.cn # Optional: Zalo # zalo: # oa_access_token: ${ZALO_OA_ACCESS_TOKEN} # webhook_token: ${ZALO_WEBHOOK_TOKEN} # allowed_user_ids: [] # Empty = allow all users # require_mention: true # mention_name: flynn # endpoint: https://openapi.zalo.me server: # Tailscale Serve config (optional). Enable `serve: true` to expose the # gateway to your tailnet via `tailscale serve`. tailscale: serve: false localhost: false port: 18800 # Maximum inbound HTTP request body size (bytes) for webhooks/Gmail push. max_request_body_bytes: 1048576 ws_rate_limit: enabled: true capacity: 30 refill_per_sec: 15 max_violations: 8 violation_window_ms: 10000 # Per-session FIFO lane queue for gateway requests. queue: mode: collect # collect | followup | steer | steer_backlog | interrupt cap: 50 # max queued (pending) requests per session lane overflow: drop_old # drop_old | drop_new debounce_ms: 0 # delay before starting next queued request summarize_overflow: true overrides: channels: {} # e.g. ws: { mode: followup, cap: 10, debounce_ms: 100 } sessions: {} # e.g. ws:vip-user: { mode: interrupt, overflow: drop_new } # Companion-node capability negotiation surface (default disabled). nodes: enabled: false allowed_roles: [companion] feature_gates: {} location: enabled: false push: enabled: false # Optional WebChat push subscription endpoints for PWA notifications. # Set `enabled: true` and provide a VAPID public key for PushManager. webchat_push: enabled: false # vapid_public_key: ${WEBCHAT_VAPID_PUBLIC_KEY} max_subscriptions: 5000 # Local-network service discovery (mDNS/Bonjour). Keep disabled by default. # Requires server.localhost: false so LAN clients can actually connect. discovery: enabled: false service_name: flynn-gateway service_type: _flynn._tcp txt: {} models: # ── Model tiers ──────────────────────────────────────────────────── # Each tier (default, fast, complex, local) defines a primary model. # When an Anthropic tier fails, Flynn automatically tries the same # model via GitHub Models before falling through to the global chain. # # You can override auto-fallback with an inline `fallback:` block: # # default: # provider: anthropic # model: claude-sonnet-4-20250514 # fallback: # ← inline per-tier fallback # provider: openai # model: gpt-4o # # ── Fallback order ───────────────────────────────────────────────── # 1. Primary client for the tier # 2. Auto same-model fallback via GitHub Models (Anthropic tiers only, # skipped when an inline `fallback:` block is present) # 3. Inline `fallback:` client (if configured) # 4. Global fallback_chain (tried in order) # default: provider: openai model: gpt-5.2 auth_mode: api_key # auto | api_key | oauth (provider-specific) api_key: ${OPENAI_API_KEY} # use_oauth: false # compat alias for auth_mode: oauth # api_keys: ["${ANTHROPIC_API_KEY_PRIMARY}", "${ANTHROPIC_API_KEY_SECONDARY}"] # Optional rotation pool # supports_audio: false # Override native audio detection per tier fast: provider: anthropic model: claude-haiku-4-5-20251001 complex: provider: anthropic model: claude-opus-4-6-20250715 local: provider: ollama model: glm-4.7-flash # ── Global fallback chain ────────────────────────────────────────── # Entries can be tier names (default, fast, complex, local) or keys # from local_providers below. Tried in order after per-tier fallbacks # are exhausted. fallback_chain: [local] # ── Named providers (optional) ───────────────────────────────────── # Define additional providers that can be referenced by name in # fallback_chain. Useful for secondary API accounts or self-hosted # endpoints that aren't tied to a specific tier. # # Use /backend in the TUI to switch between these providers local_providers: ollama: provider: ollama model: glm-4.7-flash endpoint: http://127.0.0.1:11434 llamacpp: provider: llamacpp model: gpt-oss-20b endpoint: http://127.0.0.1:8080 # # Then reference them in fallback_chain: # fallback_chain: [ollama, llamacpp, local] # Optional: external CLI backends for non-tool conversation turns. # - native remains available unless you disable it. # - set `default` to route normal text turns to a backend by default. # - per-agent override is available via `agent_configs..backend`. # backends: # default: codex # claude_code | opencode | codex | gemini | pi_embedded # native: # enabled: true # claude_code: # enabled: false # # path: /usr/local/bin/claude # # args: ["--print", "{prompt}"] # # timeout_ms: 120000 # opencode: # enabled: false # # path: /usr/local/bin/opencode # # args: ["run", "--format", "default", "{prompt}"] # # timeout_ms: 120000 # codex: # enabled: false # # path: /usr/local/bin/codex # # args: ["-p", "{prompt}"] # # timeout_ms: 120000 # gemini: # enabled: false # # path: /usr/local/bin/gemini # # args: ["-p", "{prompt}"] # # timeout_ms: 120000 # pi_embedded: # enabled: false # # timeout_ms: 120000 # # no_tools_mode: false # when true, force native for tool-like prompts (default: false) # # model: openclaw-default # optional model/session selector passed to Pi runtime # # system_prompt_mode: hybrid # flynn | pi_default | hybrid # # module: "@mariozechner/pi-agent-core" # optional module override # Optional: Kubernetes / homelab awareness tools (k8s.pods, k8s.deployments, k8s.logs) # k8s: # enabled: false # kubectl_path: kubectl # default_namespace: default # allowed_namespaces: [] # Empty = allow any namespace; set to restrict access. # Optional: Browser automation tools # (browser.navigate/screenshot/click/type/content/wait_for/assert/extract/checkpoint.save/checkpoint.resume/eval/evaluate) # Requires a local Chrome/Chromium install or a remote CDP endpoint. # browser: # enabled: true # # executable_path: /usr/bin/google-chrome # # ws_endpoint: ws://127.0.0.1:9222/devtools/browser/ # headless: true # max_pages: 5 # default_timeout: 30000 # # Guardrails: # # allowed_domains: ["*.example.com"] # # high_risk_domains: ["bank.example.com"] # # require_confirmation_for_high_risk: true # # max_workflow_steps: 120 # # default_retry_attempts: 1 # # max_retry_attempts: 5 # # retry_delay_ms: 250 # # Tool policy reminder: # - `tools.profile: coding` or `tools.profile: full` must allow browser.* tools. # - `tools.deny` patterns can still block browser.* even when browser.enabled is true. # ── Web Search ─────────────────────────────────────────────────────── # Configure web.search tools. # - provider: brave -> enables web.search + web.search.news (requires api_key) # - provider: searxng -> enables web.search (requires endpoint) # # web_search: # provider: brave # api_key: "${BRAVE_API_KEY}" # max_results: 5 # # endpoint: "http://searxng:8080" # Used when provider=searxng (or http://localhost:18803 if Flynn runs on host) # # fallback_endpoint: "https://searxng.homelab.local" # Optional backup endpoint for provider=searxng # # Optional local Brave Search MCP container (for MCP workflows only): # docker compose --profile search up -d brave-search # Optional local SearXNG container: # docker compose --profile search up -d searxng # Built-in web.search* tools call Brave/SearXNG HTTP endpoints directly. hooks: confirm: - shell.* - process.start - process.kill - browser.* - message.send - cron.create - cron.delete - file.write - file.patch log: - web.* - file.read silent: - notify # ── Safety Notes ───────────────────────────────────────────────────── # - Tool policy (tools.profile/allow/deny) controls which tools are available. # - Skills can declare capability permissions in skills//manifest.json under `permissions`. # Those permissions are enforced at runtime when requests are routed into a skill context. # - See: docs/security/SAFE_PERSONAL_AGENT.md agents: # In full-access mode, sensitive operations are gated by HookEngine confirmation # (instead of requiring temporary /elevate windows). sensitive_mode: confirm_without_elevation # Multi-turn subagent sessions (`subagent.*` tools). subagents: enabled: true max_active_sessions: 6 idle_ttl_ms: 3600000 queue_mode: followup default_tool_profile: minimal max_turns: 40 max_total_tokens: 200000 turn_timeout_ms: 120000 # ── Memory / Embeddings ────────────────────────────────────────────── # Enable hybrid keyword + vector search using local Ollama embeddings. memory: enabled: true auto_extract: true # user_namespace: "user" # working_memory_ttl_days: 14 # working_memory_max_tokens: 1000 # proactive_session_greeting: false embedding: enabled: true provider: ollama model: nomic-embed-text endpoint: http://127.0.0.1:11434 chunk_size: 512 chunk_overlap: 50 top_k: 5 hybrid_weight: 0.7 # ── Prompt Assembly ─────────────────────────────────────────────────── # Tune how much context Flynn loads into the system prompt. # # prompt: # search_dirs: [] # extra_sections: [] # context_level: normal # minimal | normal | detailed | debug # Optional: named agent profiles and routing. # Each agent can pin model tier, tool profile, sandbox mode, and execution backend. # # agent_configs: # assistant: # system_prompt: You are helpful. # model_tier: default # backend: native # native | codex | claude_code | opencode | gemini | pi_embedded # coder: # system_prompt: Write code. # model_tier: complex # backend: codex # sandbox: true # # routing: # default_agent: assistant # channels: # telegram: assistant # senders: # telegram:admin: coder # ── Context Compaction ──────────────────────────────────────────────── # Optional proactive context monitoring and checkpointing. # # compaction: # enabled: true # threshold_pct: 80 # keep_turns: 4 # summary_max_tokens: 1024 # importance_threshold: 1 # proactive: # enabled: false # warn_pct: 75 # checkpoint_pct: 85 # auto_compact_pct: 95 # checkpoint_cooldown_ms: 300000 # memory_namespace: session/checkpoints # skills: # # Registry catalog source for discovery and install-by-id: # # local path or HTTPS URL. Can also be set via FLYNN_SKILLS_REGISTRY_SOURCE. # registry_source: ~/.config/flynn/skills-registry.json # # Global installer execution policy. # # disabled: never run installer commands (default) # # enabled: allow command execution only with --execute --confirm # installation_execution: disabled # # Allow shell-based installer runner when --runner shell is requested. # allow_shell_runner: false # # Allowlist command patterns for shell runner (`*` wildcard supported). # # Empty list means no shell commands are allowed. # shell_runner_allowlist: [] # # Governance metadata for shell-runner allowlist and rollout decisions. # shell_runner_governance: # owner: "skills-team" # Required when allow_shell_runner is true # review_cadence_days: 7 # Review `skills rollout-status` at this cadence # promotion_min_success_rate: 0.9 # Rollout threshold for broader enablement # ── Automation ────────────────────────────────────────────────────── # Uncomment and configure any automation sources you need. # automation: # # shared_session: keep one session per cron job/webhook name. # # isolated_job: create a fresh session per cron trigger/webhook request. # # announce: create a fresh announce-style run per trigger (no shared automation history). # delivery_mode: shared_session # reactions: # - name: boss-email # on: [gmail] # filter: # contains: "boss@company.com" # run: | # Summarize this email and propose next actions. # # {{text}} # # - name: github-main-push # on: [webhook] # filter: # metadata: # webhookName: github-push # body.ref: refs/heads/main # run: | # Summarize this deploy-relevant push: # {{metadata.body.head_commit.message}} # cron: # - name: daily-summary # schedule: "0 9 * * *" # message: "Give me a summary of today's tasks" # output: # channel: telegram # peer: "123456789" # once_per_local_day: false # # # Optional built-in morning briefing job (auto-registered as a cron job) # daily_briefing: # enabled: false # name: daily-briefing # schedule: "0 8 * * *" # timezone: America/New_York # dedupe_per_local_day: true # output: # channel: telegram # peer: "123456789" # model_tier: fast # prompt: | # Create my daily briefing. # Summarize today's calendar, unread/important email, and top pending tasks. # # # Optional scheduled MinIO -> memory synchronization # minio_sync: # enabled: false # interval: "6h" # run_on_start: false # notify_on_success: false # notify: # channel: telegram # peer: "123456789" # tasks: # - prefix: "knowledge/" # namespace_base: "global/knowledge/minio" # mode: append # max_objects: 20 # max_chars_per_object: 8000 # force: false # # webhooks: # - name: github-push # secret: "whsec_..." # message: "GitHub push to {{json.repository.full_name}}: {{json.head_commit.message}}" # output: # channel: telegram # peer: "123456789" # # gmail: # enabled: false # credentials_file: ~/.config/flynn/gmail-credentials.json # token_file: ~/.config/flynn/gmail-token.json # # Authenticate with: flynn gmail-auth # # (requests gmail.settings.basic + gmail.readonly for filter create + inbox tools) # # # Optional Pub/Sub delivery # # Push mode: configure a topic and a push subscription that POSTs to /gmail/push # pubsub_topic: projects/your-project/topics/gmail-push # disable_push: false # # # Pull mode: no inbound webhook required (requires Application Default Credentials) # pubsub_subscription_id: projects/your-project/subscriptions/gmail-pull # pubsub_pull_interval: "60s" # pubsub_max_messages: 10 # watch_labels: [INBOX] # poll_interval: "60s" # message: "New email from {{from}}: {{subject}}\n\n{{snippet}}" # output: # channel: telegram # peer: "123456789" # # heartbeat: # enabled: false # interval: "5m" # notify_cooldown: "30m" # checks: [gateway, model, channels, memory, disk, process_memory, backup, provider_errors] # notify: # channel: telegram # peer: "123456789" # failure_threshold: 2 # disk_threshold_mb: 100 # process_memory_threshold_mb: 1500 # backup_failure_threshold: 1 # provider_error_rate_threshold: 0.5 # provider_error_min_calls: 5 automation: heartbeat: enabled: true interval: "5m" notify_cooldown: "30m" checks: [gateway, model, channels, memory, disk, process_memory, backup, provider_errors] failure_threshold: 2 disk_threshold_mb: 100 process_memory_threshold_mb: 1500 backup_failure_threshold: 1 provider_error_rate_threshold: 0.5 provider_error_min_calls: 5 # ── Backup ────────────────────────────────────────────────────────── # Snapshot sessions.db, vectors.db (optional), and memory/ into a tarball. # If MinIO is enabled, upload with `mc` using ephemeral credentials. # LINE/Zalo adapters also reuse backup.minio credentials for binary-attachment # URL sharing when those channels are configured. # # backup: # enabled: false # # Optional cron schedule (takes precedence over interval), e.g. nightly at 2 AM. # schedule: "0 2 * * *" # interval: "24h" # run_on_start: false # notify: # channel: telegram # peer: "123456789" # failure_threshold: 1 # notify_recovery: true # local_dir: ~/.local/share/flynn/backups # include_vectors: true # minio: # enabled: false # endpoint: localhost:9000 # access_key: ${MINIO_ACCESS_KEY} # secret_key: ${MINIO_SECRET_KEY} # bucket: flynn-backups # prefix: flynn # secure: true # ── Session Lifecycle ─────────────────────────────────────────────── # sessions: # ttl: "30d" # end_summary: # enabled: false # tier: fast # max_messages: 50 # max_input_chars: 20000 # max_tokens: 512 # write_to_memory: true # memory_namespace: session/summaries # ── Audio ──────────────────────────────────────────────────────────── # Configure a Whisper-compatible endpoint for audio transcription. # Models that support native audio input (Gemini, OpenAI, GitHub) will # receive raw audio directly; others fall back to this endpoint. # # For local transcription with whisper.cpp: # docker run -d --name whisper-server -p 18801:8080 \ # ghcr.io/ggml-org/whisper.cpp:main \ # --model /app/models/ggml-base.en.bin \ # --host 0.0.0.0 --port 8080 --convert --language en \ # --inference-path /v1/audio/transcriptions # # audio: # enabled: true # provider: # type: custom # openai, groq, ollama, llamacpp, custom # endpoint: "http://127.0.0.1:18801/v1/audio/transcriptions" # api_key: "${WHISPER_API_KEY}" # Optional Bearer token # model: "whisper-1" # Model name (default: whisper-1) # talk_mode: # enabled: false # wake_phrase: "hey flynn" # timeout_ms: 120000 # allow_manual_toggle: true # ── Text-to-Speech (TTS) Output ────────────────────────────────────── # Optional voice output for assistant replies. Uses an OpenAI-compatible # /v1/audio/speech endpoint and attaches audio to channel replies. # # tts: # enabled: false # enabled_channels: [telegram, whatsapp, discord] # Empty = all channels # provider: # type: openai # openai | custom # endpoint: "https://api.openai.com/v1/audio/speech" # api_key: "${OPENAI_API_KEY}" # Optional Bearer token # model: "gpt-4o-mini-tts" # voice: "alloy" # format: "mp3" # mp3 | wav | opus # ── Sub-Agent Configs ──────────────────────────────────────────────── # Named agent configurations for delegation via agent.delegate tool. # Each agent gets a focused system prompt, model tier, and tool profile. # # agent_configs: # research: # model_tier: default # tool_profile: messaging # system_prompt: | # You are a research agent. Your job is to find, verify, and synthesize # information from the web. Be thorough but concise. Cite sources when # possible. Return structured findings — not conversational filler. # Use web.search to find sources and web.fetch to read them. # # code: # model_tier: complex # tool_profile: coding # system_prompt: | # You are a code agent. Your job is to read, write, debug, refactor, # and review code. You have full access to the filesystem and shell. # Be precise. Read files before modifying them. Run tests after changes. # Use file.edit/file.patch for surgical edits, not full file rewrites. # Commit conventions: conventional commits, small atomic changes. # # comms: # model_tier: fast # tool_profile: messaging # system_prompt: | # You are a communications agent. Your job is to draft messages, # summarize emails, triage inbox items, and prepare quick replies. # Be concise and match the operator's tone. Skip marketing emails. # Never send messages without explicit instruction — draft only. # ── Councils Pipeline ──────────────────────────────────────────────── # Deterministic dual-council orchestration (D/P groups) with bridge-only # exchange and a deterministic meta merge. Requires matching agent_configs # for the configured role names below. # # councils: # enabled: false # defaults: # max_rounds: 2 # ideas_per_round: 6 # top_ideas_for_bridge: 3 # bridge_packet_max_chars: 2500 # bridge_field_max_bullets: 6 # bridge_entry_max_chars: 300 # novelty_delta_threshold: 10 # repetition_threshold: 70 # strict_grounding: false # strict_meta_validation: true # groups: # D: # arbiter_agent: council_d_arbiter # freethinker_agent: council_d_freethinker # model_tier: complex # group_prompt_prefix: Optimize for feasibility and speed-to-test. Prefer boring-but-true. # novelty_bias: low # risk_tolerance: low # forbidden_approaches: # - moonshots # - handwavy AI claims # - unverified assumptions # P: # arbiter_agent: council_p_arbiter # freethinker_agent: council_p_freethinker # model_tier: complex # group_prompt_prefix: Optimize for reframing and non-obvious leverage. Weird is fine; label speculation. # novelty_bias: high # risk_tolerance: high # forbidden_approaches: # - incremental tweaks # - obvious best practices # - purely conventional solutions # meta_arbiter_agent: council_meta_arbiter # meta_model_tier: complex # # scaffold_path: docs/councils/ai-council-production-scaffold.json # Optional: explicit intent rules for agent routing. # If enabled, these rules are evaluated before default sender/channel routing. # The research agent can already be auto-routed by prefix (`research ...`, `look up ...`) # when agent_configs.research exists and intents are disabled. # # intents: # enabled: true # match_threshold: 0.7 # rules: # - name: route-research # patterns: ["research *", "look up *", "lookup *"] # target: # type: agent # name: research # priority: 10 # enabled: true