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_searchandweb_extractwhen 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 platformexaPassthrough. 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_searchandweb_extractwhen Tavily is not available./searchreturns titles/URLs;/scrapereturns 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_searchandweb_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_KEYwhich writes theopenaiApiKeyprofile 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-Organizationon 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_messagewithout an explicitproviderroute 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_messagetool 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 (seestreamDefaultIntervalMson 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_messagestill 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(or0/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_messagetool routes to Slack whenprovider: "slack"is requested (or when slack is the default). Outbound is served by the shared Vercel Chat SDK Slack adapter:SLACK_BOT_TOKENsends messages and supports post+edit streaming;SLACK_CHANNEL_IDis the default target channel. Pair withSLACK_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_messageis unavailable; other providers still work. - Format: SLACK_BOT_TOKEN starts with
xoxb-; SLACK_CHANNEL_ID is aC...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:writescope. - 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_messagetool routes to Discord whenprovider: "discord"is requested (or when discord is the default). Supports streaming edits viaPATCH /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_messageis 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 Messagespermissions (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_messagetool routes to Home Assistant's notification platform whenprovider: "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_messageis 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 leadingnotify.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_messagetool routes to email whenprovider: "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_messageis 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.sendscope (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_messagetool routes to WhatsApp whenprovider: "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_messageis 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) — thechannelargument ofsend_messageoverrides 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_messagetool routes to Signal whenprovider: "signal"is requested. Unlike every other provider, Signal has no HTTP API — Minara shells out tosignal-clilocally (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-clitool available on the host. - When unset: the signal path of
send_messageis unavailable. - Runtime dependency:
signal-climust 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 forsignal-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-mslot, 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 Information→App 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_tokenechoed 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_xxxxxfrom Lark developer console. LARK_APP_SECRET — opaque secret from the same console. LARK_DEFAULT_CHAT_ID — defaultoc_xxxxxchat 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) oropen.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 (
@allfor 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 — theSECxxxxsigning 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 tochannel:. - 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
tokenfield 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 —
commonfor multi-tenant, GUID for single-tenant. TEAMS_DEFAULT_CONVERSATION_ID — default conversation to push to. TEAMS_DEFAULT_SERVICE_URL — default Bot Framework serviceUrl (typicallyhttps://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
trueto 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.messageevents 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 populatesInboundMessage.text. The original audio is retained inattachmentsso 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
textand 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, orelevenlabs. - 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.twitterskill viarequires_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.apiskill viarequires_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.apiskill 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.glassnodeskill viarequires_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) thekb_searchtool — 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_searchquery 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_searchQdrant 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, ordocker, or leavesauto(Desktop-local-first). Shell and files always follow the gateway process unless you pindocker.cloudonly 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
dockerexplicitly. - When to set: prefer Settings → Preferences → Session runtime.
cloudforces the Kernel browser (shell stays on this host);dockerforces the local container backend (needs MINARA_DOCKER_SANDBOX_IMAGE);localforces the Desktop bridge (fails clearly if missing). - Format:
auto,local,cloud, ordocker. 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-callenvironmentoverride. 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-slimornode: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_credentialsuploads 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_filerefuses 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