Skip to content

Configuration

Cortex reads configuration from $CORTEX_HOME/config/. Messaging integrations are optional: browser and app sessions can run without a Slack or Feishu bot. Each enabled messaging platform needs its own credentials.

File hierarchy

All paths below are relative to $CORTEX_HOME (default: ~/.cortex/).

$CORTEX_HOME/
├── AGENTS.md                     # Root instructions for agent sessions (scaffolded once)
├── config/
│   ├── .env                      # Secrets, platform credentials, startup variables
│   ├── settings.json             # Runtime behavior settings (hot-reloaded)
│   ├── preferences.json          # UI language for system messages (`!lang`)
│   ├── models.json             # Default engine and favorite models
│   ├── environments/             # Execution environments — one JSON per environment
│   ├── machines.json             # Machine registry for remote clients
│   ├── budget.json               # Daily/monthly budget limits
│   ├── mcp-config*.json          # Generated MCP configurations, rewritten at every start
│   ├── board.css                 # Optional stylesheet for every board (absent by default)
│   ├── agents/                   # Your subagent roles and overrides of the built-in ones
│   ├── archive/                  # Configuration an upgrade retired, moved aside
│   └── hooks/                    # Your hook declarations and overrides of the built-in ones
├── data/
│   ├── agent-state.json          # Channel selections, model memory, new-session choice, default agent
│   ├── schedules.json            # Persistent schedule list
│   ├── boards.json               # Board registry
│   ├── executions.json           # Unified execution registry
│   ├── costs.jsonl               # 90-day rolling cost records
│   ├── provider-state.json       # Provider usage, throttle windows, resume queue
│   ├── waitpoints.json           # Armed and resolved waitpoints
│   ├── session-registry.jsonl    # Session records, channel bindings, conversation headers + turns (single owner)
│   ├── retention-candidates.json # Two-sweep orphan retention markers
│   ├── ui-sessions.json          # Browser login sessions for the Web UI
│   ├── conversation-history/     # Per-session transcript/history JSONL
│   ├── archive/                  # Terminal execution records older than a week
│   ├── claude/                   # Cortex's own Claude Code config dir (CLAUDE_CONFIG_DIR)
│   └── pi/                       # Cortex's PI agent dir
├── .claude/
│   └── settings.json             # Cortex-scaffolded Claude Code project settings
├── hooks/                        # Your hook scripts (.mjs)
├── plugins/                      # Your plugins, and your layers of the built-in ones
├── prompts/                      # Your system prompts, directives, templates
├── rules/                        # Your context rules for agent sessions
├── context/                      # Dense Context knowledge repository
│   └── projects/                 # Research project files
├── logs/                         # Daemon and backend session logs
│   ├── events/                   # Daily event-bus JSONL (the `eventLog` setting)
│   ├── sessions/                 # Claude capture logs
│   └── sessions-pi/              # PI transcript bundles
└── tmp/                          # Temporary workspaces, spooled signals, tool images

The gateway configuration, ~/.aistatus/gateway.yaml, lives outside $CORTEX_HOME; cortex setup-gateway and provider logins write it.

Loading order and precedence

  1. Built-in defaults (agent-server/defaults/) ship with the npm package and provide fallback values for every config file. The built-in plugins, hooks, rules, agent roles and system prompts are read from there in place, with your files in $CORTEX_HOME laid over them (see Built-in assets and your overrides).
  2. $CORTEX_HOME/config/.env is loaded at daemon startup via dotenv. Existing launcher environment variables take precedence over the file, and the worker inherits the daemon environment.
  3. $CORTEX_HOME/config/settings.json holds the runtime behavior settings. It is cached, re-read whenever the file changes, and each setting takes effect at its own point of use. For every key it defines, it overrides the legacy environment variable of the same setting.
  4. $CORTEX_HOME/config/models.json is cached and watched for changes to the default engine selection and favorite models.
  5. $CORTEX_HOME/.claude/settings.json is the Cortex-scaffolded Claude Code project settings file. Claude Code may also read <spawn cwd>/.claude/settings.local.json, <spawn cwd>/.claude/settings.json, and its user settings file, which for every Cortex-started claude is $CORTEX_HOME/data/claude/settings.json (see Claude Code config directory). The terminal's ~/.claude/settings.json is not read.

The .env file supports standard KEY=VALUE syntax and # comments. Environment variables already set in the shell take precedence over the .env file (dotenv default behavior).

Platform settings in the Web UI

Settings → Platform provides Feishu/Lark and Slack connection forms in the browser, desktop app and mobile app. Enable the platform, fill its required credentials and save. Secrets are write-only: a blank secret field preserves the saved value, entering a value replaces it, and Clear saved value explicitly queues its removal for the next save. Disabling a platform keeps its credentials. Credential entry requires HTTPS or a loopback API address; a native app's local webview origin does not make a remote HTTP server secure.

The configuration check lists missing required fields; it does not test the connection or account permissions. Connection changes are saved to config/.env and require a daemon restart, not just a worker restart. The page compares saved configuration with the running server environment and marks differences. If differences remain after restarting the daemon, check its launcher environment for overriding variables. Saving does not restart any process.

Each card also edits its notification destination (adminChannel for Slack, feishuAdminChannel for Feishu). These runtime settings apply to subsequent notifications. Saving an empty destination clears the route; Feishu may register a destination automatically on the next first-contact private message.

The Feishu card's Feishu skills in Web / apps switch controls settings.feishuSkillsInWeb (default false). Enabling it allows web: sessions to load an assigned cortex-feishu skill plugin, including desktop and mobile app sessions. Feishu sessions retain their normal scope; Slack and CLI sessions do not gain the plugin. Start a new session to verify. Agent plugin assignment, lark-cli login and permissions remain necessary; this setting does not change Feishu MCP loading or grant account access.

Environment variables

All values are loaded from the .env file at $CORTEX_HOME/config/.env. Every variable is optional; a platform's credentials are needed only to bring that platform online.

Server behavior settings belong in config/settings.json, where they hot-reload. A legacy variable for one of those settings is read as a deprecated fallback and moved out of .env automatically at the next daemon start (see Automatic migration out of .env).

Paths

Variable Default Purpose
CORTEX_HOME ~/.cortex/ User data root (config, context, logs, store)
CORTEX_PROJECTS_DIR <CORTEX_HOME>/context/projects/ Override project directory
CORTEX_AGENT_CWD $CORTEX_HOME Working directory of every agent backend process. Must be an absolute path to an existing directory; anything else stops the server at startup
CORTEX_REPO — The agent-server/ directory of a source checkout. The daemon watches its src/ and rebuilds, reinstalls and restarts on change (see Hot rebuild); the server update check is skipped while it is set
CORTEX_CLIENT_REPO — A client/ source directory that remote client bundles are built from instead of the packaged bundle
CORTEX_NPM_PREFIX the running install's prefix npm prefix used when Cortex installs or updates itself

Startup

Variable Default Purpose
CORTEX_MACHINE os.hostname() Machine label for startup DM
CORTEX_LANG config/preferences.json, else en Language of system-generated messages (en or zh). Outranks the preference that !lang writes
CORTEX_CLIENT_PORT 3002 WebSocket port for cortex-client manager
CORTEX_CLIENT_HOST Unspecified (all interfaces) Bind address for the cortex-client WebSocket listener only; read at server startup
CORTEX_CLIENT_TOKEN generated Shared secret that remote clients, the desktop app and the Web UI present. cortex init and the first server start generate it
CORTEX_WEBHOOK_TOKEN generated Bearer token of the loopback webhook (hook callbacks, cortex-hook ask). Generated the same way

A new Windows local App installation writes CORTEX_CLIENT_HOST=127.0.0.1 to config/.env. Existing installations, ordinary CLI init, and other platforms keep their existing defaults and configuration. For intentional LAN-direct clients, explicitly set this variable to an appropriate bind address (for example, 0.0.0.0 for all IPv4 interfaces) and restart the server. Cortex does not configure firewall policy automatically. This setting does not affect Web UI, TUI, or other server listeners.

Platform

CORTEX_PLATFORM selects the messaging platform(s). It accepts a single value (slack, feishu) or a comma-separated list to run several at once (slack,feishu), and defaults to slack. Each listed platform whose credentials are present is brought online and the others are skipped, so a server with no credentials at all runs without a chat platform and serves the Web UI, desktop app and TUI only. The TUI gateway runs alongside every platform unless CORTEX_TUI=0. With multiple platforms, messages route by platform and system notices fan out to each platform's admin channel.

Variable Required Purpose
CORTEX_PLATFORM no slack (default). Comma list for multi-platform, e.g. slack,feishu
SLACK_BOT_TOKEN for slack Slack Bot OAuth token (xoxb-...)
SLACK_SIGNING_SECRET for slack Slack app signing secret
SLACK_APP_TOKEN for slack Slack app-level token for Socket Mode (xapp-...)
FEISHU_APP_ID for feishu Feishu app ID (cli_...)
FEISHU_APP_SECRET for feishu Feishu app secret
FEISHU_ENCRYPT_KEY no Feishu event encrypt key (optional with long-connection)
FEISHU_VERIFICATION_TOKEN no Feishu event verification token (optional)
FEISHU_DOMAIN no feishu (default) or lark for the international edition
FEISHU_AUTH_MODE no bot (default) or user. cortex feishu login sets user, so Feishu document tools act as the signed-in user
FEISHU_USER_SCOPE no Scopes cortex feishu login requests when --scope is not given
FEISHU_REDIRECT_URI no Redirect URI for cortex feishu login --manual

The admin channels for system notices are settings, not environment variables: see adminChannel and feishuAdminChannel in config/settings.json. The legacy variables SLACK_ADMIN_CHANNEL, CORTEX_ADMIN_CHANNEL, and FEISHU_ADMIN_CHANNEL are still read as a deprecated fallback.

API

Variable Purpose
ANTHROPIC_API_KEY Anthropic API key used by Claude Code runs on a metered (api) route
ANTHROPIC_BASE_URL Anthropic endpoint for Claude Code runs while the local gateway is down. With the gateway healthy, each run's base URL is its gateway route instead

Web UI and TUI

The Web UI host serves the browser workbench, the desktop app and the mobile app. Browser access covers token login and Cloudflare Access in depth.

Variable Default Purpose
CORTEX_UI_HTTP off 1, true, on or yes starts the Web UI host. cortex ui enable sets it
CORTEX_UI_PORT 3004 Port of the Web UI host, bound to 127.0.0.1
CORTEX_UI_SPA_DIR the packaged web/dist Directory of the web app the host serves
CORTEX_UI_TOKEN_LOGIN on Browser login by pasting the client token. 0, false, off or no removes the login routes
CORTEX_UI_SESSION_TTL_DAYS 30 Lifetime of a browser login session, in days
CORTEX_ACCESS_TEAM_DOMAIN, CORTEX_ACCESS_AUD, CORTEX_ACCESS_CERTS_URL — Cloudflare Access JWT verification for browsers; both of the first two are needed to enable it
CORTEX_PORT_FORWARD on 0 disables the Web UI's port forwarding to remote machines
CORTEX_TUI on 0 stops the TUI gateway
CORTEX_TUI_PORT 3003 Port of the TUI gateway, also the one cortex tui connects to

Rate limiting (Slack)

Variable Default Purpose
CORTEX_SLACK_RL_GLOBAL_CAPACITY 20 Global API call bucket capacity
CORTEX_SLACK_RL_GLOBAL_REFILL_PER_SEC 1 Global refill rate per second
CORTEX_SLACK_RL_CHANNEL_CAPACITY 1 Per-channel bucket capacity
CORTEX_SLACK_RL_CHANNEL_REFILL_PER_SEC 1 Per-channel refill rate per second

Webhook

Variable Default Purpose
WEBHOOK_PORT 3001 Port of the loopback webhook server that receives hook, waitpoint, subagent and board callbacks

Data file overrides

Variable Default Purpose
CORTEX_EXECUTIONS_FILE <STORE_DIR>/executions.json Execution records
CORTEX_COSTS_FILE <STORE_DIR>/costs.jsonl Cost tracking
CORTEX_BUDGET_FILE <CONFIG_DIR>/budget.json Budget configuration
CORTEX_SESSION_TOTALS_FILE <STORE_DIR>/session-totals.json Per-session cost and token totals carried over from archived executions

<STORE_DIR> is $CORTEX_HOME/data and <CONFIG_DIR> is $CORTEX_HOME/config.

Tuning and debugging

Variable Default Purpose
DEBUG — Enable server-wide debug mode. In addition to debug-level logs, the desktop transcript shows hover inspectors for the exact agent message and each tool call's complete input/result. Only DEBUG=1 enables it; all other values disable it. Settings → Advanced has a switch for it; restart agent-server after changing it
CORTEX_DEBUG_TOOL_WARNING_CHARS 10000 In DEBUG transcripts, mark a tool-name badge amber when its formatted complete parameters plus complete result strictly exceed this Unicode character count. Positive integers only; invalid values use the default. Restart agent-server after changing it
CORTEX_BG_GRACE_S 90 Seconds a run waits for a background task the backend reported finished but never announced (see How backends work)
CORTEX_BG_WAIT_MAX_S 1800 Seconds a turn waits on still-running background work before it stops waiting; the work itself continues
CORTEX_STREAM_DELTA_MS 120 Flush interval, in milliseconds, for streamed assistant text
CORTEX_BROWSER_BINARY auto-detected Chrome or Chromium binary for the managed browser
CORTEX_BROWSER_DISPLAY auto-detected X display the managed browser uses, skipping display detection
CORTEX_GPU_MONITOR_MOCK — Mock GPU data JSON for testing (overrides real nvidia-smi queries)

The event log, tool-call rendering, user-context injection, update check, compaction notice, turn notification, and auto-resume switches are settings keys — see config/settings.json.

DEBUG persists unabridged prompts, tool parameters, and tool results in per-session conversation-history files. These values can contain secrets, private file contents, or large outputs, and storage grows with their full size. Enable this mode only on a trusted development server and turn it off when inspection is complete. Turning it off immediately removes debug fields and buttons from transcript responses, but it does not delete records captured earlier.

models.json

Located at $CORTEX_HOME/config/models.json. It stores the default engine selection and a list of favorite models. Settings → Models manages the default and favorites; the composer's full model list also lets you star or unstar models. For backend details, see backends.md.

Schema

{
  "default": {
    "backend": "claude",
    "provider": null,
    "model": "opus",
    "thinking": "high",
    "mode": "plan"
  },
  "favorites": [
    { "backend": "claude", "provider": null, "model": "opus" },
    { "backend": "pi", "provider": "deepseek", "model": "deepseek-v4-pro" }
  ]
}

Fields

Field Type Required Description
default object yes Complete engine selection used when a session or channel has none
default.backend string yes claude or pi
default.provider string or null yes null for Claude; an explicit provider id for PI, such as openai-codex, deepseek, or a custom provider
default.model string yes Non-empty model identifier, as the backend names it
default.thinking string or null yes Backend-native reasoning level; null uses the backend's default
default.mode string yes Gateway route, such as plan or api for Claude; PI uses its provider name
favorites object[] yes Ordered model references, each with backend, provider, and model. Each model identity occurs at most once

Favorites store model identities, not thinking levels or routes. The default need not be a favorite, and the favorites list may be empty. thinking accepts low/medium/high/xhigh/max on Claude and off/minimal/low/medium/high/xhigh on PI.

Selection resolution

An ordinary turn uses the session's recorded selection, then the channel's selection in data/agent-state.json, then models.json's default. An explicit run request, such as a scheduled fire, can supply a complete selection directly. Each selection contains backend, provider, model, thinking, and mode; execution environments are independent of this choice.

data/agent-state.json stores channelSelections, per-model modelMemory (thinking and mode, keyed by the JSON array [backend, provider, model]), and selectionDefault, the last Web engine choice used to seed new conversations. Selecting a model restores its remembered thinking and route. Without a memory, thinking is null; Claude uses the first Anthropic route in gateway.yaml (plan if none is declared), and PI uses its provider name. User selection changes update this memory. See per-session controls.

!backend <claude|pi> uses the configured default when it belongs to the target backend; otherwise it uses the first favorite on that backend, with its remembered settings. If neither exists, favorite a model on that backend first.

Validation rules

mode and PI provider must match ^[a-zA-Z0-9_-]+$. Claude requires a null provider. Thinking must be null or one of the backend's allowed values, and favorite identities must be unique. Selection writes validate the route against gateway.yaml when it is available. A file that parses but fails schema validation is rejected as a whole until corrected.

Execution environments

An execution environment decides how a conversation runs, apart from which model runs it: the system prompt, the built-in tool list, which plugins load, which Cortex MCP tools the session gets, and how much ambient context (rules, hooks, the project block, the backend's own skills) comes with it. Each environment is one file, config/environments/<name>.json, and the file name is its name.

Cortex ships two:

Environment What it is for
main General-purpose work on the session’s selected engine, with the built-in Cortex system, board, and Feishu plugins. It is the default: a session that has not picked an environment runs in main, and main cannot be deleted
creative A clean-room surface for writing, image prompts, naming, and copy, with the operational context stripped out (see A minimal surface)

The shipped files are copied into config/environments/ at cortex init, and again at every server start when they are missing (a sealed home, with CORTEX_CONFIG_IMMUTABLE=1, skips that start-time copy). A file you already have is never overwritten, so your edits survive upgrades, and a file you add is an environment like any other. Changes to the directory are picked up about 300 ms later without a restart. A file that is not valid JSON, or whose name does not match its file name, is skipped and logged.

Fields

Field Type Required Description
name string yes Must equal the file name without .json
description string no One line describing what the environment is for, shown in pickers
directive string no Role or identity text prepended to the prompt
systemPrompt string no Replaces the backend's default system prompt
promptTemplate string no Wraps each message; {{input}} stands for what was sent
claudeAgent string no Claude Code agent name, passed as --agent. Claude only
outputStyle string no Claude Code output style. Claude only
tools string no Comma-separated built-in tool list that replaces the backend's default set
pluginDirs string[] no Plugin directories to load, relative to $CORTEX_HOME unless absolute. They load first, in the order listed, followed by the built-in plugins
disabledPlugins string[] no Built-in plugin ids this environment leaves out. Every other built-in plugin loads, unless skills is false, in which case only pluginDirs loads
mcpComposition "direct" | "none" no direct (default) gives the session the Cortex MCP tools; none gives it no MCP at all, plugin servers included
mcpToolAllowlist string[] no Narrows the Cortex MCP tools to the names listed. Absent keeps the full surface
loadRules boolean no Default true: prepend the global rules from rules/
disableHooks boolean no Default false. true runs the session without lifecycle hooks
skills boolean no Default true. false removes the backend's skill layer, including the Skill tool
settingSources string[] no Which Claude setting files to read: any of user, project, local. Absent uses the CLI default; [] reads none
projectContext boolean no Default true: tell the session which project it belongs to on its first turn

The three prompt fields accept file:<name> instead of literal text. The text is then read from prompts/directives/, prompts/systemPrompts/, or prompts/promptTemplates/ respectively, and from the built-in prompt of that name when $CORTEX_HOME has none; the shipped main uses "systemPrompt": "file:direct.md". A field the schema does not know, a missing prompt file, or a missing plugin directory is reported as a warning, and the environment still loads.

Plugins and the Environments panel

Plugins attach to environments. An environment's sessions load the plugins its pluginDirs lists and every built-in plugin its disabledPlugins does not name; the environment's Plugins tab writes those same fields, and Settings → Plugins shows which environments use each installed plugin (see Skills and Plugins).

In the Web workbench and the desktop app, Settings → Environments lists every environment and edits one at a time: the JSON body with live validation, a Validation tab for errors and warnings, and a Plugins tab for its plugin set. A new environment starts from a skeleton that already validates, and an existing one can be duplicated under a new name. Errors block a save, warnings do not, and a save made against a file that changed on disk in the meantime is refused rather than overwriting it. Editing a shipped environment that still matches its default warns first, because from then on it stops following the shipped version. main cannot be deleted. On Android, Settings shows Environments as a desktop-only entry.

The System prompt tab edits the text an environment's systemPrompt resolves to, which replaces the backend's default system prompt as a whole. When the field is a file: reference, the tab edits that file under prompts/systemPrompts/ and names any other environment that references the same file, since a save changes the prompt for all of them. When the field is literal text or absent, saving writes the text to prompts/systemPrompts/<name>.md and points the environment at it. Empty text leaves the environment on the backend's default prompt. A prompt file that ships with Cortex shows its built-in text until you save your own version, which is written to prompts/systemPrompts/ and used in its place; Restore default loads the built-in text into the editor, and saving it removes your version. New sessions start with the saved prompt. A session already open on the PI backend switches to it on its next turn; one on Claude Code keeps its prompt until its backend process restarts.

Environments and engine selection

The engine selection decides backend, provider, model, thinking, and mode. An execution environment decides the prompt, tools, plugins, MCP surface, and ambient context. They change independently: !model <id> changes the model; !agent <name> changes the environment. !agent reset also leaves the engine selection intact.

Choosing an environment per conversation

The environment is chosen per conversation, over a host-wide default.

Command What it does
!agent Shows this conversation's environment, the host-wide default it falls back to, and the list to pick from — as buttons on Slack and Feishu
!agent <name> Runs THIS conversation in <name>, from its next turn
!agent reset Hands this conversation back to the host-wide default. clear, off, none and disable say the same thing
!agent global <name> Sets the host-wide default: what every conversation that has not chosen one runs
!agent global off Clears the host-wide default, leaving main

Every bare form is scoped to the conversation it is typed in; the host-wide default moves only behind the explicit global prefix.

In the Web app and the desktop client the same choice is the composer's own control: a chip of its own, immediately left of the engine chip — where the turn runs, before what runs it. It names the environment the conversation is in, or, when the conversation has picked none, the host-wide default it falls back to, drawn muted so a choice reads differently from a fallback. Clicking it opens one flat list: a default row meaning "follow the host-wide default", then every environment this host declares, each with what it is for. A host declaring a single environment shows no chip — there is nothing to choose between. A pick on a new conversation travels with its first message; on a live one it applies at once and takes effect on the next turn.

A turn resolves its environment through this chain: the session's own choice, then the conversation's, then the host-wide default, then main. A name that no longer resolves, because the environment was renamed or deleted, is skipped.

Switching environments leaves the backend unchanged. It takes effect on the next turn and preserves history, so the model sees turns from the previous environment; use !new for a fresh context.

A minimal surface

The shipped creative environment is the smallest one Cortex ships — writing, image prompts, naming and copy, with the operational context stripped out:

{
  "name": "creative",
  "description": "Clean-room creative environment: writing, image prompts, naming, copy. No skills, no project rules, minimal tools.",
  "promptTemplate": "{{input}}",
  "systemPrompt": "file:creative.md",
  "tools": "Read,Write,Edit",
  "pluginDirs": [],
  "skills": false,
  "settingSources": [],
  "mcpComposition": "direct",
  "mcpToolAllowlist": ["send_file", "send_view", "cortex_ask_user"],
  "loadRules": false,
  "disableHooks": true,
  "projectContext": false
}

Three built-in tools, no Cortex plugins, no ambient rules, no hooks, no project block, and an MCP surface of exactly three tools: send_file and send_view to deliver, cortex_ask_user to ask. A session opened under it reports three tools and zero skills when asked what it can see.

mcpToolAllowlist is an upper bound, not a requirement. It narrows a session's MCP surface to the names it lists; a name the session does not compose on the surface it opened on is simply absent there. One list therefore runs everywhere: this creative keeps all three tools in the Web and desktop app, and only cortex_ask_user on Slack and Feishu (their file delivery is a tool of its own, which this list does not name). A name that exists in no bundle on the host is still refused at spawn — that is a typo, not a surface.

Skills outside pluginDirs

pluginDirs is not the whole skill layer. Claude Code loads its own on every spawn no matter what Cortex passes: the plugins enabled in its setting files (installed under Cortex's Claude config directory, $CORTEX_HOME/data/claude), anything under $CORTEX_HOME/data/claude/skills/, and the CLI's built-in skills. Two environment fields reach them. settingSources: [] stops the setting files being read at all, which removes the enabled plugins' skills; skills: false removes the layer outright, built-ins included, and takes the Skill tool with it. An environment that sets both sees no skills and no slash commands.

config/settings.json

Located at $CORTEX_HOME/config/settings.json. This file holds the runtime behavior settings of the server: switches, limits and notification routes. Editing this file takes effect without a daemon restart, except for the few keys read once at startup, which the table marks.

It is a flat JSON object that stores only the keys you explicitly override. Any key you leave out falls back to its legacy environment variable when one exists, and then to the built-in default. The file itself is optional — a fresh install has none and runs entirely on defaults. It is created the first time something writes it: the startup migration below, admin-channel auto-detection, or the Web workbench settings panels.

{
  "turnNotify": false,
  "turnNotifyThresholdS": 120,
  "uiCorsOrigins": ["cortexui://localhost"]
}

Values are checked against the schema below when the file is read. A key whose JSON type does not match, or whose value is outside its documented range (the job intervals, sessionRetentionDays, piCompactReserveTokens, maxContextTokens, the shape of providerRateLimits), rejects the whole file: at startup Cortex falls back to environment variables and defaults, and on a reload it keeps the last valid settings. Either way the reason is logged. Unknown keys are ignored.

Keys

Key Type Default Effect Legacy env variable
turnNotify boolean true When a long-running turn finishes, post a fresh message to the conversation so you get a push notification (the inline status seals to "✓ Done" with an edit, which Slack and Feishu do not notify on). Both success and failure are announced CORTEX_TURN_NOTIFY
turnNotifyThresholdS number 60 Minimum turn duration, in seconds, before that completion notification is posted. Shorter turns stay quiet CORTEX_TURN_NOTIFY_THRESHOLD_S
notifyCompaction boolean true Post a chat notice when an agent's context is compacted. Covers the Claude Code (print mode) and pi backends CORTEX_NOTIFY_COMPACTION
showToolCalls boolean false Inline tool-call rendering in VirtualMessage tails CORTEX_SHOW_TOOL_CALLS
statusNewqButton boolean false Show the "New (quiet)" button on status messages (=!newq, which skips the pre-close hook) CORTEX_STATUS_NEWQ_BUTTON
autoResume boolean true When a usage-limit window resets, automatically continue the conversations the limit interrupted, injecting a note to pick up where they left off. Set to false to leave interrupted work paused for manual continuation CORTEX_AUTO_RESUME
providerRateLimits object {} Per-provider, per-quota-window throttle policy. Provider entries contain a windows array of { type, label?, enabled, threshold? }; threshold is a ratio greater than 0 and at most 1 —
streamDeltas boolean true Stream assistant text token by token. Disable to deliver each assistant message in one piece CORTEX_STREAM_DELTAS
bgContinuation boolean true Forward the output of background tasks back into the conversation when they finish CORTEX_BG_CONTINUATION
eventLog boolean true Write the event bus to the daily rolling JSONL event log CORTEX_EVENT_LOG
diskMonitor boolean true Check free space on the filesystem containing $CORTEX_HOME every five minutes and send a system notice below 500 MiB. false stops the timer; switching back to true runs an immediate check CORTEX_DISK_MONITOR
disableUserContext boolean false Set to true to stop injecting USER.md context into direct conversation turns (injected by default) CORTEX_DISABLE_USER_CONTEXT
serverUpdateDisable boolean false Set to true to disable the server auto-update check (enabled by default) CORTEX_SERVER_UPDATE_DISABLE
serverAutoUpdate boolean true When the update check finds a newer server release, install it without asking. Installation starts only once no turn is running, and the server restarts right after. false asks for confirmation first; an agreed update waits for idle the same way. A skipped version stays skipped —
clientHotReloadEnabled boolean true Push the server's client bundle to connecting remote clients that run a different one, so they update themselves. Read at startup —
feishuSkillsInWeb boolean false Let web: sessions, including desktop and mobile app sessions, load an assigned cortex-feishu skill plugin (see Platform settings in the Web UI) —
teamEnabled boolean true Experimental team mode: any web session can start and manage persistent worker sessions with the worker_* tools. Turning it off removes those tools from the next turn and leaves existing workers as ordinary sessions. See team.md CORTEX_TEAM_ENABLED
waitpointSweepMs number 30000 Interval, in milliseconds, of the waitpoint sweep: it drains spooled signals (locally and from connected devices), expires overdue waitpoints, and retries deliveries that failed. See waitpoints.md CORTEX_WAITPOINT_SWEEP_MS
waitpointTtlMs number 604800000 Default lifetime of a waitpoint (7 days) when the agent does not set one. A waitpoint may ask for less, or for more up to a hard ceiling of 30 days; past it the waitpoint expires and the session is told nothing arrived CORTEX_WAITPOINT_TTL_MS
waitpointMaxWakesPerHour number 12 Cap on how many times a single waitpoint may wake its session per hour. A runaway signalling loop is throttled (and says so once) rather than flooding the conversation CORTEX_WAITPOINT_MAX_WAKES_PER_HOUR
injectWaitMaxS number 600 Upper bound, in seconds, on how long an injected mid-turn message waits for a reply before the busy gate is released. Prevents a wedged process from holding the daemon restart gate forever CORTEX_INJECT_WAIT_MAX_S
anthropicSubscriptionModes string[] ["plan"] Gateway modes whose Anthropic account quota is polled for the Usage screens. [] turns the polling off —
subscriptionBillingModes string[] ["plan", "openai-codex", "google-gemini-cli", "google-antigravity"] Gateway modes paid by a subscription. The gateway still prices their requests, but usage rows for these modes show quota only and no cost —
providerUsageCollectionEnabled boolean true Run the built-in provider-usage collection job that refreshes quota and spend readings —
providerUsageCollectionIntervalMs number 300000 Interval between provider-usage collections, in integer milliseconds —
storeArchiveEnabled boolean true Once a day, move terminal execution records older than a week from data/executions.json to data/archive/. Read at startup —
memoryIndexRegenEnabled boolean true Run the built-in experiment/knowledge/pattern index rebuild. Switching from false to true runs it immediately —
memoryIndexRegenIntervalMs number 86400000 Interval between memory-index rebuilds, in integer milliseconds —
sessionRetentionDays number 30 Whole safe-integer retention window in days (valid range 1 through 104249991). The retention coordinator starts a sweep in the background at daemon startup (boot does not wait for it), runs every 6 hours, and runs again after a hot-reload change; it prunes expired session-registry entries, orphan conversation-history, orphan PI transcript bundles, orphan Claude capture logs, orphan tool-image directories, and syncs Claude user cleanupPeriodDays, while skipping active sessions/captures —
piCompactReserveTokens number 16384 PI backend only: the context headroom PI keeps in reserve, in tokens. PI compacts once the context passes contextWindow - reserveTokens — both between turns and, since PI 0.84.4, inside a running turn at a tool-batch boundary — so a larger reserve compacts earlier and leaves more working room in a long tool-driven turn. A whole number from 1024 through 131072. Cortex mirrors it into the private PI agent dir's settings.json, which is the only way in: PI exposes no setter for it CORTEX_PI_COMPACT_RESERVE_TOKENS
maxContextTokens number | null null Cap on every model's usable context window, in tokens, for both backends: the usable window is the smaller of the model's own window and this value, and each backend compacts before reaching it. A whole number from 100000 through 1000000 (Claude Code's own accepted range), or null to leave every model at its own window. Claude receives it as CLAUDE_CODE_AUTO_COMPACT_WINDOW, which outranks any autoCompactWindow in Claude's settings files; PI compacts at cap - piCompactReserveTokens once a model's window exceeds the cap. Context-usage percentages use the capped window —
uiCorsOrigins string[] [] Origins that receive CORS headers from the Web UI HTTP host. See desktop-app.md CORTEX_UI_CORS_ORIGINS (comma-separated)
adminChannel string | null null Slack channel for system notices (startup, rate-limit, disk alerts). The first DM to the bot is auto-detected and persisted here SLACK_ADMIN_CHANNEL, then CORTEX_ADMIN_CHANNEL
feishuAdminChannel string | null null Feishu admin chat_id (oc_...) for the same notices. Independent of adminChannel — Slack channel ids are not usable on Feishu FEISHU_ADMIN_CHANNEL

The Web workbench writes a subset of these from Settings → Notifications (turnNotify, autoResume, notifyCompaction), Settings → Advanced (eventLog, diskMonitor, showToolCalls, disableUserContext, serverUpdateDisable, serverAutoUpdate, teamEnabled, sessionRetentionDays, maxContextTokens, piCompactReserveTokens, memoryIndexRegenEnabled and memoryIndexRegenIntervalMs), Settings → Platform (adminChannel, feishuAdminChannel, feishuSkillsInWeb), and the desktop/mobile Usage screens (providerRateLimits). Every other key is edited by hand in the file.

providerRateLimits is a settings.json object keyed by provider id. Each provider entry can contain a windows array whose entries are { type, label?, enabled, threshold? }. The identity of an ordinary quota row is its provider-reported type; a model-scoped row uses both type and its exact provider-reported label. An exact window entry takes precedence over older provider-level enabled/threshold fields. Those root fields remain a backward-compatible fallback and, when non-default, appear in Usage with an action to clear them.

Desktop and mobile Usage place separate controls on every displayed 5-hour, weekly, weekly-overage, Codex primary/secondary, and labeled model row. Leaving threshold out uses the row default: 0.95 for seven_day and seven_day_overage_included, 0.90 for other rows. Spend-only cards have no quota controls. Model policy identity follows the reported display label; if a provider renames that label, the old entry stops matching. Policy edits apply only to future quota observations; an already-active quota or outage window keeps its current state until its reset time or a manual Resume now clear. The Codex and Anthropic cards also show the account's banked resets and a confirmed Reset action; see Account usage and banked resets.

Built-in job intervals must be integer milliseconds from 1000 through 2147483647, the safe range for Node timers. Enabled jobs run once at daemon startup. Memory-index runs never overlap; a changed interval takes effect after an active run finishes.

sessionRetentionDays is the one numeric retention control in Settings → Advanced. The desktop Web UI writes it through the same config.set { section: 'settings' } path as the boolean runtime toggles, and the client enforces the same upper bound the server validates. piCompactReserveTokens sits in the same card and takes the same path; the client accepts 1024–131072, exactly what the server validates. Cortex writes it into PI's private settings.json when a PI session is created, so a new value reaches the next PI session without a restart — sessions already pooled keep the reserve they were built with.

maxContextTokens sits in the same card. Its field accepts 100000–1000000; clearing the field saves null, which leaves every model at its own window. PI reads the cap each time it decides whether to compact, so running PI sessions follow a new value at once. Claude takes it when its CLI process starts, so it reaches the next Claude session; a pinned CLI compatibility trial never receives it.

The retention coordinator has six housekeeping surfaces on that clock: live session-registry expiry (via delete-intent then delete-commit), orphan data/conversation-history/*.jsonl, orphan PI transcript bundles under logs/sessions-pi/, orphan Claude capture files under logs/sessions/, orphan per-session image directories under tmp/tool-images/, and the synced Claude user cleanupPeriodDays helper. An image directory goes once its session is neither live nor pending deletion and nothing in it changed inside the retention window. Orphan history and PI files use a two-sweep confirmation before deletion. Active direct sessions, running executions, pending interaction/bg-held sessions, live PI backend session ids, and active Claude capture pairs are protected.

Registry expiry writes the delete intents for every expired session in one journal append, then cleans up and commits them in batches of 100 sessions; each batch costs three registry appends however many sessions it holds. A daemon shutdown stops a running sweep between batches. Sessions whose deletion was not committed stay pending, and the next sweep finishes them first.

Hot reload

The config directory is watched. A change to settings.json is debounced for 300 ms, the file is re-read, and the new values are used from then on — no daemon restart. Two consequences worth knowing:

  • Each setting takes effect at its own point of use: the next turn, the next agent spawn, the next built-in job run, or the next HTTP request. uiCorsOrigins is resolved per request; adminChannel and feishuAdminChannel are pushed into the running platform adapter as soon as they change. diskMonitor=false stops its timer, while restoring true starts the timer and checks immediately. Each built-in job switch likewise cancels or starts its timer immediately, and interval edits reschedule its next idle run.
  • A broken file never takes the server down. Invalid JSON or a type mismatch leaves the previous settings in place and logs the error; fix the file and the next write reloads it.

Legacy environment variables and deprecation

Keys with a legacy environment variable in the table keep it as a fallback, parsed with its established semantics — CORTEX_EVENT_LOG=off, CORTEX_TURN_NOTIFY=0/false/off/no, CORTEX_NOTIFY_COMPACTION=0/false/off/no, a comma-separated list for CORTEX_UI_CORS_ORIGINS, and so on. Precedence is always: key in settings.json → legacy variable → built-in default. For adminChannel the original chain is preserved: SLACK_ADMIN_CHANNEL is consulted before CORTEX_ADMIN_CHANNEL.

The first time a legacy variable supplies a value, the daemon logs a deprecation warning naming the variable and the setting it feeds (Deprecated env <VAR> supplies settings.<key>; move it to settings.json). The fallback exists for compatibility during migration — new configuration belongs in settings.json.

Automatic migration out of .env

On every daemon start, right after .env is loaded, Cortex scans $CORTEX_HOME/config/.env for the legacy variables listed above. If it finds at least one:

  1. .env is copied to .env.bak-<timestamp> in the same directory, where <timestamp> is an ISO 8601 instant with : and . replaced by - (for example .env.bak-2026-07-30T09-12-33-482Z).
  2. Each legacy value is parsed with its old semantics and written into settings.json. A key already present in settings.json is never overwritten — the file wins over .env.
  3. The migrated assignments are removed from .env, which keeps its original file permissions and gains the header comment # Legacy server settings migrated to settings.json; secrets remain in .env.
  4. The dead variable CORTEX_SERVER_UPDATE_ENABLE, which nothing reads any more, is dropped in the same pass.

The migration is idempotent: a .env with no legacy variables is left untouched, and later starts do nothing. If any step fails, the error is logged, .env keeps its variables, and the server keeps running on whichever source supplies each value — so a failed migration costs nothing but the deprecation warnings.

What stays in .env

Three categories of variables deliberately remain in .env:

  • Secrets and credentials — SLACK_BOT_TOKEN, SLACK_SIGNING_SECRET, SLACK_APP_TOKEN, FEISHU_APP_ID, FEISHU_APP_SECRET, ANTHROPIC_API_KEY, CORTEX_CLIENT_TOKEN, CORTEX_WEBHOOK_TOKEN. They are settings in a loose sense, but keeping them out of a file that the Web UI can read and write keeps the blast radius small.
  • Variables consumed by child processes — CORTEX_HOME, CORTEX_PROJECTS_DIR, WEBHOOK_PORT, DEBUG, CORTEX_LANG, and the data file overrides. Hooks, MCP servers, the CLI, and cortex-client inherit the daemon's environment; a server-side JSON file would never reach them.
  • Startup topology — CORTEX_PLATFORM, CORTEX_MACHINE, CORTEX_UI_HTTP, CORTEX_UI_PORT, CORTEX_UI_SPA_DIR, CORTEX_UI_TOKEN_LOGIN. These decide which adapters and listeners exist at boot, so hot-reloading them would have no meaning; they require a restart by construction.

Not the same file as .claude/settings.json

$CORTEX_HOME/config/settings.json (this file) configures the Cortex server. $CORTEX_HOME/.claude/settings.json configures Claude Code's hooks and permissions and is read by the Claude CLI, not by Cortex. Same file name, different directory, different owner, different schema — see the next section.

.claude/settings.json (Claude Code)

Located at $CORTEX_HOME/.claude/settings.json. This is the Cortex-scaffolded Claude Code project settings file under the Cortex data root. cortex init seeds it from defaults/.claude/settings.json once and Cortex never overwrites that path on later runs.

Claude Code itself can also read higher-precedence project files in the spawn cwd (.claude/settings.local.json, then .claude/settings.json) and its user settings file in Cortex's Claude config directory (next section). Cortex does not scaffold, sync, or overwrite those project-local cwd files at all; "never overwrite" here applies only to the $CORTEX_HOME/.claude/settings.json seed path above.

The file follows Claude Code's settings format with hooks and permissions sections. See hooks.md for the hook system documentation. It has nothing to do with the Cortex runtime settings in config/settings.json.

Claude Code config directory

Every claude process Cortex starts — session engines, subagent children, and the claude auth login, status and logout commands behind the login flow — runs with CLAUDE_CONFIG_DIR=$CORTEX_HOME/data/claude, whatever the daemon's own environment holds. It is Cortex's private Claude Code home, the counterpart of PI's $CORTEX_HOME/data/pi: transcripts (projects/<encoded cwd>/<id>.jsonl), the user settings file, user- and local-scope plugins, .claude.json and the login all live there. A terminal claude working from ~/.claude and Cortex never see each other's sessions, settings or plugins. The claude binary itself still comes from PATH.

Login. The directory has a login of its own; Cortex never copies or links the terminal's .credentials.json, because each OAuth refresh rotates the token and two copies would invalidate each other. Sign in from Settings → Accounts, with !login cc in chat, or with cortex auth login --backend claude (see Remote login). On macOS the CLI keeps the login in the keychain item Claude Code-credentials-<first 8 hex of sha256(config dir)>.

settings.json. The user settings file belongs to Cortex. When it does not exist, the server creates it at startup from the model, effortLevel, modelSettings, autoCompactWindow, autoMemoryEnabled, env and attribution keys of ~/.claude/settings.json; once it exists the terminal file is never read again. The session-retention coordinator keeps one key in it current — cleanupPeriodDays, from the sessionRetentionDays runtime setting — and leaves every other key as it is. Edit the file directly to change what Cortex's Claude sessions use.

Plugins. Plugins Cortex's Claude sessions should load are installed into this directory:

cd ~/.cortex
CLAUDE_CONFIG_DIR=$HOME/.cortex/data/claude claude plugin install <plugin>@<marketplace> --scope project

Project- and local-scope installs record the plugin in $CORTEX_HOME/.claude/settings*.json and the plugin files under $CORTEX_HOME/data/claude/plugins/.

Sessions from ~/.claude. At startup the server moves, once, every file a live Claude session in the session registry owns under ~/.claude/projects — its transcript, its turn backups and its sidecar directory — to the same path under $CORTEX_HOME/data/claude/projects, so those sessions keep resuming. A destination that already exists is never overwritten, and files no registry session owns stay where they are.

Boards

A board is the status page of a long task, kept by the agents working on it (see Boards in the team guide). Each board lives in <project context>/boards/<slug>/, and data/boards.json lists every board with its project, title and when it was last used. On upgrade, registered commissions from earlier Cortex releases become boards: their directories move from commissions/ to boards/, and their files are moved, never deleted.

Boards are styled by a preset stylesheet that ships with Cortex in agent-server/defaults/board/board.css. It is read from the package and never copied into $CORTEX_HOME, so every upgrade brings its current version. The preset uses only the app's theme variables (--proto-*, --material-*, --border-card, --r-*), so boards follow light and dark mode, the palette and the accent.

To restyle every board, create $CORTEX_HOME/config/board.css. The file does not exist by default. It loads after the preset, so it can redefine any variable, the preset's own --b-* or a theme --proto-*, or override any rule:

:root { --proto-accent: #0f766e; --b-gap: 16px; }
.card { border-radius: 4px; }

Open boards pick up a save within a few seconds, and deleting the file restores the preset.

Built-in assets and your overrides

Cortex's plugins, hooks, rules, subagent roles and system prompts ship in the install's agent-server/defaults/ and are read from there at runtime. They are never copied into $CORTEX_HOME, so they always match the running version, and $CORTEX_HOME holds only your own files. A file of yours at the matching place replaces the built-in one, a new name adds to them, and deleting your file brings the built-in one back.

Asset Built in Yours How yours combine with the built-in ones
Plugins defaults/plugins/<id>/ plugins/<id>/ Same id: your skills replace or add to the built-in ones and your mcp.json replaces the built-in one; the manifest stays built in. Any other id is a plugin of its own. See Built-in plugins
Hook declarations defaults/config/hooks/*.json config/hooks/*.json Same file name: each top-level field it sets replaces that field of the built-in declaration as a whole, so { "enabled": false } switches a built-in hook off. See the hook registry
Hook scripts defaults/hooks/*.mjs hooks/*.mjs Same file name: replaces the built-in script
Rules defaults/rules/*.md rules/*.md Same file name: replaces the built-in rule
Subagent roles defaults/agents/*.md config/agents/*.md Same file name: replaces the built-in role
System prompts defaults/prompts/systemPrompts/*.md prompts/systemPrompts/*.md Same file name: replaces the built-in prompt

Rules are Markdown files for agent sessions. A rule without paths frontmatter is prepended to every session whose environment loads rules (loadRules); a rule whose paths globs match a file the session reads is injected then by the rules-loader hook, once per session.

Settings edits — a skill, a hook switch, a system prompt — are written to $CORTEX_HOME in the same way, and Cortex never writes to the install. A sealed home (CORTEX_CONFIG_IMMUTABLE=1, as in a benchmark trial) declares its whole surface itself and uses none of the built-in plugins, hooks or rules.

A home set up by a release that kept copies of these files in $CORTEX_HOME is converted on its first start: copies identical to the shipped files are removed, plugin and hook copies that differ are moved to config/archive/builtin-overlay-<timestamp>/, edited rules, roles and prompts stay as your overrides, a switched-off built-in hook keeps its switch, and each environment keeps loading the same plugins.

defaults/ layout

The agent-server/defaults/ directory in the npm package contains the shipped defaults. The scaffold files are copied to $CORTEX_HOME/ during init, and the execution environments are also merged at every server start; the built-in assets are read in place:

Source Destination Overwrite behavior
defaults/AGENTS.md $CORTEX_HOME/AGENTS.md Never
defaults/gitignore $CORTEX_HOME/.gitignore Never
defaults/.claude/settings.json $CORTEX_HOME/.claude/settings.json Never — this applies only to the $CORTEX_HOME/.claude/settings.json scaffolded path; arbitrary repository-local .claude/settings.json files are outside Cortex's copy/sync loop
defaults/config/budget.json $CORTEX_HOME/config/budget.json Only with --force
defaults/config/environments/ $CORTEX_HOME/config/environments/ Per-file copy-if-missing, at init and again at every server start: a shipped environment you do not have yet is added; a file you already have is never overwritten — --force does not apply to this tree
defaults/data/schedules.json $CORTEX_HOME/data/schedules.json Never (unless --force)
defaults/context/ $CORTEX_HOME/context/ Index AGENTS.md files: never overwritten. The decisions/ scaffold: only with --force
defaults/plugins/, defaults/config/hooks/, defaults/hooks/, defaults/rules/, defaults/agents/, defaults/prompts/ — Not copied: read in place, with your files laid over them (see Built-in assets and your overrides)
defaults/board/ — Not copied: the board preset stylesheet and the scaffold of a new board are read from the package

Init also generates config/machines.json, config/preferences.json and data/agent-state.json when they are missing (or with --force), writes the config/mcp-config*.json files, and sets up the gateway configuration and models.json (see cortex init). The server rewrites the mcp-config*.json files at every start, so edits to them do not persist.

An upgrade therefore brings the current built-in plugins, hooks, rules, roles and prompts with it, and newly shipped environments reach existing installs without touching your edits. Whole-file config such as budget.json requires --force to replace.

Hot-reload behavior

Watcher-backed files under config/ automatically switch to filesystem snapshot polling every 5 seconds if the watcher cannot start or reports a runtime error. Each loader keeps the same validation and failure behavior in polling mode as it uses with filesystem events.

  • config/settings.json — watched via file watcher, debounced 300 ms. New values apply at the next point of use without a restart; a broken file keeps the previous settings. See config/settings.json.
  • schedules.json — watched via file watcher. Changes are picked up within seconds without restart. See scheduling.md for the full scheduling system.
  • models.json — cached and watched for parseable JSON changes. An update refreshes the cache before the next agent spawn; malformed JSON keeps the previous model configuration.
  • machines.json — watched and reloaded after valid changes. Invalid entries keep the previous machine registry.
  • environments/ — watched. Changes are debounced (300 ms) and every environment is reloaded without a restart. See Execution environments.
  • .env — requires a daemon restart to pick up changes (loaded once at startup via dotenv). Behavior settings live in config/settings.json, which does not.
  • Hook declarations (config/hooks/*.json) — the registry is re-read on every agent spawn, so a new agent:* / cc:* / pi:* entry applies to the next agent that starts. cortex:* entries are snapshotted at server start and need a restart. See hooks.md.
  • Hook scripts — read fresh on every hook invocation.
  • Plugins, prompts, rules, subagent roles — read fresh on each agent session spawn.
  • config/board.css — watched together with every board's directory. Open boards re-render within a few seconds of a change. See Boards.

Where each file lives

File Purpose Path
.env Environment variables $CORTEX_HOME/config/.env
settings.json Runtime behavior settings (hot-reloaded) $CORTEX_HOME/config/settings.json
models.json Default engine and favorite models $CORTEX_HOME/config/models.json
environments/ Execution environments — one JSON per environment $CORTEX_HOME/config/environments/
machines.json Machine registry $CORTEX_HOME/config/machines.json
budget.json Budget limits $CORTEX_HOME/config/budget.json
preferences.json UI language for system messages $CORTEX_HOME/config/preferences.json
mcp-config*.json Generated MCP server configs (rewritten at every start) $CORTEX_HOME/config/
.claude/settings.json Claude Code hooks/permissions (not the Cortex settings file) $CORTEX_HOME/.claude/settings.json
agent-state.json Channel selections, model memory, new-session choice, default agent $CORTEX_HOME/data/agent-state.json
schedules.json Schedules $CORTEX_HOME/data/schedules.json
boards.json Board registry $CORTEX_HOME/data/boards.json
provider-state.json Provider usage, throttle windows, resume queue $CORTEX_HOME/data/provider-state.json
waitpoints.json Waitpoints $CORTEX_HOME/data/waitpoints.json
board.css Optional stylesheet for every board $CORTEX_HOME/config/board.css
session-registry.jsonl Append-only session registry journal $CORTEX_HOME/data/session-registry.jsonl
retention-candidates.json Two-sweep orphan cleanup candidates $CORTEX_HOME/data/retention-candidates.json
conversation-history/ Per-session transcript/history JSONL $CORTEX_HOME/data/conversation-history/
logs/sessions/ Claude capture logs $CORTEX_HOME/logs/sessions/
logs/sessions-pi/ PI transcript bundles $CORTEX_HOME/logs/sessions-pi/
hooks/*.json Your hook declarations and overrides $CORTEX_HOME/config/hooks/