Configuration
Configure startup, global and conversation settings, platform credentials, sandbox limits, and environment variables.
First-time setup
Section titled “First-time setup”mikan requires a global settings file before normal startup. Create it once, review it, then start mikan with a workspace:
mikan onboardmikan --sandbox=host /path/to/workspaceThe default state directory is ~/.mikan. Use the same --state-dir for onboarding and normal startup when you choose another location:
mikan onboard --state-dir=/secure/mikan-statemikan --state-dir=/secure/mikan-state /path/to/workspacemikan creates a missing state directory with mode 0700. An existing directory must be owned by the current user and must not be world-writable. For sandboxed modes, keep it outside the workspace so tools cannot reach credentials or administrator settings.
Settings locations
Section titled “Settings locations”| Scope | Path | Purpose |
|---|---|---|
| Global | <state-dir>/settings.json | Required defaults for every conversation |
| Conversation | <state-dir>/conversations/<officeKey>/settings.json | Partial overrides for one conversation |
Conversation settings are host-authoritative. Older <workspace>/<officeKey>/settings.json files are migrated on first access and are not read again from the sandbox-visible workspace.
Office keys
Section titled “Office keys”Every conversation is an office, identified by its platform plus the platform’s raw conversation id. Storage paths use the office key derived from both — v1-<platform>-<readable-id>-<hash>, for example v1-slack-c0aaaaaa1-1f4b9c0d2e3a5b7c — so two platforms that happen to share a raw conversation id can never address each other’s files, settings, or credentials. The same key names the office directory in the workspace, its state directory, and its vault.
Office keys are not reversible to a raw platform id, so the host keeps a registry at <state-dir>/office-registry.json recording each office’s platform and conversation id. Use mikan office list to read it.
Upgrading from a release that stored conversations under raw platform ids migrates those directories, vaults, and state trees to the office-key layout on the next start; see Deployment.
Generated settings
Section titled “Generated settings”mikan onboard creates:
{ "llm": { "provider": "anthropic", "model": "claude-sonnet-4-6", "thinkingLevel": "off", "autoReply": { "provider": "anthropic", "model": "claude-haiku-4-5" } }, "slack": { "replyMode": "top-level" }, "sandbox": { "cpus": "0.5", "memory": "1g", "boost": { "cpus": "2", "memory": "4g" }, "defaultSharedVault": "" }}Settings fields
Section titled “Settings fields”The values below are generated by onboarding. llm.provider, llm.model, and llm.thinkingLevel are required in the resolved global configuration; the other fields may be omitted.
| Field | Onboarding value | Description |
|---|---|---|
llm.provider | anthropic | Main AI provider |
llm.model | claude-sonnet-4-6 | Main model name |
llm.thinkingLevel | off | off, minimal, low, medium, high, xhigh, or max |
llm.autoReply.provider | anthropic | Optional model provider used to evaluate auto-reply rules |
llm.autoReply.model | claude-haiku-4-5 | Optional model used to evaluate auto-reply rules |
sentry.dsn | unset | Sentry DSN; sensitive prompt and tool content is redacted |
sandbox.cpus | 0.5 | CPU limit for mikan-managed image runtimes |
sandbox.memory | 1g | Memory limit for mikan-managed image runtimes |
sandbox.boost.cpus | 2 | Temporary CPU limit applied by /pi-sandbox boost |
sandbox.boost.memory | 4g | Temporary memory limit applied by /pi-sandbox boost |
sandbox.workspace.doorPolicy | unset | Explicit override: isolated locks the office to its own data; trusted permits a collaborative workspace layout |
sandbox.workspace.layout | unset | Explicit trusted layout override: shared-support or full |
sandbox.workspace.visibility | unset | For shared-support, public makes global memory read-write; private makes it read-only |
sandbox.defaultSharedVault | empty | Shared vault copied into eligible membership-trust image/Cloudflare conversations |
slack.replyMode | top-level | Slack response mode: top-level or thread |
/pi-model writes a partial conversation override, and /pi-sandbox door <default|isolated|shared|shared-private|full> writes the conversation’s sandbox.workspace override; the admin portal sets both the per-office and the global door policy. Auto-reply enablement and rule text are managed by /pi-auto-reply and the conversation’s auto-reply marker file, not by the JSON settings fields.
Onboarding does not write sandbox.workspace. Without an explicit global or conversation override, mikan follows recorded platform channel visibility. Currently Slack public channels resolve to trusted + shared-support + public, so they can read and update workspace-global MEMORY.md; Slack private channels resolve to trusted + shared-support + private, so global memory is mounted read-only. Slack DMs, externally shared channels, unknown channel kinds, and platforms without recorded channel visibility resolve to isolated. This means a new Slack public channel contributes to shared workspace memory without an additional door-policy command.
Door policy and layout resolve together. isolated always means the conversation layout: only the office’s own directory is mounted. trusted means either shared-support — the office plus the workspace-level MEMORY.md, skills/, and events/ — or full, which mounts the whole workspace root. A door policy of trusted with no layout resolves to shared-support. Only image:* can enforce isolated projections or read-only shared memory; host, container:*, and cloudflare:* fail closed for those projections and require either image:* or an explicit trusted read-write policy.
Legacy sandbox.image.workspaceMount remains readable for migration: legacy workspaceMount: "private" means trusted + shared-support with public/read-write visibility, preserving its old behavior; it is unrelated to the new sandbox.workspace.visibility: "private", which makes shared memory read-only. Legacy workspaceMount: "full" means trusted + full.
MCP servers
Section titled “MCP servers”mcpServers connects Model Context Protocol servers and exposes their tools to the agent as mcp__<server>__<tool>. Servers run on the host: credentials placed in env (stdio) or headers (HTTP) stay in the server process and are never visible to the model or the sandbox.
{ "mcpServers": { "github": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_..." } }, "internal-docs": { "url": "https://mcp.example.com/mcp", "headers": { "Authorization": "Bearer ..." } } }}Each entry uses exactly one transport: command (+ optional args, env) spawns a stdio server; url (+ optional headers) connects over streamable HTTP. disabled: true turns an entry off without deleting it.
Global and per-conversation mcpServers merge per server name: a conversation entry overrides (or disables) the same-name global entry and other global entries stay available. The admin portal has panels for both scopes; changes take effect on the next response. An unreachable server logs a warning and the rest still load.
Platform credentials
Section titled “Platform credentials”At least one complete platform credential set is required for normal bot mode:
| Platform | Required environment variables | Optional variables |
|---|---|---|
| Slack | SLACK_APP_TOKEN, SLACK_BOT_TOKEN | — |
| Telegram | TELEGRAM_BOT_TOKEN | — |
| Discord | DISCORD_BOT_TOKEN | — |
| GitHub | GITHUB_APP_ID, GITHUB_INSTALLATION_ID, and GITHUB_APP_PRIVATE_KEY or GITHUB_APP_PRIVATE_KEY_PATH | GITHUB_REPOS, GITHUB_POLL_INTERVAL |
See Platform adapters for platform-specific setup and permissions.
CLI reference
Section titled “CLI reference”| Command or option | Purpose |
|---|---|
mikan onboard [--state-dir=<dir>] | Create the required global settings file |
mikan [--state-dir=<dir>] [--sandbox=<mode>] [working-directory] | Start configured platform bots; the working directory defaults to <state-dir>/workspace |
--sandbox=host | container:<name> | image:<image> | cloudflare:<id> | Select tool execution mode; default is host |
mikan env | Show the full environment-variable inventory and what is currently set |
mikan --download <channel-id> | Download Slack channel history; requires SLACK_BOT_TOKEN |
mikan --version | Print the installed version |
mikan --help | Show CLI usage and the platform-token summary |
mikan ext ... | Manage harness extensions; run mikan ext for subcommands |
mikan office list | List registered offices, enabled platforms, and pending legacy migrations |
mikan office claim <conversationId> <platform> | Name the owning platform of a legacy raw-id directory boot could not attribute |
mikan office accepts --state-dir <dir> and --workspace <dir>; the workspace defaults to <state-dir>/workspace. claim only records the decision — the daemon performs the move on its next start, so run it with the daemon stopped.
Environment variable aliases
Section titled “Environment variable aliases”Environment variables read through mikan’s configuration helper also accept a MIKAN_ prefix. For example, MIKAN_SLACK_APP_TOKEN and MIKAN_LINK_URL are fallbacks for SLACK_APP_TOKEN and LINK_URL; the unprefixed value takes precedence. SENTRY_DSN is the exception: configure it directly or set sentry.dsn in settings.json.
The daemon’s full environment interface is declared as a manifest in the source tree; mikan env prints the annotated inventory grouped by platform and feature, with the current status of each variable, so you can audit a deployment without reading code.
mikan writes logs to stdout/stderr. Use PM2, systemd, Docker, or your hosting platform to route and retain them.