MINARA

Builtin tools

Builtin tools (src/tools/*) Each one is optional — missing key → the feature silently disables.

web_search / web_extract backend

web_search and web_extract share one backend. The model cannot pick a provider. First available of: 1. Tavily — TAVILY_API_KEY. Highest priority. 2. Firecrawl — FIRECRAWL_API_KEY, only when Tavily is not available. 3. Exa — local EXA_API_KEY, or a signed-in Minara session (platform exaPassthrough), only when neither Tavily nor Firecrawl is available. There is no chain fallthrough, no DuckDuckGo / Google / Brave / Anthropic native path, and no HTML-fetch extract. Empty results and HTTP errors stay on the chosen backend. JS-walled pages belong to browser_navigate / browser_snapshot. Advanced filters (Exa category, Firecrawl sources/tbs, Tavily topic/depth) appear on the tool schema for the active backend only.

EXA_API_KEY

Exa web search and extract (after Tavily and Firecrawl).

  • What it controls: the backend for both web_search and web_extract when neither Tavily nor Firecrawl is available. A local key is used directly (beats platform passthrough). When unset, a signed-in Minara session still runs Exa via platform exaPassthrough. Exa is a neural search index; search results carry highlight snippets. Extract uses /contents (clean page text). Failures do not fall through to another backend.
  • Consumed by: src/tools/web-backends.ts via createWebTools() in src/app.ts.
  • When to set: you want Exa locally without depending on Minara login.
  • When unset: Tavily is used if its key is set, otherwise Firecrawl, otherwise Exa still runs if you are signed in. If none of those are available, the tools are hidden.
  • Format: Exa API key from https://dashboard.exa.ai/api-keys.
  • Setting home: Settings → API Keys

FIRECRAWL_API_KEY

Firecrawl search + extract (after Tavily).

  • What it controls: the backend for web_search and web_extract when Tavily is not available. /search returns titles/URLs; /scrape returns clean markdown (onlyMainContent: true).
  • Consumed by: src/tools/web-backends.ts via createWebTools() in src/app.ts.
  • When to set: you want search + high-quality page extract without Tavily.
  • When unset: Firecrawl is skipped; Exa is used if a local key is set or you are signed in.
  • Free tier: 500 credits/month. Format: Firecrawl API key from https://www.firecrawl.dev/app/api-keys.
  • Setting home: Settings → API Keys

TAVILY_API_KEY

Tavily web search and extract (highest priority).

  • What it controls: the default backend for web_search and web_extract. Failures do not fall through to Firecrawl or Exa.
  • Consumed by: src/tools/web-backends.ts via createWebTools() in src/app.ts.
  • When to set: you want Tavily as the web research backend.
  • When unset: Firecrawl is used if its key is set, otherwise Exa (local key or signed-in passthrough). If none of those are available, web_search and web_extract stay hidden. There is no DuckDuckGo fallback.
  • Format: Tavily API key from https://tavily.com.
  • Setting home: Settings → API Keys

GOAL_MAX_TURNS

max automatic continuation turns for a standing /goal before it pauses for review (REPL goal mode).

  • Default when unset: 20
  • Format: positive integer
  • Setting home: Settings → Preferences (schema key)

POSITION_MEMORY_ENABLED

position/session-aware memory injection. When on, each chat turn that involves an asset (a ticker in the message, or one of the user's recent spot top symbols) gets up to 5 stored memories about that asset injected into the volatile prompt tail — past views and trade notes for advice-style questions, the user's analysis preferences and habits for fundamentals-style questions. Deterministic and local (keyword intent routing + SQLite lookups, zero extra LLM calls); the trading signal comes from the pre-built spot top-symbols artifact refreshed by the trading-summary rebuild. Consumed by app.ts (positionMemoryProvider) via memory/position-memory.ts. Read live through the preferences manager, so flipping it takes effect on the next turn.

  • Default when unset: off
  • Format: 1/true/yes/on to enable
  • Setting home: Settings → Preferences (schema key)

OPENAI_API_KEY

OpenAI platform key, multi-purpose.

  • What it controls: (1) research.knowledge_base skill — text-embedding-3-* embeddings written to Qdrant. (2) Audio TTS in src/tools/audio.ts. (3) Optional LLM provider (opt-in) — distinct from the OpenAI OAuth path. Setting this env alone does NOT auto-select OpenAI as the LLM provider. To enable the LLM path, run minara auth login openai --api-key $OPENAI_API_KEY which writes the openaiApiKey profile slot.
  • Consumed by: src/skills/builtin/research/knowledge-base.ts, src/tools/audio.ts, src/llm/openai-api-key.ts.
  • When unset: research KB writes are skipped; TTS unavailable; LLM uses another provider.
  • Format: sk-... from https://platform.openai.com.
  • Setting home: LLM provider credential (Settings → Provider & Model)

OPENAI_BASE_URL

optional override for the OpenAI API base URL.

  • What it controls: where the OpenAI api-key LLM client + tools call.
  • Default: https://api.openai.com/v1
  • When to set: targeting an Azure OpenAI-compatible gateway or a corporate proxy with the same wire shape.
  • When unset: official OpenAI endpoint is used.
  • Setting home: Not a user-facing setting

OPENAI_ORG_ID

optional OpenAI organization header.

  • What it controls: sent as OpenAI-Organization on every LLM call.
  • When to set: your OpenAI account has multiple orgs and you want billing routed to a specific one.
  • When unset: the default org tied to the API key is used.
  • Setting home: Not a user-facing setting

FAL_KEY / FAL_QUEUE_URL

Fal.ai image/media provider.

  • What it controls: Fal.ai authentication for image_generate, video_generate, and the live image/video model catalog.
  • Consumed by: src/media/* and the media generation tools.
  • When to set: you want API-key authentication. You can instead use Settings > Provider & Model > Image & video to sign in with Fal.ai.
  • When unset: media generation requires Fal.ai sign-in.
  • Format: FAL_KEY is an opaque string from https://fal.ai; FAL_QUEUE_URL is an optional override for the queue endpoint (absolute URL, only set if you're using a private Fal.ai deploy).
  • Setting home: Settings → API Keys (FAL_KEY)
  • Setting home: Not a user-facing setting (FAL_QUEUE_URL)

Messaging gateways

Outbound notification destinations used by the send_message tool, workflow triggers, and autopilot reports. Multiple providers can be configured simultaneously; the LLM picks one per call via the provider arg, falling back to MESSAGING_DEFAULT_PROVIDER. Prefer minara auth messaging add <provider> — the wizard persists the credentials to ~/.minara/credentials.json (messaging slot) and never touches this file.

MESSAGING_DEFAULT_PROVIDER

default provider for send_message when the caller omits the provider argument.

  • What it controls: agent-loop / workflow / autopilot calls to send_message without an explicit provider route to this id.
  • Consumed by: src/app.ts (gateway map construction), src/tools/messaging.ts (handler dispatch).
  • When to set: you've configured more than one provider and want a specific one to be the default (e.g. telegram for personal alerts, slack for team notifications — and you want telegram by default).
  • When unset: the first configured provider wins (insertion order follows MESSAGING_PROVIDERS in src/messaging/providers.ts — telegram → discord → slack → whatsapp → signal → email → home_assistant).
  • Format: a provider id — one of telegram, discord, slack, whatsapp, signal, email, home_assistant. Case-insensitive.
  • Setting home: Not a user-facing setting

MESSAGING_MAX_ATTACHMENT_BYTES

per-attachment size cap for send_message({attachments: [...]}) calls.

  • What it controls: before each attachment is uploaded to its provider API, the resolver stats the sandbox file and rejects with a clear error when the size exceeds this value. Prevents an unbounded LLM-controlled path from DOSing SMTP relays / Discord / Telegram with massive files.
  • Consumed by: src/messaging/attachment-resolver.ts.
  • When to set: operators want to tighten the 50 MB default (e.g. for Slack free-tier 1 GB storage quotas, or to block any audio longer than a minute).
  • When unset: defaults to 52 428 800 bytes (50 MB). Provider APIs enforce their own maxima independently — this cap is the Minara- side limit; providers may still reject larger files their APIs consider too big.
  • Format: decimal integer (bytes). Values <= 0 are ignored.
  • Setting home: Not a user-facing setting

TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID

outbound Telegram messaging.

  • What it controls: the send_message tool posts to the given chat via the Bot API. Used by workflow notifications and alert outputs. Telegram is the only provider with streaming edit support baked in today (see streamDefaultIntervalMs on the gateway).
  • Consumed by: src/messaging/telegram.ts.
  • When to set: you want the agent (or a workflow trigger) to push alerts to a Telegram channel or DM.
  • When unset: send_message still works for other transports but the Telegram path returns a clear configuration error.
  • Format: TELEGRAM_BOT_TOKEN is the 123456:ABC-DEF... string from @BotFather; TELEGRAM_CHAT_ID is a numeric chat id (negative for channels/groups, positive for DMs).
  • Setting home: Settings → Messaging

TELEGRAM_RICH_TEXT

render outbound replies as Telegram rich text.

  • What it controls: when on (the default), markdown replies are rendered to Telegram HTML (bold, headings, tables, code blocks, task lists) with a MarkdownV2 → plain-text fallback if Telegram rejects the markup. Streaming edits flush only fully-closed constructs so the user never sees half-rendered formatting.
  • Consumed by: src/messaging/telegram.ts.
  • When to set: set to false (or 0/no/off) to send the agent's text verbatim with no formatting.
  • When unset: rich text is ON.
  • Setting home: Settings → Preferences (schema key)

SLACK_BOT_TOKEN / SLACK_CHANNEL_ID

outbound Slack.

  • What it controls: the send_message tool routes to Slack when provider: "slack" is requested (or when slack is the default). Outbound is served by the shared Vercel Chat SDK Slack adapter: SLACK_BOT_TOKEN sends messages and supports post+edit streaming; SLACK_CHANNEL_ID is the default target channel. Pair with SLACK_APP_TOKEN (below) to open the Socket Mode inbound stream.
  • Consumed by: src/messaging/chatsdk/adapters/slack.ts.
  • When to set: team / work notifications and two-way Slack chat.
  • When unset: the Slack path of send_message is unavailable; other providers still work.
  • Format: SLACK_BOT_TOKEN starts with xoxb-; SLACK_CHANNEL_ID is a C... channel id (not the #name).
  • Setting home: Settings → Messaging

SLACK_APP_TOKEN

app-level token (xapp-…) for Socket Mode inbound.

  • What it controls: enables the client-outbound Slack inbound daemon (Socket Mode). With this set, the agent opens a WebSocket to Slack and receives Events API messages — no public Request URL / webhook server needed, so two-way chat works on a no-public-IP machine. Pair with SLACK_BOT_TOKEN (used to send the reply).
  • Consumed by: src/messaging/inbound/slack-daemon.ts.
  • When to set: you want Slack two-way chat without exposing a public webhook (e.g. running on a laptop / behind NAT). Generate it in the Slack app config under "Socket Mode" / "App-Level Tokens" with the connections:write scope.
  • When unset: Slack inbound falls back to the webhook route (needs a public Request URL + SLACK_SIGNING_SECRET).
  • Format: xapp-1-....
  • Setting home: Settings → Messaging

DISCORD_BOT_TOKEN / DISCORD_CHANNEL_ID

outbound Discord.

  • What it controls: the send_message tool routes to Discord when provider: "discord" is requested (or when discord is the default). Supports streaming edits via PATCH /channels/{id}/messages/{id} with a 1000ms throttle (Discord rate limits = 5/5sec per channel).
  • Consumed by: src/messaging/discord.ts.
  • When to set: community server notifications, Discord-centric teams.
  • When unset: the Discord path of send_message is unavailable.
  • Format: DISCORD_BOT_TOKEN is the opaque bot secret from the Developer Portal app page; DISCORD_CHANNEL_ID is a numeric channel snowflake. The bot must have been invited to the server + channel with the Send Messages + Manage Messages permissions (Manage Messages is only needed if you rely on streaming edits).
  • Setting home: Settings → Messaging

HASS_URL / HASS_TOKEN / HASS_NOTIFY_SERVICE

Home Assistant notify.

  • What it controls: the send_message tool routes to Home Assistant's notification platform when provider: "home_assistant" is requested. Does not support streaming (notify.* is fire-and-forget); helper falls back to a single send at finalize.
  • Consumed by: src/messaging/home_assistant.ts.
  • When to set: you want trade alerts pushed to phone push / Alexa TTS / other Home Assistant-mediated endpoint.
  • When unset: the Home Assistant path of send_message is unavailable.
  • Format: HASS_URL is the full base URL (https://hass.example:8123, no trailing slash); HASS_TOKEN is a long-lived access token from your HA profile; HASS_NOTIFY_SERVICE is a notify service id (mobile_app_pixel, alexa_tts; the leading notify. is optional — stripped by the adapter).
  • Setting home: Settings → Messaging

SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASSWORD / EMAIL_FROM / EMAIL_TO

outbound email (SMTP).

  • What it controls: the send_message tool routes to email when provider: "email" is requested. Subject is inferred from the first line of the message (≤ 120 chars, single line) — otherwise falls back to "Minara alert". Does NOT support streaming edits.
  • Consumed by: src/messaging/email.ts (wraps MIT-licensed nodemailer).
  • When to set: compliance / archival channels, or when the target recipient doesn't use one of the chat platforms.
  • When unset: the email path of send_message is unavailable; other providers keep working.
  • Format: SMTP_HOST is the relay hostname; SMTP_PORT is 587 for STARTTLS or 465 for SSL (the adapter picks TLS mode by port — secure: true iff port === 465); SMTP_USER / SMTP_PASSWORD are credentials (use an app-specific password for Gmail); EMAIL_FROM is the From header address; EMAIL_TO is the default recipient (can be overridden per-message via channel).
  • Setting home: Settings → Messaging

GOOGLE_OAUTH_CLIENT_ID / GOOGLE_OAUTH_CLIENT_SECRET

operator-supplied Google OAuth client that powers the "Email (Gmail)" one-click connect (provider id email-gmail).

  • What it controls: enables the Connect-Gmail button in Settings → Messaging. The agent then sends notifications through the Gmail API using the narrow gmail.send scope (it never reads mail).
  • Consumed by: src/gateway/api.ts (OAuth init) + src/messaging/gmail.ts.
  • When to set: to offer the Gmail connector. Create an OAuth client in Google Cloud console, type "Desktop app" (loopback redirect), then paste the ID + secret here OR directly in the Email (Gmail) panel. Precedence matches every other messaging credential: an exported env var wins; otherwise the value saved in the UI is used.
  • When unset (and not entered in the UI): the Connect-Gmail button is disabled; other providers keep working.
  • preferences-schema-coverage: SKIP — messaging-provider credentials are managed in Settings → Messaging, not the Preferences schema (same as SMTP_* / TELEGRAM_*).
  • Setting home: Settings → Messaging

GMAIL_REFRESH_TOKEN / GMAIL_SENDER_EMAIL / GMAIL_TO

Gmail connection state. GMAIL_REFRESH_TOKEN and GMAIL_SENDER_EMAIL are written by the Connect-Gmail flow (no need to set by hand). GMAIL_TO is an optional recipient override — leave blank to push notifications to the connected inbox itself (push-to-self). preferences-schema-coverage: SKIP — see GOOGLE_OAUTH_CLIENT_ID above.

  • Setting home: Settings → Messaging

WHATSAPP_ACCESS_TOKEN / WHATSAPP_PHONE_NUMBER_ID / WHATSAPP_RECIPIENT

outbound WhatsApp via the Meta Cloud API.

  • What it controls: the send_message tool routes to WhatsApp when provider: "whatsapp" is requested. Does NOT support streaming edits (Meta's edit API has a 15-min window + strict rate limits that make it unsuitable for LLM token streaming).
  • Consumed by: src/messaging/whatsapp.ts.
  • When to set: you've provisioned a Meta Business app with WhatsApp Business API access and need notifications delivered via WhatsApp.
  • When unset: the whatsapp path of send_message is unavailable.
  • Format: WHATSAPP_ACCESS_TOKEN is a long opaque Bearer token (usually starts with EAA...) issued by the Meta Developer Portal or your Business System User; WHATSAPP_PHONE_NUMBER_ID is the numeric id of the registered business phone number; WHATSAPP_RECIPIENT is a default recipient in E.164 format (+12025551234) — the channel argument of send_message overrides it per call.
  • Note: recipients must have an active 24-hour customer service window or be sent a pre-approved template message — that's a Meta policy constraint, not ours. Plain text messages only work if the user has messaged the business in the last 24 hours.
  • Setting home: Settings → Messaging

SIGNAL_CLI_NUMBER / SIGNAL_RECIPIENT / SIGNAL_CLI_BINARY

outbound Signal.

  • What it controls: the send_message tool routes to Signal when provider: "signal" is requested. Unlike every other provider, Signal has no HTTP API — Minara shells out to signal-cli locally (MIT-licensed, https://github.com/AsamK/signal-cli). Does NOT support streaming edits (the Signal protocol doesn't allow edits).
  • Consumed by: src/messaging/signal.ts.
  • When to set: you want privacy-focused alerts to a Signal recipient and have the signal-cli tool available on the host.
  • When unset: the signal path of send_message is unavailable.
  • Runtime dependency: signal-cli must be installed and on PATH (macOS: brew install signal-cli; Debian: see upstream repo). Before first use, register the sender number: signal-cli -u +15555550100 register then enter the SMS code that arrives on the phone. If the binary is not present at app boot, the signal provider is silently skipped — other providers continue to work.
  • Format: SIGNAL_CLI_NUMBER and SIGNAL_RECIPIENT must both be in E.164 format (leading +, country code, 8-15 total digits, no spaces or dashes — e.g. +12025551234). The adapter validates the regex at construction and on every send() call, and uses a fixed argv layout with -- before the recipient to block any leading-dash flag injection. SIGNAL_CLI_BINARY is an optional override for the binary path — default is PATH lookup for signal-cli.
  • CLAUDE.md §4a Bucket B exemption: this module spawns a subprocess but uses argv form with shell: false, and no LLM input reaches the argv — the message body is passed via a distinct -m slot, and the recipient is regex-validated before being written.
  • Setting home: Settings → Messaging

Inbound messaging webhook (optional, kill-switched)

The inbound listener is NOT started automatically — callers instantiate InboundServer themselves (see src/messaging/inbound/server.ts). When enabled, it listens for signed webhooks from Telegram, Discord, and Slack and dispatches normalized InboundMessage events to a single registered handler. WhatsApp + Signal inbound are NOT wired in this PR (WhatsApp needs a cert-exchange handshake; Signal uses a signal-cli daemon JSON-RPC socket — both deserve their own PR).

TELEGRAM_WEBHOOK_SECRET

shared secret echoed by Telegram in the X-Telegram-Bot-Api-Secret-Token header. Set at the same time as setWebhook's secret_token parameter.

  • What it controls: requests missing or mismatching this header return 401 before the handler runs.
  • When unset: the /webhooks/telegram route returns 404 — the inbound server behaves as if Telegram inbound isn't configured.
  • Format: opaque string, 1–256 chars (Telegram's own constraint).
  • Setting home: Not a user-facing setting

SLACK_SIGNING_SECRET

Slack app signing secret (NOT the bot token).

  • What it controls: webhook signature verification using HMAC-SHA256 over v0:{timestamp}:{body}, per Slack's published scheme.
  • When unset: the HTTP webhook path can't verify Slack requests; Socket Mode (SLACK_APP_TOKEN) doesn't need it.
  • Format: opaque hex string from Basic InformationApp Credentials.
  • Setting home: Settings → Messaging

DISCORD_PUBLIC_KEY / DISCORD_APPLICATION_ID

Discord app identity.

  • What it controls: the Vercel Chat SDK Discord adapter's constructor requires the application public key (even in Gateway mode), so DISCORD_PUBLIC_KEY must be set alongside DISCORD_BOT_TOKEN to build the adapter. DISCORD_APPLICATION_ID is optional and identifies the app for slash-command registration.
  • When unset: Discord is treated as unconfigured (the adapter can't be built without the public key).
  • Format: DISCORD_PUBLIC_KEY is 64-character lowercase hex (raw 32-byte Ed25519 public key); DISCORD_APPLICATION_ID is the numeric app id.
  • Setting home: Settings → Messaging

WHATSAPP_APP_SECRET / WHATSAPP_VERIFY_TOKEN

WhatsApp Cloud API inbound webhook (Meta).

  • What it controls: - WHATSAPP_APP_SECRET signs every POST via X-Hub-Signature-256 (HMAC-SHA256 over the raw body). Bad signature → 401. - WHATSAPP_VERIFY_TOKEN is the Meta hub.verify_token echoed on the one-time GET handshake when the operator registers the webhook URL in the Meta dashboard.
  • When unset: - WHATSAPP_APP_SECRET missing → /webhooks/whatsapp POST = 404. - WHATSAPP_VERIFY_TOKEN missing → /webhooks/whatsapp GET = 404.
  • Format: opaque strings; the app secret is hex from the Meta app dashboard, the verify token is whatever you configured.
  • Setting home: Settings → Messaging

PR2: Asian IM platforms (Lark / WeCom / DingTalk / WeChat OA / QQ / LINE)

Each block sets BOTH the outbound credentials AND the inbound webhook secrets needed by src/messaging/inbound/specs/<id>.ts. Leaving any block empty disables both outbound + inbound for that provider; the route 404s and the gateway is omitted from the live map.

LARK_APP_ID / LARK_APP_SECRET / LARK_DEFAULT_CHAT_ID / LARK_VERIFICATION_TOKEN / LARK_ENCRYPT_KEY / LARK_DOMAIN

Lark / Feishu — LARK_*. Tenant-token outbound + signed/encrypted webhook events.

  • What it controls: outbound Lark IM messaging; inbound webhook events (im.message.receive_v1) for the agent-loop.
  • Consumed by: src/messaging/lark.ts + src/messaging/inbound/specs/lark.ts.
  • Format: LARK_APP_ID — cli_xxxxx from Lark developer console. LARK_APP_SECRET — opaque secret from the same console. LARK_DEFAULT_CHAT_ID — default oc_xxxxx chat id. LARK_VERIFICATION_TOKEN — Event Subscription verification token. LARK_ENCRYPT_KEY — optional Event Subscription encrypt key. When set, inbound POST bodies arrive AES-256-CBC-encrypted under SHA256(encrypt_key). Leave blank for plaintext mode. LARK_DOMAIN — open.feishu.cn (default, mainland) or open.larksuite.com (international).
  • Setting home: Settings → Messaging

WECOM_CORP_ID / WECOM_AGENT_ID / WECOM_SECRET / WECOM_DEFAULT_TOUSER / WECOM_CALLBACK_TOKEN / WECOM_CALLBACK_AES_KEY

WeCom (企业微信) self-built application.

  • What it controls: outbound to WeCom users / departments + signed AES-encrypted callback inbound. WeCom routes both surfaces through the same legacy SHA1+AES envelope as WeChat OA.
  • Consumed by: src/messaging/wecom.ts + src/messaging/inbound/specs/wecom.ts.
  • Format: WECOM_CORP_ID — corp ID from "我的企业" page. WECOM_AGENT_ID — application agent ID (numeric). WECOM_SECRET — application secret. WECOM_DEFAULT_TOUSER — default recipient (@all for the whole agent). WECOM_CALLBACK_TOKEN — Token from "接收消息" callback config. WECOM_CALLBACK_AES_KEY — 43-char EncodingAESKey from same page.
  • Setting home: Settings → Messaging

DINGTALK_WEBHOOK_URL / DINGTALK_WEBHOOK_SECRET

DingTalk (钉钉) custom group robot — outbound webhook with HMAC sign.

  • What it controls: outbound to a DingTalk group via the robot's webhook URL (signed with timestamp + secret per DingTalk's spec). Inbound listens for outgoing-message webhooks DingTalk POSTs back.
  • Consumed by: src/messaging/dingtalk.ts + src/messaging/inbound/specs/dingtalk.ts.
  • Format: DINGTALK_WEBHOOK_URL — the https://oapi.dingtalk.com/robot/send?access_token=… URL. DINGTALK_WEBHOOK_SECRET — the SECxxxx signing secret from "签名" mode.
  • Setting home: Settings → Messaging

DINGTALK_STREAM_APP_KEY / DINGTALK_STREAM_APP_SECRET

Stream Mode inbound.

  • What it controls: enables the client-outbound DingTalk Stream Mode daemon. With both set, the agent opens a gateway WebSocket and receives bot messages over it — no public callback URL needed, so two-way chat works on a no-public-IP machine. Distinct from the robot webhook above: Stream Mode authenticates as the org app (AppKey / AppSecret), while replies still go out via the robot webhook.
  • Consumed by: src/messaging/inbound/dingtalk-daemon.ts.
  • When to set: you want DingTalk two-way chat without exposing a public callback. Find the AppKey / AppSecret on the app's "凭证与基础信息" page in the DingTalk developer console.
  • When unset: DingTalk inbound falls back to the outgoing-webhook route (needs a public callback URL).
  • Format: opaque strings from the DingTalk developer console.
  • Setting home: Settings → Messaging

WECHAT_OA_APP_ID / WECHAT_OA_APP_SECRET / WECHAT_OA_TOKEN / WECHAT_OA_AES_KEY / WECHAT_OA_DEFAULT_OPENID

WeChat OA (公众号) customer-service messaging.

  • What it controls: outbound customer-service messages (must be within the user's 48-hour interaction window — outside that window the platform returns errcode 45015). Inbound: signed + AES-encrypted message events.
  • Consumed by: src/messaging/wechat_oa.ts + src/messaging/inbound/specs/wechat_oa.ts.
  • Format: WECHAT_OA_APP_ID / WECHAT_OA_APP_SECRET — OA AppID + AppSecret. WECHAT_OA_TOKEN — the server-config Token from 公众平台 → 设置 → 服务器配置. WECHAT_OA_AES_KEY — 43-char EncodingAESKey from same page. WECHAT_OA_DEFAULT_OPENID — default recipient openid.
  • Setting home: Settings → Messaging

QQ_BOT_APP_ID / QQ_BOT_APP_SECRET / QQ_BOT_TOKEN / QQ_BOT_DEFAULT_CHANNEL_ID

QQ Bot v2 (Official Bot OpenAPI).

  • RATE LIMIT WARNING: official bots are capped at ONLY 4 active
  • messages per month per bot, plus 200 active DMs per day, plus 20
  • active sub-channel messages per day per channel. Most interactions
  • MUST use passive replies (≤5s after a user-initiated message);
  • active push is reserved for critical alerts.
  • Consumed by: src/messaging/qq.ts + src/messaging/inbound/specs/qq.ts.
  • Format: QQ_BOT_APP_ID — bot AppID (numeric). QQ_BOT_APP_SECRET — bot Secret (used both as outbound auth and as the seed for inbound Ed25519 signature verification). QQ_BOT_TOKEN — bot token (legacy field, retained for compatibility). QQ_BOT_DEFAULT_CHANNEL_ID — default target. Format <kind>:<id> where kind is c2c | group | channel | dm. Bare ids default to channel:.
  • Setting home: Settings → Messaging

LINE_CHANNEL_ACCESS_TOKEN / LINE_CHANNEL_SECRET / LINE_DEFAULT_USER_ID

LINE Messaging API.

  • What it controls: outbound LINE push messages (PAID metered) plus signed inbound webhook events. Reply tokens (free) are not yet wired — every outbound goes through the metered push API.
  • Consumed by: src/messaging/line.ts + src/messaging/inbound/specs/line.ts.
  • Format: LINE_CHANNEL_ACCESS_TOKEN — long-lived bearer from LINE Developers. LINE_CHANNEL_SECRET — channel secret used to verify X-Line-Signature on inbound (HMAC-SHA256, base64). LINE_DEFAULT_USER_ID — default recipient userId / groupId / roomId.
  • Setting home: Settings → Messaging

PR3: Western IM + federation (Mattermost / Teams / Google Chat / BlueBubbles / Matrix)

MATTERMOST_URL / MATTERMOST_BOT_TOKEN / MATTERMOST_DEFAULT_CHANNEL_ID / MATTERMOST_OUTGOING_WEBHOOK_TOKEN

Mattermost — bot-token outbound + outgoing-webhook inbound.

  • CAVEAT: Mattermost's outgoing-webhook feature ONLY fires in PUBLIC
  • channels and only on TRIGGER WORDS configured for the webhook.
  • Private channels and DMs need the WebSocket bot path (future PR).
  • Consumed by: src/messaging/mattermost.ts + src/messaging/inbound/specs/mattermost.ts.
  • Format: MATTERMOST_URL — Mattermost server URL (no trailing slash). MATTERMOST_BOT_TOKEN — personal access token of a bot account. MATTERMOST_DEFAULT_CHANNEL_ID — default target channel. MATTERMOST_OUTGOING_WEBHOOK_TOKEN — token compared against the token field in outgoing-webhook bodies (constant-time).
  • Setting home: Settings → Messaging

TEAMS_BOT_APP_ID / TEAMS_BOT_APP_PASSWORD / TEAMS_BOT_TENANT_ID / TEAMS_DEFAULT_CONVERSATION_ID / TEAMS_DEFAULT_SERVICE_URL

Microsoft Teams — Bot Framework with JWT-validated inbound.

  • Inbound JWT validation pulls JWKS dynamically from
  • https://login.botframework.com/v1/.well-known/openidconfiguration
  • do NOT hard-code the JWKS URL anywhere; Microsoft rotates it.
  • Consumed by: src/messaging/teams.ts + src/messaging/inbound/specs/teams.ts.
  • Format: TEAMS_BOT_APP_ID — bot's Microsoft App ID GUID. TEAMS_BOT_APP_PASSWORD — bot's Microsoft App password. TEAMS_BOT_TENANT_ID — common for multi-tenant, GUID for single-tenant. TEAMS_DEFAULT_CONVERSATION_ID — default conversation to push to. TEAMS_DEFAULT_SERVICE_URL — default Bot Framework serviceUrl (typically https://smba.trafficmanager.net/teams). Production code should learn this from inbound activities and persist per-conversation; this env var is the bootstrap fallback.
  • Setting home: Settings → Messaging

GOOGLE_CHAT_CREDENTIALS / GOOGLE_CHAT_USE_ADC / GOOGLE_CHAT_PROJECT_NUMBER / GOOGLE_CHAT_DEFAULT_SPACE_ID

Google Chat — Vercel Chat SDK adapter (service-account or ADC; webhook inbound).

  • What it controls: outbound + inbound are served by the shared Vercel Chat SDK Google Chat adapter. Inbound is a Google-signed webhook / Pub/Sub push: the InboundServer forwards /webhooks/google_chat to chat.webhooks.google_chat (needs MESSAGING_INBOUND_ENABLED=1).
  • Consumed by: src/messaging/chatsdk/adapters/google_chat.ts.
  • Format: GOOGLE_CHAT_CREDENTIALS — service-account credentials JSON (or a path to the key file). CLAUDE.md §4: a path MUST live inside the data / sandbox tree; never reference a path outside the operator's control. GOOGLE_CHAT_USE_ADC — set to true to use Application Default Credentials instead of GOOGLE_CHAT_CREDENTIALS. GOOGLE_CHAT_PROJECT_NUMBER — GCP project number used to verify the inbound webhook signature (or set GOOGLE_CHAT_PUBSUB_AUDIENCE for Pub/Sub push). Required for inbound. GOOGLE_CHAT_DEFAULT_SPACE_ID — default space resource name (e.g. spaces/AAAA1234567).
  • Setting home: Settings → Messaging

BLUEBUBBLES_SERVER_URL / BLUEBUBBLES_PASSWORD / BLUEBUBBLES_DEFAULT_CHAT_GUID

BlueBubbles — iMessage bridge over a self-hosted server.

  • Requires the operator to run the BlueBubbles server on a Mac that
  • stays signed into the desired iMessage account. Authentication is
  • a single shared password — pure constant-time compare, no HMAC.
  • Consumed by: src/messaging/bluebubbles.ts + src/messaging/inbound/specs/bluebubbles.ts.
  • Format: BLUEBUBBLES_SERVER_URL — public URL of the BlueBubbles server (typically a tunnel like ngrok / cloudflared). BLUEBUBBLES_PASSWORD — server password (matches ?guid= query). BLUEBUBBLES_DEFAULT_CHAT_GUID — default chat GUID (e.g. iMessage;-;+15551234567).
  • Setting home: Settings → Messaging

MATRIX_HOMESERVER / MATRIX_ACCESS_TOKEN / MATRIX_USER_ID / MATRIX_DEFAULT_ROOM_ID

Matrix — federated client-server API. Inbound runs as a long-poll daemon (no HTTP webhook), gated by MESSAGING_MATRIX_INBOUND.

  • Consumed by: src/messaging/matrix.ts + src/messaging/inbound/matrix-daemon.ts.
  • Format: MATRIX_HOMESERVER — homeserver URL (e.g. https://matrix.org). MATRIX_ACCESS_TOKEN — long-lived access token. Use Bearer header, NOT the deprecated ?access_token= query form. MATRIX_USER_ID — bot user (e.g. @bot:example.org); used to filter self-loops in /sync. MATRIX_DEFAULT_ROOM_ID — default room id (e.g. !abc:example.org).
  • Caveats: this gateway does NOT support end-to-end encryption (E2EE)
  • rooms. Only plaintext rooms emit m.room.message events the
  • daemon will consume.
  • Setting home: Settings → Messaging

MESSAGING_MATRIX_INBOUND

kill-switch for the Matrix /sync daemon.

  • What it controls: when set, app boot starts the long-poll listener that emits Matrix room messages into the agent-loop bridge.
  • Consumed by: src/messaging/inbound/matrix-daemon.ts via the provider registry's inboundDaemon enumeration in src/app.ts.
  • Format: 1/true/yes/on (case-insensitive).
  • Setting home: Settings → Preferences (schema key)

MESSAGING_TELEGRAM_POLLING / MESSAGING_DISCORD_GATEWAY / MESSAGING_SLACK_SOCKET / MESSAGING_MATTERMOST_WS / MESSAGING_QQ_WS / MESSAGING_DINGTALK_STREAM / MESSAGING_LARK_WS

Client-outbound inbound daemons — override switches.

  • These platforms support BOTH a webhook (the platform connects IN to a
  • public URL) and a client-outbound daemon (the agent connects OUT and
  • holds a long connection / poll). The daemon is what makes two-way chat
  • work on a personal machine with no public IP — no tunnel, no third
  • party.
  • By DEFAULT each daemon auto-starts when the platform's outbound
  • credentials are configured AND no public webhook is wired for it
  • (i.e. its webhook signing secret is unset, or MESSAGING_INBOUND_ENABLED
  • is off). Configure a webhook for a platform and it keeps webhook
  • inbound instead. These switches are an explicit override of that
  • auto-decision and are tri-state: - unset / empty → auto (the default above) - 1/true/yes/on → force the daemon ON - 0/false/no/off → force the daemon OFF (stay webhook-only) MESSAGING_TELEGRAM_POLLING — Telegram getUpdates long-poll (src/messaging/inbound/telegram-daemon.ts). Webhook signal: TELEGRAM_WEBHOOK_SECRET. MESSAGING_DISCORD_GATEWAY — Discord Gateway WebSocket; also delivers normal channel / DM messages the Interactions webhook can't (src/messaging/inbound/discord-daemon.ts). Requires the privileged "Message Content" intent enabled in the Discord developer portal. Webhook signal: DISCORD_APPLICATION_PUBLIC_KEY. MESSAGING_SLACK_SOCKET — Slack Socket Mode; requires SLACK_APP_TOKEN (src/messaging/inbound/slack-daemon.ts). Webhook signal: SLACK_SIGNING_SECRET. MESSAGING_MATTERMOST_WS — Mattermost v4 WebSocket bot; also reaches DMs / private channels the outgoing-webhook path can't (src/messaging/inbound/mattermost-daemon.ts). Webhook signal: MATTERMOST_OUTGOING_WEBHOOK_TOKEN. MESSAGING_QQ_WS — QQ v2 gateway WebSocket (src/messaging/inbound/qq-daemon.ts). No webhook-exclusive signal (QQ's webhook reuses the outbound QQ_BOT_APP_SECRET), so the gateway daemon is preferred whenever QQ is configured; set MESSAGING_QQ_WS=0 to use the webhook instead. MESSAGING_DINGTALK_STREAM — DingTalk Stream Mode; requires DINGTALK_STREAM_APP_KEY / _SECRET (src/messaging/inbound/dingtalk-daemon.ts). DINGTALK_WEBHOOK_SECRET signs outbound sends, not webhook inbound, so it does not suppress the daemon; set MESSAGING_DINGTALK_STREAM=0 to use the webhook. MESSAGING_LARK_WS — Lark / Feishu long connection via the official SDK (src/messaging/inbound/lark-daemon.ts). Webhook signal: LARK_VERIFICATION_TOKEN.
  • Setting home: Not a user-facing setting

MESSAGING_INBOUND_TRANSCRIBE

enable voice-message transcription on inbound messages. Requires OPENAI_API_KEY.

  • What it controls: when set to 1/true/yes/on, inbound voice attachments are downloaded, transcribed via OpenAI Whisper, and the transcription populates InboundMessage.text. The original audio is retained in attachments so the downstream consumer can replay it.
  • Consumed by: src/messaging/inbound/transcribe.ts (invoked from the inbound message normalizer in follow-up work).
  • When unset: voice attachments arrive with empty text and the caller is responsible for whatever routing it wants.
  • Format: 1/true/yes/on (case-insensitive).
  • Setting home: Settings → Preferences (schema key)

MESSAGING_VOICE_REPLY

answer inbound voice notes with a spoken reply attachment in addition to the streamed text.

  • What it controls: after the agent's text reply finalizes, the reply is synthesized to audio and sent back to the same channel/thread as a voice (or audio/file, capability-dependent) attachment. Only fires when the inbound message itself contained a transcribed voice attachment. TTS failures never break the text reply.
  • Consumed by: src/messaging/inbound/agent-bridge.ts via the voice-delivery helper. Requires a configured voice provider (ELEVENLABS_API_KEY or OPENAI_API_KEY).
  • Format: 1/true/yes/on (case-insensitive).
  • Setting home: Settings → Preferences (schema key)

ELEVENLABS_API_KEY

ElevenLabs voice platform key.

  • What it controls: preferred provider for speech synthesis (TTS, eleven_turbo_v2_5) and transcription (STT, Scribe) across the gateway /v1/voice/* endpoints, web read-aloud, and messaging voice replies. When set, ElevenLabs is auto-selected over OpenAI for voice (lower latency); OpenAI remains the fallback and the only provider able to mint OGG/Opus voice notes (Telegram).
  • Consumed by: src/voice/resolve.ts.
  • When unset: voice falls back to OPENAI_API_KEY; if that is also unset, voice features are unavailable (clear error, no crash).
  • Format: sk_... from https://elevenlabs.io.
  • Setting home: Not a user-facing setting

VOICE_COMPOSER_STT_PROVIDER / VOICE_COMPOSER_STT_MODEL / VOICE_CONVERSATION_STT_PROVIDER / VOICE_CONVERSATION_STT_MODEL / VOICE_FILE_STT_PROVIDER / VOICE_FILE_STT_MODEL / VOICE_REPLY_TTS_PROVIDER / VOICE_REPLY_TTS_MODEL

Scenario-specific voice providers and models.

  • What it controls: independent provider/model choices for composer dictation, hands-free conversation, complete audio files/messages, and Agent voice replies. Provider values are auto, openai, or elevenlabs.
  • Defaults: composer/conversation: auto + gpt-live-transcribe; files/messages: auto + gpt-transcribe; replies: auto + the resolved provider's recommended TTS model.
  • Compatibility: VOICE_STT_PROVIDER/MODEL remain aliases for the file scenario; VOICE_TTS_PROVIDER/MODEL remain aliases for replies. Scenario-specific variables take precedence.
  • Consumed by: src/voice/resolve.ts and the /v1/voice/* gateway routes.
  • Setting home: Settings → Preferences (schema key)

VOICE_TTS_PROVIDER

pin the speech-synthesis vendor.

  • What it controls: overrides auto-selection (ElevenLabs first, OpenAI fallback) for TTS only.
  • Consumed by: src/voice/resolve.ts (runtime preference voice.ttsProvider).
  • Format: auto | elevenlabs | openai. Default: auto.
  • Setting home: Settings → Preferences (schema key)

VOICE_TTS_VOICE

voice id used when replies are read aloud.

  • What it controls: passed to the PRIMARY TTS provider as its native voice id (ElevenLabs voice id, or OpenAI voice name like alloy). Fallback providers use their own default voice.
  • Consumed by: src/voice/resolve.ts (runtime preference voice.ttsVoice).
  • When unset: the project default voice / OpenAI "alloy".
  • Setting home: Settings → Preferences (schema key)

VOICE_TTS_MODEL

TTS model id for the primary provider.

  • What it controls: the ElevenLabs model (also the default selected in Settings -> Voice models), and the OpenAI model when OpenAI is the primary. ElevenLabs choices: eleven_v3 (most human, default), eleven_multilingual_v2, eleven_turbo_v2_5, eleven_flash_v2_5.
  • Consumed by: src/voice/settings.ts (ElevenLabs base) + src/voice/resolve.ts (OpenAI, runtime preference voice.ttsModel).
  • When unset: eleven_v3 (ElevenLabs) / gpt-4o-mini-tts (OpenAI).
  • Setting home: Settings → Preferences (schema key)

VOICE_TTS_STABILITY / VOICE_TTS_SIMILARITY_BOOST / VOICE_TTS_STYLE / VOICE_TTS_SPEAKER_BOOST / VOICE_TTS_SPEED / VOICE_TTS_FAST_FIRST

Voice delivery defaults (ElevenLabs voice_settings). Each sets the BASE; the Settings -> Voice models sliders override on top, per user.

  • Consumed by: src/voice/settings.ts. VOICE_TTS_STABILITY 0..1 — lower = livelier, higher = steadier (default 0.6) VOICE_TTS_SIMILARITY_BOOST 0..1 — adherence to the voice timbre (default 0.8) VOICE_TTS_STYLE 0..1 — expressiveness / personality (default 0.45) VOICE_TTS_SPEAKER_BOOST 1/0 — clarity boost (default on) VOICE_TTS_SPEED 0.7..1.2 — playback rate (default 0.9) VOICE_TTS_FAST_FIRST 1/0 — read the first sentence of each reply on the fastest model so speech starts sooner (default on)
  • Setting home: Not a user-facing setting

VOICE_STT_PROVIDER

pin the speech-to-text vendor.

  • Consumed by: src/voice/resolve.ts (runtime preference voice.sttProvider).
  • Format: auto | elevenlabs | openai. Default: auto.
  • Setting home: Settings → Preferences (schema key)

VOICE_FFMPEG_PATH

optional ffmpeg binary for voice transcodes.

  • What it controls: AMR voice formats (WeCom, WeChat OA without the built-in transcript) hop through ffmpeg before STT, and WeCom voice replies transcode mp3 → AMR. When unset, "ffmpeg" is looked up on PATH; when neither resolves, those transcodes are skipped and the affected platform degrades gracefully.
  • Consumed by: src/messaging/audio-transcode.ts.
  • Setting home: Not a user-facing setting

VOICE_STT_MODEL

STT model id for the primary provider.

  • Consumed by: src/voice/resolve.ts (runtime preference voice.sttModel).
  • When unset: scribe_v1 (ElevenLabs) / gpt-4o-mini-transcribe (OpenAI).
  • Setting home: Settings → Preferences (schema key)

TWITTERAPI_API_KEY

third-party Twitter/X scraping provider.

  • What it controls: reads tweets, profiles, and search results via the twitterapi.io scraper (rate-limited, no OAuth needed).
  • Consumed by: src/tools/providers/twitterapi.ts. Gates the research.social.twitter skill via requires_env.
  • When to set: you want the research.social skill to pull live Twitter data without owning a developer app.
  • When unset: research.social.twitter is hidden from the skill catalog.
  • Format: opaque API key from https://twitterapi.io.
  • Distinct from X_API_BEARER_TOKEN (the official X API) — this one
  • goes to a third-party scraper, the other speaks to api.x.com/2
  • directly. Either or both can be set.
  • Setting home: Settings → API Keys

X_API_BEARER_TOKEN

X (Twitter) OFFICIAL API bearer token.

  • What it controls: read-only app-only access to api.x.com/2 (search, lookups, timelines).
  • Consumed by: src/tools/providers/x-api.ts. Gates the built-in x.api skill via requires_env.
  • When to set: you own a developer app on https://developer.x.com and want the agent to speak to the official API instead of (or in addition to) the twitterapi.io scraper.
  • When unset: the x.api skill is hidden from the catalog.
  • Format: Bearer token from console.x.com — long opaque string.
  • Setting home: Settings → API Keys

GLASSNODE_API_KEY

Glassnode on-chain analytics.

  • What it controls: on-chain metric endpoints (SOPR, MVRV, realised cap, flows, etc.).
  • Consumed by: src/tools/providers/glassnode.ts. Gates the research.onchain.glassnode skill via requires_env.
  • When to set: you have a Glassnode subscription and want the agent to cite on-chain indicators directly.
  • When unset: the glassnode skill is hidden from the catalog.
  • Format: opaque API key from https://glassnode.com.
  • Setting home: Settings → API Keys

QDRANT_URL / QDRANT_API_KEY

vector database for research KB.

  • What it controls: (1) the research.knowledge_base skill — teaches the agent to query the operator's curated Qdrant collections (news, projects, people, docs) via curl from the terminal. (2) the kb_search tool — first-class wrapper around the same Qdrant query path, used by institution-mode analysts (news / fundamentals / sentiment) BEFORE falling back to web_search.
  • Consumed by: src/skills/builtin/research-knowledge-base.ts (skill) and src/tools/kb-search.ts (institution-mode tool). Both gate on QDRANT_URL being present; kb_search ALSO requires a configured embeddings provider (EMBEDDING_PROVIDER + EMBEDDING_API_KEY).
  • When to set: you want the agent to query an existing v1-populated Qdrant instance for fresher / narrower news + project facts than open-web search returns.
  • When unset: the knowledge_base skill is hidden from the catalog and the kb_search tool isn't registered. Institution analysts fall back to web_search (zero behavior change).
  • Format: QDRANT_URL is an absolute URL (https://xxx.qdrant.io). QDRANT_API_KEY is optional — only set if your deployment requires auth (Qdrant Cloud does; a local docker usually doesn't).
  • Setting home: Not a user-facing setting (QDRANT_URL)
  • Setting home: Settings → API Keys (QDRANT_API_KEY)

KB_EMBEDDING_PROVIDER / KB_EMBEDDING_API_KEY / KB_EMBEDDING_MODEL / KB_EMBEDDING_DIM

KB-specific Embedder override for kb_search ONLY.

  • What it controls: which embedder is used to embed kb_search query strings before posting them to Qdrant. The embedder MUST match the one that originally populated the Qdrant collections — a dimension mismatch yields a 400 from Qdrant on every call.
  • When to set: only when your kb_search Qdrant instance was populated with a DIFFERENT embedder than the one you use for memory (EMBEDDING_PROVIDER / EMBEDDING_MODEL). Common case: v1 Qdrant populated with OpenAI text-embedding-3-small (1536d) while you run voyage-3 (1024d) for memory.
  • When unset: kb_search falls back to the shared embedder (EMBEDDING_PROVIDER + EMBEDDING_API_KEY + EMBEDDING_MODEL + EMBEDDING_DIM). If memory and KB share the same model, no override is needed.
  • Format: identical accepted values to the EMBEDDING_* family above. KB_EMBEDDING_API_KEY defaults to EMBEDDING_API_KEY when unset (so OpenAI users only need to set the provider + model overrides).
  • Setting home: Not a user-facing setting (KB_EMBEDDING_PROVIDER, KB_EMBEDDING_MODEL, KB_EMBEDDING_DIM)
  • Setting home: Settings → API Keys (KB_EMBEDDING_API_KEY)

E2B_API_KEY

Kernel cloud-browser coordinator (https://e2b.dev).

  • What it controls: lets the workbench start the Kernel cloud Chromium session (coordinator sandbox). Pair with KERNEL_API_KEY. This key does not move shell, files, or execute_code off the gateway host.
  • Consumed by: the workbench cloud-browser runtime (Kernel bootstrap). Resolved at call time (a key added in Settings → API Keys works without a restart).
  • When to set: Desktop/CLI when you want Kernel Live View instead of a local browser. Hosted Web Browser also needs it for the coordinator.
  • When unset: cloud browser placement is unavailable; Desktop/CLI stay on a local browser when the host allows it.
  • Format: opaque key from https://e2b.dev dashboard.
  • Safety: never inject a shared org key into a per-user hosted agent for the sake of running bash. Shell stays on the gateway VM.
  • Setting home: Settings → API Keys

KERNEL_API_KEY

KERNEL_API_KEY, managed Chromium for the Web Browser workbench.

  • What it controls: creates the Kernel cloud Chromium session used by the Web Browser workbench. The same session supplies the embedded Live View and the CDP endpoint used by agent browser tools.
  • Consumed by: the workbench cloud-browser runtime. It resolves at call time, so a key saved in Settings > API Keys applies without a restart.
  • When to set: when Web users need an interactive remote Browser. The runtime also requires E2B_API_KEY for the kernel-browser coordinator.
  • When unset: the Web Browser shows a setup screen with links to the official Kernel key documentation and dashboard. Desktop native Browser sessions remain available.
  • Format: opaque API key created at https://dashboard.onkernel.com.
  • Setting home: Settings → API Keys

WORKBENCH_E2B_SESSION_IDLE_SECONDS

Web cloud-resource pause grace period.

  • What it controls: how long the Gateway waits after the final Web client leaves a Chat or Institution session before pausing that session's cloud Browser. Pause keeps the Kernel session until timeout; shell on the gateway host is unaffected.
  • Consumed by: Preferences computer.cloudSessionIdleSeconds → the Gateway workbench cloud-resource reaper. Desktop native Browser and Computer sessions do not use this setting.
  • Default when unset: 60 seconds (1 minute).
  • When to set: raise it to preserve cloud state across slower session switches, or lower it to pause sooner. Prefer Settings → Preferences; env is an override. Returning before the delay expires cancels the pending pause.
  • Format: a finite number of seconds from 0 through 3600. Invalid or negative values use the 60-second default. Terminals are unaffected.
  • Setting home: Settings → Preferences (schema key)

MINARA_HOST_KIND

override gateway host classification.

  • What it controls: forces the gateway host kind used for Browser/Computer backend selection: desktop | local-cli | hosted-e2b | web-remote.
  • Consumed by: src/computer/host.ts (currentHostKind). Placement, capability endpoints, Chromium launch policy, and private-URL rules all read this classification.
  • Default when unset: inferred from MINARA_DESKTOP_PID / computer bridge / CDP, E2B_SANDBOX_ID or CREDENTIALS_DEK+/data, then GATEWAY_HOST, else local-cli.
  • When to set: tests and operator diagnostics only. Production Desktop and hosted-e2b should rely on the automatic signals.
  • Format: one of: desktop, local-cli, hosted-e2b, web-remote.
  • Setting home: Not a user-facing setting

MINARA_COMPUTER_BACKEND

pin session runtime (local / cloud / docker).

  • What it controls: pins session runtime (browser, shell, and files) to local, cloud, or docker, or leaves auto (Desktop-local-first). Shell and files always follow the gateway process unless you pin docker. cloud only selects the Kernel cloud browser; GUI computer use stays Desktop-only. Hosted hosts always use a cloud browser and run bash on the agent VM.
  • Consumed by: Preferences computer.backend → src/computer/backends.ts (readSessionBackendOverride) and session-runtime placement via SessionRuntimeStore.
  • Default when unset: auto: hosted → local shell + cloud browser; Desktop prefers native bridge when available (even with cloud keys); Desktop without bridge uses cloud browser when E2B_API_KEY + KERNEL_API_KEY are set, else local; local-cli stays local (keys only enable an explicit Cloud browser pin). Docker is never chosen by auto — pin docker explicitly.
  • When to set: prefer Settings → Preferences → Session runtime. cloud forces the Kernel browser (shell stays on this host); docker forces the local container backend (needs MINARA_DOCKER_SANDBOX_IMAGE); local forces the Desktop bridge (fails clearly if missing).
  • Format: auto, local, cloud, or docker. Empty / unset = auto.
  • Setting home: Settings → Preferences (schema key)

MINARA_DOCKER_SANDBOX_IMAGE

Docker execution backend image.

  • What it controls: container image for the sticky docker execution backend (shell + files + execute_code in one container). Pin Preferences computer.backend=docker (or MINARA_COMPUTER_BACKEND=docker) to select it — there is no per-call environment override. Hardened: capabilities dropped except the minimum package managers need, no-new-privileges, PID limit, size-limited /tmp; no host bind mounts, no host env forwarding. One container per chat session, reused across calls, removed after 15 min idle.
  • Consumed by: src/tools/_execution/docker-environment.ts via the ExecutionRouter wired in src/app.ts. Resolved at call time.
  • When to set: you want strong local isolation for untrusted code without a second cloud VM. Requires a running Docker (or Podman) daemon; the CLI is discovered on PATH and at the usual Docker Desktop locations.
  • When unset: the docker backend is unavailable; code/shell runs on the gateway host.
  • Format: any pullable image reference with the language runtimes you need, e.g. python:3.12-slim or node:22-slim.
  • Safety: same posture as local isolation — the container is the security boundary and holds no host credentials unless explicitly provisioned via environment_provision_credentials. Select docker only by pinning Preferences computer.backend=docker (no per-call override).
  • Setting home: Not a user-facing setting

MINARA_GITHUB_APP_CLIENT_ID

public client id for Coding GitHub sign-in.

  • What it controls: enables the GitHub App device-flow credential provider used by structured HTTPS Git and pull-request operations.
  • Consumed by: Desktop release CI embeds it in product-config.json; Desktop passes that public value to src/app/bootstrap.ts when wiring CodingGitService. A shell or project .env value remains a development override.
  • When to set: the operator has registered the Minara GitHub App with device flow and the reviewed repository permissions.
  • When unset: the Connect GitHub option reports that operator configuration is required; local SSH remains independent.
  • Format: the optional product-owned GitHub App client id (for example, Iv1.example). This is not a client secret.
  • Setting home: Not a user-facing setting

MINARA_FILTERED_SSH_AGENT

internal Desktop SSH authority marker.

  • What it controls: marks SSH_AUTH_SOCK as a Desktop-owned filtered endpoint that exposes only the identity selected by the owner.
  • Consumed by: src/gateway/api.ts when wiring the local SSH credential provider.
  • When to set: never set it manually; Minara Desktop injects it only while its filtered SSH-agent proxy is active.
  • When unset: the local SSH credential provider stays unavailable rather than trusting an unrestricted ambient SSH agent.
  • Format: the internal value 1; any other value is treated as unset.
  • Setting home: Not a user-facing setting

MINARA_CLOUD_CREDENTIAL_FILES

credential files provisioned into the docker execution container — OPERATOR-DECLARED, upload-only.

  • What it controls: the file set environment_provision_credentials uploads into the remote execution container so code running there can authenticate. The agent cannot choose paths — only this config can. Each entry maps a file INSIDE the data dir to an absolute VM destination. Local paths must be relative to MINARA_DATA_DIR; absolute paths, .. traversal, and symlinks escaping the data dir are rejected entry-by-entry at parse time. Provisioned VM paths are upload-only: environment_pull_file refuses to read them back, and file contents never enter the model context.
  • Consumed by: src/tools/_execution/credential-provision.ts via src/tools/environment-files.ts.
  • When to set: code you run in the cloud VM needs an API key file / token file. Prefer read-only, low-blast-radius credentials — the VM has full network egress. Never declare wallet keys or fund-moving credentials here.
  • When unset: environment_provision_credentials reports no files; cloud runs are credential-free (the default posture).
  • Format: JSON array of {"local": "<relative path>", "remote": "/abs/vm/path"}. Example: [{"local":"cloud-creds/market.json","remote":"/root/.creds/market.json"}]
  • Setting home: Not a user-facing setting

On this page

web_search / web_extract backendEXA_API_KEYFIRECRAWL_API_KEYTAVILY_API_KEYGOAL_MAX_TURNSPOSITION_MEMORY_ENABLEDOPENAI_API_KEYOPENAI_BASE_URLOPENAI_ORG_IDFAL_KEY / FAL_QUEUE_URLMessaging gatewaysMESSAGING_DEFAULT_PROVIDERMESSAGING_MAX_ATTACHMENT_BYTESTELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_IDTELEGRAM_RICH_TEXTSLACK_BOT_TOKEN / SLACK_CHANNEL_IDSLACK_APP_TOKENDISCORD_BOT_TOKEN / DISCORD_CHANNEL_IDHASS_URL / HASS_TOKEN / HASS_NOTIFY_SERVICESMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASSWORD / EMAIL_FROM / EMAIL_TOGOOGLE_OAUTH_CLIENT_ID / GOOGLE_OAUTH_CLIENT_SECRETGMAIL_REFRESH_TOKEN / GMAIL_SENDER_EMAIL / GMAIL_TOWHATSAPP_ACCESS_TOKEN / WHATSAPP_PHONE_NUMBER_ID / WHATSAPP_RECIPIENTSIGNAL_CLI_NUMBER / SIGNAL_RECIPIENT / SIGNAL_CLI_BINARYInbound messaging webhook (optional, kill-switched)TELEGRAM_WEBHOOK_SECRETSLACK_SIGNING_SECRETDISCORD_PUBLIC_KEY / DISCORD_APPLICATION_IDWHATSAPP_APP_SECRET / WHATSAPP_VERIFY_TOKENPR2: Asian IM platforms (Lark / WeCom / DingTalk / WeChat OA / QQ / LINE)LARK_APP_ID / LARK_APP_SECRET / LARK_DEFAULT_CHAT_ID / LARK_VERIFICATION_TOKEN / LARK_ENCRYPT_KEY / LARK_DOMAINWECOM_CORP_ID / WECOM_AGENT_ID / WECOM_SECRET / WECOM_DEFAULT_TOUSER / WECOM_CALLBACK_TOKEN / WECOM_CALLBACK_AES_KEYDINGTALK_WEBHOOK_URL / DINGTALK_WEBHOOK_SECRETDINGTALK_STREAM_APP_KEY / DINGTALK_STREAM_APP_SECRETWECHAT_OA_APP_ID / WECHAT_OA_APP_SECRET / WECHAT_OA_TOKEN / WECHAT_OA_AES_KEY / WECHAT_OA_DEFAULT_OPENIDQQ_BOT_APP_ID / QQ_BOT_APP_SECRET / QQ_BOT_TOKEN / QQ_BOT_DEFAULT_CHANNEL_IDLINE_CHANNEL_ACCESS_TOKEN / LINE_CHANNEL_SECRET / LINE_DEFAULT_USER_IDPR3: Western IM + federation (Mattermost / Teams / Google Chat / BlueBubbles / Matrix)MATTERMOST_URL / MATTERMOST_BOT_TOKEN / MATTERMOST_DEFAULT_CHANNEL_ID / MATTERMOST_OUTGOING_WEBHOOK_TOKENTEAMS_BOT_APP_ID / TEAMS_BOT_APP_PASSWORD / TEAMS_BOT_TENANT_ID / TEAMS_DEFAULT_CONVERSATION_ID / TEAMS_DEFAULT_SERVICE_URLGOOGLE_CHAT_CREDENTIALS / GOOGLE_CHAT_USE_ADC / GOOGLE_CHAT_PROJECT_NUMBER / GOOGLE_CHAT_DEFAULT_SPACE_IDBLUEBUBBLES_SERVER_URL / BLUEBUBBLES_PASSWORD / BLUEBUBBLES_DEFAULT_CHAT_GUIDMATRIX_HOMESERVER / MATRIX_ACCESS_TOKEN / MATRIX_USER_ID / MATRIX_DEFAULT_ROOM_IDMESSAGING_MATRIX_INBOUNDMESSAGING_TELEGRAM_POLLING / MESSAGING_DISCORD_GATEWAY / MESSAGING_SLACK_SOCKET / MESSAGING_MATTERMOST_WS / MESSAGING_QQ_WS / MESSAGING_DINGTALK_STREAM / MESSAGING_LARK_WSMESSAGING_INBOUND_TRANSCRIBEMESSAGING_VOICE_REPLYELEVENLABS_API_KEYVOICE_COMPOSER_STT_PROVIDER / VOICE_COMPOSER_STT_MODEL / VOICE_CONVERSATION_STT_PROVIDER / VOICE_CONVERSATION_STT_MODEL / VOICE_FILE_STT_PROVIDER / VOICE_FILE_STT_MODEL / VOICE_REPLY_TTS_PROVIDER / VOICE_REPLY_TTS_MODELVOICE_TTS_PROVIDERVOICE_TTS_VOICEVOICE_TTS_MODELVOICE_TTS_STABILITY / VOICE_TTS_SIMILARITY_BOOST / VOICE_TTS_STYLE / VOICE_TTS_SPEAKER_BOOST / VOICE_TTS_SPEED / VOICE_TTS_FAST_FIRSTVOICE_STT_PROVIDERVOICE_FFMPEG_PATHVOICE_STT_MODELTWITTERAPI_API_KEYX_API_BEARER_TOKENGLASSNODE_API_KEYQDRANT_URL / QDRANT_API_KEYKB_EMBEDDING_PROVIDER / KB_EMBEDDING_API_KEY / KB_EMBEDDING_MODEL / KB_EMBEDDING_DIME2B_API_KEYKERNEL_API_KEYWORKBENCH_E2B_SESSION_IDLE_SECONDSMINARA_HOST_KINDMINARA_COMPUTER_BACKENDMINARA_DOCKER_SANDBOX_IMAGEMINARA_GITHUB_APP_CLIENT_IDMINARA_FILTERED_SSH_AGENTMINARA_CLOUD_CREDENTIAL_FILES