---
title: "Configuration"
description: "Configure startup, global and conversation settings, platform credentials, sandbox limits, and environment variables."
url: "https://geminixiang.github.io/configuration/"
---

# Configuration

## First-time setup

mikan requires a global settings file before normal startup. Create it once, review it, then start mikan with a workspace:

```bash
mikan --onboard
mikan --sandbox=host /path/to/workspace
```

The default state directory is `~/.mikan`. Use the same `--state-dir` for onboarding and normal startup when you choose another location:

```bash
mikan --onboard --state-dir=/secure/mikan-state
mikan --state-dir=/secure/mikan-state /path/to/workspace
```

mikan 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

| 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

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](/deployment/#upgrading-across-the-office-layout-migration).

## Generated settings

`mikan --onboard` creates:

```json
{
  "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"
    },
    "workspace": {
      "doorPolicy": "isolated"
    },
    "defaultSharedVault": ""
  }
}
```

## 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/Gondolin runtimes; Gondolin rounds fractional values up to whole vCPUs                 |
| `sandbox.memory`               | `1g`                | Memory limit for mikan-managed image/Gondolin 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` | `isolated`          | `isolated` locks each conversation to its own office data; `trusted` explicitly permits a collaborative workspace layout |
| `sandbox.workspace.layout`     | `conversation`      | Effective layout: isolated always uses `conversation`; trusted uses `shared-support` or `full`                           |
| `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|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.

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`.

Legacy `sandbox.image.workspaceMount` remains readable for migration: `private` means `trusted` + `shared-support`, while `full` means `trusted` + `full`. Fresh installs write the canonical backend-neutral settings and default to `isolated`.

## 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](/platform-adapters/) for platform-specific setup and permissions.

## 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> \| gondolin:default \| firecracker:... \| 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

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.
