Configuration

Plexi reads config.toml from the active channel profile. Alpha uses ~/.plexi-alpha/config.toml, beta uses ~/.plexi-beta/config.toml, main uses ~/.plexi/config.toml, and PR builds use ~/.plexi-pr-<N>/config.toml.

Changes take effect on the next launch unless a command says otherwise.

Channel discipline

Alpha config stays default. just install refreshes ~/.plexi-alpha/config.toml from the built-in template, and PR builds seed from alpha. Do not use alpha for personal overrides you expect to keep.

Beta config is the staging ground. ~/.plexi-beta/config.toml is not reset by alpha installs, so it is the right place to test migrations, advanced settings, and personal config before promoting a release.

PR builds are isolated. A PR build reads ~/.plexi-pr-<N>/config.toml; use the plexi-pr-<N> binary when checking a PR-specific config behavior.

Commands

plexi config edit
plexi config check
plexi config reset

config edit opens the active channel config. config check validates known keys and TOML syntax. config reset backs up the current file to config.toml.bak, then writes the built-in default template.

Reference

Top-level (config.toml)

FieldTypeDefaultDescription
config_versioninteger——
font_sizefloat——
pane_gapfloat4.0Inter-pane gap width in pixels. Default: 4.0. Clamped to [0.0, 20.0].
pane_title_font_sizefloat11.0Pane title bar font size. Default: 11.0. Clamped to [6.0, 32.0].
osc_pane_titlebooltrueApply OSC 0/1/2 title sequences from terminal processes as pane names. Defaults to true; set false to keep terminal pane names manual-only.
theme_presetstring——
confirm_quitbooltrueSet to false to quit immediately on Cmd+Q without triple-press confirmation (default: true).
confirm_closebooltrueSet to false to close panes immediately on Cmd+W without a confirmation dialog (default: true).
confirm_context_closebooltrueSet to false to close contexts immediately on Cmd+Shift+W without a confirmation dialog (default: true).
focus_history_depthinteger——

Theme ([theme])

Pick a preset and optionally override individual colors. Individual color fields override preset values when both are set.

Available presets: catppuccin-mocha, catppuccin-latte, dracula, tokyo-night, tokyo-day, gruvbox-dark, gruvbox-light, nord, solarized-dark, solarized-light, matrix, bios, plexi-night, plexi-day.

FieldTypeDefaultDescription
presetstring—Theme preset name. Applied first; individual color fields override the preset.
bg_darkeststring——
bg_sidebarstring——
bg_toolbarstring——
terminal_bgstring——
bg_hoverstring——
bg_sidebar_hoverstring——
bg_activestring——
text_primarystring——
text_dimstring——
text_sectionstring——
accentstring——
borderstring——
foregroundstring——
backgroundstring——
blackstring——
redstring——
greenstring——
yellowstring——
bluestring——
magentastring——
cyanstring——
whitestring——
bright_blackstring——
bright_redstring——
bright_greenstring——
bright_yellowstring——
bright_bluestring——
bright_magentastring——
bright_cyanstring——
bright_whitestring——
bright_foregroundstring——
pip_workingstring——
pip_idlestring——
pip_blockedstring——
pip_dimfloat——

Effects ([effects])

FieldTypeDefaultDescription
crtbool——
ghostbool——
ghost_opacityfloat0.75Opacity for unfocused panes when ghost is enabled. Range 0.0–1.0; default 0.75. Setting this implies ghost = true unless ghost = false is explicit.

Log ([log])

level accepts error, warn, info, or debug. Applies live on config save — no restart required.

FieldTypeDefaultDescription
levelstring——
retention_daysinteger——

Notifications ([notifications])

Visible notifications open the modal unless focus_mode is enabled. There is one notification level.

FieldTypeDefaultDescription
enabledbooltrueMaster switch. If false, incoming notifications are silently dropped — apps still send them, but the modal never appears and the queue stays empty. Defaults to true.
focus_modeboolfalseFocus mode. When true, no notification auto-surfaces. Everything queues silently; the user reviews via Cmd+Shift+A. Defaults to false.
soundstring—Path to an audio file (WAV, MP3, FLAC, OGG) played once when a notification arrives, so an agent waiting on input can pull a human back while Plexi is in the background. Unset (the default) means no sound; an explicitly empty string also means no sound, so a workspace overlay can set sound = "" to silence a globally configured cue. The cue only fires for a notification that may interrupt: enabled = false drops it before the cue, focus_mode = true suppresses it, and an invisible notification arrives silently.

AI ([ai])

API keys are NOT stored in config.toml. Export OPENROUTER_API_KEY in your shell profile (~/.zshrc, ~/.zprofile, etc.), or store it in the Plexi secret store:

plexi secret set openrouter-api-key --global
FieldTypeDefaultDescription
backendstring—Backend selection: "openrouter" (default), "ollama", or "local".
per_app_daily_usdfloat$1.00Per-app daily spend cap in USD. Default $1.00.
global_daily_usdfloat$10.00Global daily spend cap across all apps in USD. Default $10.00.

OpenRouter ([ai.openrouter])

FieldTypeDefaultDescription
api_key_envstringOPENROUTER_API_KEYEnvironment variable name for the API key. Default: OPENROUTER_API_KEY.
model_lowstring—Low-tier model. Concrete identifiers are backend-specific; see the example config below.
model_mediumstring—Medium-tier model. Concrete identifiers are backend-specific; see the example config below.
model_highstring—High-tier model. Concrete identifiers are backend-specific; see the example config below.

Ollama ([ai.ollama])

FieldTypeDefaultDescription
hoststringhttp://localhost:11434Ollama host URL. Default: http://localhost:11434.
model_lowstring—Low-tier model. Concrete identifiers are backend-specific; see the example config below.
model_mediumstring—Medium-tier model. Concrete identifiers are backend-specific; see the example config below.
model_highstring—High-tier model. Concrete identifiers are backend-specific; see the example config below.

Local OpenAI-compatible ([ai.local])

FieldTypeDefaultDescription
base_urlstring—Server base URL. Required — no default. e.g. “http://127.0.0.1:3456”
api_key_envstring—Environment variable name for the API key. Unset = no auth header (for local proxies that accept unauthenticated requests).
model_lowstring—Low-tier model. Concrete identifiers are backend-specific; see the example config below.
model_mediumstring—Medium-tier model. Concrete identifiers are backend-specific; see the example config below.
model_highstring—High-tier model. Concrete identifiers are backend-specific; see the example config below.

Agents ([agents])

Command templates for dispatching coding agents. {cmd} is replaced with the prompt or slash command at dispatch time.

FieldTypeDefaultDescription
lowstringclaude haiku with —dangerously-skip-permissionsFast/cheap tasks. Default: claude haiku with —dangerously-skip-permissions.
mediumstringclaude sonnet with —dangerously-skip-permissionsStandard work. Default: claude sonnet with —dangerously-skip-permissions.
highstringclaude with —dangerously-skip-permissionsComplex/autonomous tasks. Default: claude with —dangerously-skip-permissions.

Keybindings ([keybindings])

Override only the shortcuts you want to change. Unknown or conflicting overrides log a warning at startup and keep the default binding.

Modifier keys: cmd (alias: command), shift, ctrl (alias: control), alt (aliases: opt, option).

Key names: a–z, 0–9, enter, escape, tab, space, backspace, delete, up, down, left, right, open_bracket, close_bracket, backslash, slash, comma, period, equals, minus.

Each value is a string like "cmd+p" or "cmd+shift+w".

ActionDescription
quitquit
close_paneclose pane
toggle_command_palettetoggle command palette
split_horizontalsplit horizontal
split_verticalsplit vertical
split_rightsplit right
split_downsplit down
swap_pane_leftswap pane left
swap_pane_downswap pane down
swap_pane_upswap pane up
swap_pane_rightswap pane right
send_pane_leftsend pane left
send_pane_downsend pane down
send_pane_upsend pane up
send_pane_rightsend pane right
navigate_leftnavigate left
navigate_downnavigate down
navigate_upnavigate up
navigate_rightnavigate right
new_tabnew tab
next_tabnext tab
prev_tabprev tab
next_contextnext context
prev_contextprev context
move_context_upmove context up
move_context_downmove context down
nav_backnav back
focus_history_forwardfocus history forward
toggle_sidebartoggle sidebar
toggle_zoomtoggle zoom
toggle_shortcutstoggle shortcuts
rename_contextrename context
rename_panerename pane
new_contextnew context
new_page_rightnew page right
toggle_minimaptoggle minimap
scroll_upscroll up
scroll_downscroll down
increase_font_sizeincrease font size
decrease_font_sizedecrease font size
open_file_browseropen file browser
open_quick_noteopen quick note
open_configopen config
reload_configreload config
open_secrets_manageropen secrets manager
open_assistantopen assistant (beta-gated; not part of stable v1)
force_reload_appforce reload app
toggle_notification_modaltoggle notification modal
open_scratchpadopen scratchpad
push_to_subcontextpush to subcontext
new_child_contextnew child context
set_context_root_from_cwdset context root from cwd
hide_panehide pane
park_contextpark context
open_notes_pickeropen notes picker
close_contextclose context

CLI ([cli])

FieldTypeDefaultDescription
tipsbooltruePrint contextual tips after CLI commands. Default: true. Set to false to suppress.

File handlers ([file_handlers])

Choose which app opens each file type from the File Explorer. Keys are bare lowercase extensions (e.g. md, not .md). Values are handler specs:

ValueBehavior
app:<id>Open in a Plexi app by its registry id (e.g. app:text-editor)
osHand to the OS default opener (open / xdg-open)
cmd:<command>Reserved; falls back to os until wired

A handler here overrides an app’s own file_types association. Unmapped extensions fall through to manifest associations, then builtin media players, then the OS default.

Marketplace ([marketplace])

Marketplace configuration is beta-gated and is not part of stable v1. Use it only in a beta or explicit worktree channel while testing publishing flows. All fields are optional.

FieldTypeDefaultDescription
registry_urlstringofficial `plexiappOverride the catalog index URL. Default: official plexiapp.com index.
cdn_urlstringofficial `plexiappOverride the package CDN base. Default: official plexiapp.com CDN.
submit_urlstring—Publisher submission endpoint. Unset = publishing prepared locally but not uploaded (fails closed with a clear message).
account_backendstring—Account/auth backend selector. "plexi" enables plexiapp.com accounts; unset / "none" = stub (login fails closed). Selects the provider in crate::app::account::account_provider().
account_urlstringcrate::app::account::DEFAULT_ACCOUNT_URLOverride the accounts service base URL. Default: crate::app::account::DEFAULT_ACCOUNT_URL (plexiapp.com).
account_emailstring—Default email pre-filled by plexi account login. Unset = prompt.

Default config.toml

This is the built-in template Plexi writes when it creates or resets the active channel config.

# Plexi Configuration — full reference: https://plexiapp.com/docs/config | docs/CONFIG.md
# Edit: plexi config edit  |  Check: plexi config check  |  Reset: plexi config reset

config_version = 1

font_size = 14.0
# pane_gap = 4                 # inter-pane gap in pixels (0-20)
# pane_title_font_size = 12    # pane title bar font size (6-32)

confirm_quit  = true
confirm_close = false
confirm_context_close = true

# focus_history_depth = 100
osc_pane_title = true

[theme]
preset = "catppuccin-mocha"
# Presets: catppuccin-mocha, catppuccin-latte, dracula, tokyo-night, tokyo-day, gruvbox-dark, gruvbox-light, nord, solarized-dark, solarized-light, matrix, bios, plexi-night, plexi-day
# Uncomment to override individual colors:
# accent       = "#89b4fa"
# bg_darkest   = "#11111b"
# terminal_bg  = "#292a44"
# text_primary = "#cdd6f4"
# foreground   = "#e8e6ed"

[effects]
crt           = false
ghost         = true
ghost_opacity = 0.75

[notifications]
# enabled = true
# focus_mode = false
# sound = "/System/Library/Sounds/Ping.aiff"   # cue played on arrival; unset = silent

[ai]
backend = "openrouter"         # "openrouter" (cloud), "ollama", or "local" (OpenAI-compatible server)

[ai.openrouter]
api_key_env  = "OPENROUTER_API_KEY"
model_low    = "qwen/qwen3.6-flash"
model_medium = "xiaomi/mimo-v2.5"
model_high   = "anthropic/claude-fable-5"

# [ai.ollama]
# host         = "http://localhost:11434"
# model_low    = "llama3.2:3b"
# model_medium = "llama3.3:70b"
# model_high   = "qwq:32b"

# [ai.local]                   # any OpenAI-compatible server; api_key_env optional
# base_url     = "http://127.0.0.1:3456"
# model_low    = "claude-haiku-4-5"
# model_medium = "claude-opus-5"
# model_high   = "claude-fable-5"

# [log]
# level = "info"               # error | warn | info | debug — applies live on save, no restart
# retention_days = 30

# [keybindings]
# toggle_command_palette = "cmd+p"
# open_config            = "cmd+comma"
# close_context          = "cmd+shift+w"

[agents]
low    = "claude --model claude-haiku-4-5 --dangerously-skip-permissions '{cmd}'"
medium = "claude --model claude-sonnet-4-6 --dangerously-skip-permissions '{cmd}'"
high   = "claude --dangerously-skip-permissions '{cmd}'"

[cli]
tips = true

# [file_handlers]
# Choose which app opens each file type from the File Explorer (Enter / double-
# click). Keys are bare, lowercase extensions; values are `kind:value` specs:
#   "app:<id>"      open in a Plexi app by its registry id (e.g. text-editor)
#   "os"            hand to the OS default opener (open / xdg-open)
#   "cmd:<command>" reserved for a future release; falls back to "os" for now
# A handler here overrides an app's own file_types association. Unmapped types
# fall through to manifest associations, then builtin media players, then the OS.
# md   = "app:text-editor"
# txt  = "app:text-editor"
# json = "app:text-editor"
# py   = "os"

[marketplace]
# Hosted app catalog + CDN. Defaults point at the official plexiapp.com registry,
# so leave these unset to use it. Override only to point at a private registry.
# registry_url    = "https://plexiapp.com/registry/v1/index.json"
# cdn_url         = "https://plexiapp.com/registry/v1/packages"
# Publisher submission endpoint. Unset = `plexi app publish` prepares the package
# locally but does not upload it.
# submit_url      = "https://plexiapp.com/registry/v1/submit"
# Account/auth backend. Unset / "none" = login/signup fail closed. Accounts are
# only ever needed to publish or buy paid apps — free apps install without one.
# account_backend = "none"
# Accounts service host the device-flow login talks to. Defaults to plexiapp.com.
# account_url     = "https://plexiapp.com"
# Default email pre-filled by `plexi account login`.
# account_email   = "[email protected]"