Docs/Using Flowly/Configuration
Using Flowly

Configuration

The canonical configuration reference for Flowly. All settings live in a single JSON file with camelCase keys; most users never edit it by hand, but every key is documented here.

File path

<FLOWLY_HOME>/config.json

FLOWLY_HOME defaults to ~/.flowly. The file is stored with owner-only (0600) permissions because it holds API keys.

Sibling files in the same directory:

  • config.json.bak β€” a self-heal backup, seeded from the last good config.
  • config.json.broken-<unix-ts> β€” a corrupted config moved aside during recovery.

Profiles

A profile is an isolated FLOWLY_HOME with its own config, sessions, workspace, and credentials. The active profile is resolved in this order:

  1. -p / --profile <name> CLI flag
  2. FLOWLY_PROFILE environment variable
  3. ~/.flowly/active_profile sticky pointer file
  4. "default"

The default profile is ~/.flowly. A named profile coder lives at ~/.flowly/profiles/coder. Profile names must match ^[a-z0-9][a-z0-9_-]{0,63}$; the names flowly, default, test, tmp, root, and sudo are reserved. See Environment variables.

Self-healing loader

The loader is resilient by design:

  1. It parses config.json. If valid, it's used (and a .bak is seeded if missing).
  2. If parsing or validation fails, it tries config.json.bak. If that's good, the broken file is renamed config.json.broken-<ts> and the backup is restored.
  3. If both fail, Flowly falls back to in-code defaults so the agent still boots.
If both

config.json and config.json.bak fail to load, secrets in the broken file are lost from the running process (but the broken file is preserved on disk).

Saving is read-modify-write with a deep merge, so

unknown or manually-added keys are preserved. A None value in the model never overwrites an existing non-None value on disk β€” to truly clear a value, write "" or delete the key. Unknown top-level keys are ignored (extra = "ignore"), not errors. A handful of behaviors are overridable with the documented FLOWLY_* environment variables; note that a generic nested override like FLOWLY_GATEWAY__PORT only takes effect on the fallback path (when config.json is missing or unparseable) β€” when a valid config.json exists, it is not consulted for those.

Top-level keys

KeyPurpose
agentsAgent defaults, compaction, heartbeat, memory search, multi-agent
channelsMessaging channels (Telegram, Discord, Slack, …)
providersLLM providers and API keys
gatewayGateway host / port
toolsBuilt-in tool toggles and limits
integrationsThird-party integrations (Trello, voice, X, …)
auditLocal audit-log retention
pluginsEnable / disable plugins
mcpServersMCP server definitions
backgroundModebool (default false)

agents

agents.defaults β€” applied to the main agent:

KeyDefault
workspace"~/.flowly/workspace"
cwd"" (empty β†’ workspace; overridden by FLOWLY_CWD)
model"moonshotai/kimi-k2.5"
maxTokens8192
temperature0.7
actionTemperature0.1
actionToolRetries2
maxToolIterations100
softWarnAtIteration30
contextMessages100
persona"default"
saveTrajectoriesfalse
memoryNudgeInterval10
skillNudgeInterval15

agents.defaults.compaction β€” see Sessions:

KeyDefault
mode"safeguard" ("default" | "safeguard")
reserveTokensFloor20000
maxHistoryShare0.5
contextWindow128000
memoryFlush.enabledtrue
memoryFlush.softThresholdTokens4000

agents.defaults.heartbeat:

KeyDefault
enabledtrue
everyMinutes30
activeHoursnull (or { start: "09:00", end: "23:00", timezone: "" })
deliver"none" ("none" | "message_tool")

agents.defaults.memorySearch:

KeyDefault
enabledtrue
provider"auto" ("auto" | "openai" | "gemini" | "none")
model""
apiKey""
apiBase""
chunkTokens400
overlapTokens80
maxResults6
minScore0.35
vectorWeight0.7
textWeight0.3

agents.defaults.memoryDreaming β€” cross-session "dreaming" + autonomous consolidation (see Memory):

KeyDefault
enabledtrue (the whole governance/dreaming layer)
commitMode"selective" ("selective" | "manual" | "aggressive")
idleMinutes30 (run after this much agent inactivity; background heartbeats don't count)
dailyEnabledtrue
dailyTime"03:30" (HH:MM local)
turnInterval10 (also run every N user turns; 0 disables the coarse pass)
autoFloor0.80 (β‰₯ β†’ auto-active when unconflicted and not sensitive)
reviewFloor0.55 (< β†’ dropped instead of queued)
maxMessagesPerRun500 (bound per pass so a backlog can't blow up one run)
autoConsolidatetrue (background cleanup: merge duplicates, retire stale)
consolidateTurnInterval50 (consolidate every N user turns; 0 off)
consolidateEveryMinutes30 (background consolidation timer; 0 off)
freezeInjectedMemoryfalse (advanced: freeze the injected memory block per session for prefix-cache stability)

agents.agents (per-agent map) β€” name, provider ("anthropic" default | "openai" | "flowly"), model="", workingDirectory="", persona="". agents.teams (per-team map) β€” name, agents=[], leaderAgent="".

channels

Channels are off by default. See Channels overview.

ChannelKey defaults
whatsappenabled=false, bridgeUrl="ws://localhost:3001", allowFrom=[]
telegramenabled=false, token="", allowFrom=[], dmPolicy="pairing" ("open"|"pairing"|"allowlist")
discordenabled=false, token="", allowFrom=[], gatewayUrl="wss://gateway.discord.gg/?v=10&encoding=json", intents=37377
slackenabled=false, mode="socket", botToken="", appToken="", groupPolicy="mention", groupAllowFrom=[], dm.enabled=true, dm.policy="open", dm.allowFrom=[]
webenabled=false, relayUrl="", serverId="", authToken="", jwtSecret=""
emailenabled=false, pollIntervalSeconds=30, allowFrom=[]
teamsenabled=false, webhookUrl="", defaultChatLabel="", allowFrom=[]

providers

KeyDefault / notes
active"" β€” explicit default provider slug; "" falls back to the API-key cascade
flowlyenabled=true, apiBase="https://useflowlyapp.com/api/v1" (Flowly Cloud; uses account token when signed in)
xaiOAuthenabled=true, clientId="", apiBase="https://api.x.ai/v1" (tokens stored in OS keychain, not config)

BYOK provider slots β€” each with apiKey="", apiBase=null, fallbackKeys=[]: anthropic, openai, openrouter, zhipu, vllm, gemini, groq, xai.

When active="", the API-key cascade picks the first usable provider in priority order: OpenRouter β†’ Anthropic β†’ OpenAI β†’ xAI β†’ xAI OAuth β†’ Gemini β†’ Groq β†’ Zhipu β†’ Sakana β†’ vLLM. See Providers and models.

gateway

KeyDefault
host"127.0.0.1"
port18790 (1–65535)

tools

See Sandbox and approvals for execution policy.

ToolKey defaults
web.searchBrave defaults (apiKey="", maxResults=5, proxyUrl="") + backend selectors (backend/searchBackend/extractBackend) + per-backend sub-sections (ddgs, searxng, tavily, exa, firecrawl, parallel). See Web & research.
execenabled=true, timeoutSeconds=300, maxOutputChars=200000, approvalTimeoutSeconds=120, cronMode="deny" ("deny"|"approve")
artifactenabled=true, maxContentLength=500000
browserTabenabled=false
computerenabled=false, actionDelayMs=100, failsafe=true
codexSessionenabled=false, codexBin="codex", codexHome="", cwd="", turnTimeoutS=600, postToolQuietTimeoutS=90, approvalPolicy="on-request", sandbox="workspace-write", exposeFlowlyTools=true
The exec allowlist / per-command approval policy is

not in config.json β€” only enabled and the runtime knobs above are. Approval policy lives in the approvals store at ~/.flowly/credentials/exec-approvals.json. See Sandbox and approvals.

integrations

IntegrationKey defaults
trelloapiKey="", token=""
voiceenabled=false, bridgeUrl="http://localhost:8765", plus Twilio / STT / TTS credentials (sttProvider="groq", ttsProvider="elevenlabs", ttsVoice="21m00Tcm4TlvDq8ikWAM", language="en-US")
xbearerToken, apiKey, apiSecret, accessToken, accessTokenSecret (all "")
googleWorkspaceenabled=false, email=""
linearapiKey=""
homeAssistanturl="", token="" (tools register only when both are set)

audit

KeyDefault
enabledtrue
retentionDays90 (-1 disables the age cap)
maxSizeMb100 (0 disables the size cap)

Audit records are written as daily JSONL files under <FLOWLY_HOME>/audit/. These keys control retention only.

plugins

KeyDefault
enabled[]
disabled[]

Bundled plugins load by default unless listed in disabled. User plugins under $FLOWLY_HOME/plugins/<name>/ load only if listed in enabled. disabled overrides enabled.

mcpServers

A map of server name β†’ server config. Per server: enabled=true; stdio (command="", args=[], env={}) or http/sse (url="", headers={}); transport="auto" ("auto"|"stdio"|"http"|"sse"); timeout=120.0, connectTimeout=60.0; auth="" (""|"oauth"); tools.include=[], tools.exclude=[], tools.resources=false, tools.prompts=false; plus TLS, sampling, and osvCheck=true. Server names are preserved verbatim by the loader.

Example

A minimal config.json after setting an Anthropic key and a Telegram bot:

{
  "providers": {
    "active": "anthropic",
    "anthropic": { "apiKey": "sk-ant-..." }
  },
  "agents": {
    "defaults": {
      "model": "claude-sonnet-4-5",
      "persona": "default"
    }
  },
  "channels": {
    "telegram": { "enabled": true, "token": "123:ABC", "dmPolicy": "pairing" }
  },
  "gateway": { "host": "127.0.0.1", "port": 18790 }
}