| title | Configuration | |||||||
|---|---|---|---|---|---|---|---|---|
| status | current | |||||||
| version | 0.2.0 | |||||||
| last_updated | 2026-09-16 | |||||||
| last_verified | 2026-09-16 | |||||||
| source_refs |
|
|||||||
| owner | @rfluid | |||||||
| tags |
|
Aura's configuration is layered: typed Rust structs are the source of truth, a
field registry documents and validates them, three on-disk files persist the
values, and a small runtime layer keeps the running tray app and modal in sync
as those files change. This page documents every field and how the layers fit
together. To add or change a config field as a developer, see
.agent/skills/add-or-change-config.md,
which builds on this reference.
Most users should configure Aura with the CLI first, then use the config file for fine tuning.
-
Run the setup wizard to detect installed agents and create/update the file:
aura config setup
-
Use the interactive configuration wizard when you want to walk every supported setting without memorizing key names:
aura config wizard
-
Use direct CLI edits for one setting at a time. The CLI validates values and rewrites the file with the inline comments preserved:
aura config set window.anchor bottom aura config set tray.progress true aura config set content.default_period 7d
-
Open the config file when you want to edit multiple values together:
aura config edit
You can also right-click the tray icon and choose Open config file, or open Aura and use the settings button.
-
Keep this guide handy from the app: right-click the tray icon and choose Configuration guide, or use the ... menu in the modal.
The generated config.toml starts with a link back to this tutorial and then
documents each field above the value it controls. Repeatable [[agents]] and
[[plugins]] blocks are ordinary TOML arrays of tables; scalar settings live
under [window], [tray], [content], and [update].
| File | Path | What it holds | Edited by |
|---|---|---|---|
| Config | ~/.config/aura/config.toml |
Agents, plugins, [window], [tray], [content], [update] |
You (CLI / editor) |
| Theme | ~/.config/aura/theme.toml |
Color / font / spinner overrides | You (CLI / editor) |
| State | ~/.local/share/aura/state.json |
Active profile selection | Aura (do not hand-edit) |
| Plugins dir | ~/.config/aura/plugins/ |
Auto-discovered plugin binaries | aura plugin add |
Paths follow the XDG base-dir spec via the dirs crate, so the exact location
differs on macOS / Windows — always resolve it with aura config path. Aura
writes a fully-commented default config.toml on first run if none exists.
Config flows through five layers, top (authoring) to bottom (consumption):
-
Typed structs —
crates/aura-core/src/config.rs.AppConfigis the root (agents,plugins,window,tray,content,update); each sub-struct derivesSerialize/Deserializeand aDefault, so the whole tree round-trips through TOML and an empty/partial file still parses (missing fields fall back toDefault). This is the source of truth — the shape of a config is whatever these structs say it is. -
Field registry / schema —
crates/aura-core/src/config_schema.rs. A flat list ofFieldDescriptors (one per settable scalar in theconfig_schema::SECTIONStables) plusSectionFields describing the repeatable[[agents]]/[[plugins]]tables. This registry powers everything self-documenting:config describe,get/setvalidation, thewizard, and the#-commentedconfig.tomltemplate (render_commented). A unit test (registry_covers_every_field) serializes a default config and asserts every leaf key has a descriptor — so the docs cannot drift from the structs without breaking the build. -
Persistence (disk) — the four files above.
config.tomlis always written throughrender_commented, so every key carries a#comment lifted from the registry; those comments survive programmatic edits (set,wizard,setup). -
Load + merge —
AppConfig::loadreads the file (writing defaults if absent) and runs it throughconfig_migrate::normalizefirst, so a config written against an older section layout parses as the current one;load_with_discoveryadditionally merges executable plugins found in the plugins dir (config-listed entries win on name collision) and appliescontent.plugin_order;run_setupdetects installed agents and merges new ones without disturbing existing edits. See Migrating an older config. -
Runtime mirror —
crates/aura/src/runtime.rs. The tray poll loop inmain.rsand the modal's async refresh task inapp.rseach reload the config independently. To stop them drifting, a handful of[window]fields are mirrored into process atomics viaruntime::set_from_config, and any platform state they drive (e.g. the macOS NSApp activation policy) is reapplied there. Add an atomic + accessor here when a new knob must be visible to both the background loop and the modal.
Most edits take effect without a restart. set_from_config is called, and
the config (and theme.toml) reloaded, at three moments:
- Startup —
main.rsloads once before launching GPUI. - Every tray click — the
Showarm reloads viaload_with_discovery, so a config edit (or a freshly-dropped plugin binary) is live on the next open. - Refresh button —
app::do_refreshreloads config + theme on a background thread; a malformedtheme.tomllogs a warning and falls back to defaults rather than blanking the UI.
A failed reload falls back to the last good in-memory snapshot, so a transient I/O error never breaks the toggle.
- Scalar fields: struct
Default(incl. the per-OSdefault_anchor) → value inconfig.toml. - Plugins: a
[[plugins]]block inconfig.tomloverrides an auto-discovered binary of the samename(case-insensitive) — that's how you pin a color/icon onto a discovered plugin. - Agent accent color:
[agents."<name>"].accentintheme.toml→[[agents]] colorinconfig.toml→ per-kind brand default → luminance fallback (see Themes).
Prefer the CLI over editing the file by hand — it validates values, suggests near-miss keys, and keeps the inline docs intact.
aura config setup # detect installed agents, write/update config.toml
aura config path # print resolved config path
aura config show # print loaded config (--format text|json)
aura config describe [<key>] # list every field (type/default/docs), or explain one
# (--format json emits the full schema)
aura config get <key> # print a single field's current value
aura config set <key> <value> # validate and set one field (e.g. set window.anchor top)
aura config wizard # walk every field interactively; blank keeps current
aura config init [--force] # write a fresh, fully-commented config.toml
aura config document # rewrite the existing config in place with inline docs
aura config edit # open in $EDITOR (creates defaults if missing)
aura config validate # parse-check
Keys are dotted paths into [window] / [tray] / [content] / [update],
e.g. window.anchor, window.max_height, update.dismiss_all. A key from an
older layout (display.anchor) still resolves — get, set and describe
answer with its current name and print a note. set rejects bad enums/booleans and
suggests near-miss keys; pass none (or empty) to clear an optional field. The
repeatable [[agents]] / [[plugins]] tables are documented by describe
but edited via aura config edit, aura agents, or aura plugin — they
are not get/set targets. The legacy aura setup-config is a hidden alias for
aura config setup. See docs/cli.md for the full surface.
Where the modal sits, how big it gets, and what kind of window it is.
| Key | Type | Allowed | Default | Summary |
|---|---|---|---|---|
anchor |
string | none | bottom | top |
none (macOS/Linux), bottom (Windows) |
How the modal anchors as it auto-fits height. |
linux_backend |
string | auto | x11 | wayland |
auto |
Which display server GPUI talks to on Linux/BSD. Ignored elsewhere. |
show_in_app_switcher |
bool | true | false |
false |
Show the modal in Alt+Tab / Cmd+Tab / dock surfaces. |
dismiss_on_focus_loss |
bool | true | false |
true |
Auto-close the modal when it loses focus. |
chrome |
bool | true | false |
false |
Show the native window title bar (independent of auto_resize). |
auto_resize |
bool? | true | false |
unset (auto-fit) | Auto-resize the modal to fit its content height. false = fixed-size. Works with or without chrome. |
max_height |
u32? | — | unset | Upper bound (logical px) on auto-fit height; ignored when auto_resize is false. |
linux_backend lives here rather than in a platform section because it exists
entirely to decide whether Aura can place its own window — on a native Wayland
surface anchor has no effect at all. See
Linux display backend.
The icon by the clock. None of this reaches the modal.
| Key | Type | Allowed | Default | Summary |
|---|---|---|---|---|
indicator |
bool | true | false |
true |
Whether the icon reports quota, or is just a button that opens the modal. Master switch for the three below. |
progress |
bool | true | false |
true |
Fill the tray icon's ring in proportion to peak quota usage. |
color |
bool | true | false |
true |
Move the tray icon through the purple/yellow/orange/red usage ramp. |
pulse |
bool | true | false |
false |
Ask the desktop to emphasize the tray icon at 90% usage. Effective on Linux SNI hosts. |
refresh_secs |
u64 | — | 1200 |
Seconds between background refreshes of the indicator; clamped up to 30. |
What the modal renders, as opposed to where the window sits.
| Key | Type | Allowed | Default | Summary |
|---|---|---|---|---|
default_period |
string | all | 7d | 30d |
all |
Usage period tab selected on open. |
plugin_order |
string[] | — | [] |
Display order for plugin pills (comma-separated names on set). |
goblin_mode |
bool | true | false |
false |
Swap UI copy for the aggressive "Goblin Mode" variant. |
Controls the "Update available" header button.
| Key | Type | Allowed | Default | Summary |
|---|---|---|---|---|
dismissed_version |
string? | — | unset | Last release dismissed via the button's ×; a newer release re-shows it. |
dismiss_all |
bool | true | false |
false |
Master mute: never render the button or fire the GitHub check. |
| Field | Type | Allowed | Summary |
|---|---|---|---|
name |
string | — | Display name for this agent profile. |
kind |
string | claude-code | codex | gemini |
Which agent this profile reads. |
config_path |
string? | — | Agent config dir; defaults to ~/.claude, ~/.codex, ~/.gemini per kind. |
color |
string? | — | Accent color override, hex like #rrggbb or #rgb. |
tray_progress_source |
u32? | — | Quota window that fills the tray ring, by position. Unset = 0, the session. |
tray_color_source |
u32? | — | Quota window that drives the tray color ramp, by position. Unset = 1, the week. |
The two tray_*_source selectors point the halves of the tray indicator at
different quota windows. Out of the box the ring is the session you're in and
the color is the week you're spending; set them to repoint either half:
[[agents]]
name = "Claude Code"
kind = "claude-code"
tray_progress_source = 0 # ring ← Current session (the default)
tray_color_source = 1 # color ← Current week, all models (the default)They live on the agent rather than under [tray] because the positions
index that agent's own window list: position 1 is Claude's all-models week and
Codex's weekly limit, and a Gemini profile reports no percentages at all.
Backends emit only the windows they actually have — an idle Claude session has no 5h window, a plan without Opus has no Opus week — so positions shift. A selector past the end, or one landing on a window with no percentage, falls back to the peak across every window rather than blanking the icon; that is also what puts the ramp on the only window a single-window agent reports. Note that only the two selected windows reach the icon: a third window running out shows up in the tooltip, not the ring.
| Field | Type | Allowed | Summary |
|---|---|---|---|
name |
string | — | Display name for the plugin pill. |
command |
string | — | Binary name on $PATH or absolute path. |
color |
string? | — | Accent color override, hex like #rrggbb or #rgb. |
icon |
string? | — | SVG icon: embedded asset name, absolute path, or ~/ path. |
# Aura configuration.
# Run `aura config describe` for full field docs, or
# `aura config set <key> <value>` to change a value from the CLI.
# ── Agent profiles ───────────────────────────────────────────────────────────
# Define as many profiles as you need. The active profile is tracked in state
# (state.json), not here — switching profiles in the UI does not touch this file.
[[agents]]
name = "Claude Code (Personal)"
kind = "claude-code"
# Path to the agent's config directory. Defaults to ~/.claude when omitted.
config_path = "~/.claude"
[[agents]]
name = "Claude Code (Enterprise)"
kind = "claude-code"
config_path = "~/.claude-enterprise"
[[agents]]
name = "Codex"
kind = "codex"
config_path = "~/.codex"
# ── Plugins ──────────────────────────────────────────────────────────────────
# Plugins are usually installed via `aura plugin add <path>`, which drops the
# binary into ~/.config/aura/plugins/ and registers it via auto-discovery — no
# [[plugins]] block needed. Use the inline form below only for plugins outside
# the user plugins dir, or to pin a color/icon onto a discovered plugin (a block
# with the same name wins over discovery).
[[plugins]]
name = "RTK Gains"
command = "aura-plugin-rtk"
# ── Window ────────────────────────────────────────────────────────
[window]
# How the modal anchors as it auto-fits height: "none" | "bottom" | "top".
# Default is per-OS and written at install (see "Modal anchoring" below).
anchor = "bottom"
# Which display server GPUI talks to on Linux/BSD: "auto" | "x11" | "wayland".
# "auto" (default) prefers X11 whenever $DISPLAY is set — via XWayland on a
# Wayland session — because Wayland forbids a client from positioning its own
# window, which disables `anchor` entirely. See "Linux display backend" below.
linux_backend = "auto"
# Appear in Alt+Tab / Cmd+Tab / dock / panel surfaces. Default false (tray-only).
# Reapplies on the next refresh or open — no restart needed.
show_in_app_switcher = false
# Auto-close the modal when it loses focus. Default true (tray-popup behaviour);
# set false to keep it open until the tray icon is clicked again.
dismiss_on_focus_loss = true
# Show the native window title bar. Default false — Aura is a chromeless tray
# popup. Turning this on also puts the modal in the taskbar / alt-tab list.
# Independent of `auto_resize`.
chrome = false
# Auto-resize the modal to fit its content height. Unset (default) = auto-fit on.
# Set false for a fixed-size modal. Works the same with or without `chrome`.
# (This is not user drag-to-resize — the window manager owns that.)
# auto_resize = false
# Optional upper bound (logical px) on auto-fit height. Already capped at the
# screen work area; this is a tighter ceiling. Ignored when auto_resize = false.
# max_height = 500
# ── Tray ──────────────────────────────────────────────────────────
[tray]
# Whether the icon reports quota or is just a button that opens the modal.
# Default true. While the modal is closed this costs one quota lookup per
# `refresh_secs` (a network request for the API-backed agents); set false to
# leave the icon static.
indicator = true
# Fill the icon's complete ring to the highest quota-window usage. Default true.
# Ignored when indicator is false.
progress = true
# Change the icon from purple to yellow at 50%, orange at 75%, and red at 90%.
# Default true. Ignored when indicator is false.
color = true
# Ask the desktop to emphasize the icon at 90%. Default false because Linux
# panels may animate it or pull it out of the overflow group. SNI/Linux only;
# the color remains the attention signal on macOS and Windows.
pulse = false
# Seconds between background refreshes of the indicator. Ignored when indicator
# is false. Values below 30 are clamped up to 30.
refresh_secs = 1200
# ── Content ────────────────────────────────────────────────────
[content]
# Which usage period tab is selected on open: "all" | "7d" | "30d".
default_period = "all"
# Explicit ordering for the plugin pill row. Named plugins render first in this
# order (case-insensitive match on `name`); the rest keep their natural order.
plugin_order = ["Hello", "RTK Gains"]
# Swap UI copy for the aggressive "Goblin Mode" variant. Default false.
goblin_mode = false
# ── Update ───────────────────────────────────────────────────────────────────
[update]
# Last release version dismissed via the update button's × (bare semver). A
# newer release re-shows the button. Omit for "never dismissed".
# dismissed_version = "0.1.18"
# Master mute: never render the update button, never call GitHub. Default false.
dismiss_all = falseanchor controls which edge of the modal stays put as it auto-fits its
content height:
| Value | Behavior | Default on |
|---|---|---|
none |
Opens at the platform's natural tray corner and grows downward; never repositioned. | macOS, Linux |
bottom |
Bottom edge pinned above a bottom taskbar; grows upward. | Windows |
top |
Top edge pinned just below a top panel / menu bar; grows downward. | — |
The right default is written to your config at install time based on your OS,
so most people never need to set this. Change it if your taskbar/panel is
somewhere other than your platform's default. Linux defaults to none because
panel placement varies so much there — a top-panel GNOME session and a
bottom-panel Plasma one are equally normal — so a Linux desktop with a bottom
panel wants anchor = "bottom" and one with a top panel anchor = "top".
Unrecognised values (including the legacy "auto") fall back to the per-OS
default.
Horizontally the modal follows each platform's own tray popups: macOS and Linux centre it on the tray icon (clamped to stay on screen), Windows right-aligns it to the screen edge the way its volume / network flyouts do. Opening from the tray menu's Show Aura entry carries no click position, so that path falls back to the corner on every platform.
Linux note: anchor = "bottom" repositions live — after each resize Aura
asks the window manager to move the modal via an EWMH
_NET_MOVERESIZE_WINDOW request (a plain ConfigureWindow is ignored by KWin
for a managed top-level), so the modal hugs the bottom taskbar as it
grows/shrinks. This needs an X11 connection; see
Linux display backend for how that is
arranged on a Wayland session, and what you lose if you opt out. We currently
detect only bottom panel reservations, so top on a Linux top-panel setup
approximates by sitting at the very top of the display.
KDE Plasma: if the modal visibly stretches/animates over ~0.5s as it resizes, that is KWin's Morphing Popups effect, not Aura — see Troubleshooting: modal stretches on resize.
Aura's placement — anchor, keeping clear of the taskbar, centring the modal
under the tray icon — all depends on the app choosing its own window position.
Wayland does not allow that. An xdg_toplevel surface has no position in
the protocol, so on a native Wayland session the compositor puts the modal
wherever it likes, anchor does nothing, and an auto-hidden panel can slide
out on top of the window.
X11 has no such restriction, and every mainstream Wayland desktop ships
XWayland, so Aura prefers GPUI's X11 backend whenever $DISPLAY resolves:
| Value | Behavior |
|---|---|
auto (default) |
Use X11 whenever $DISPLAY is set — through XWayland on a Wayland session. Full placement control. |
x11 |
Same, but also warns on stderr when there is no $DISPLAY to use. |
wayland |
Keep the native Wayland backend. The compositor owns placement; anchor and icon-centring stop having an effect. |
Pick wayland if XWayland output looks soft on a fractional-scale display,
and place the modal with a compositor window rule instead (KDE: System
Settings → Window Management → Window Rules, window class substring aura,
property Position → Apply Initially / Force).
Mechanically, GPUI picks its Linux backend in guess_compositor(), which
takes Wayland whenever $WAYLAND_DISPLAY is non-empty and offers no override.
Aura therefore hides that variable across the single Application::new() call
and restores it immediately after, so plugin commands and xdg-open still see
the real session environment. The field is ignored on macOS and Windows.
Aura's section layout can change between releases. Reorganizing it must never
mean "everyone's settings silently revert to defaults", so every key that moves
is recorded as a declarative migration in
crates/aura-core/src/config_migrate.rs, and that one registry drives
everything:
AppConfig::loadnormalizes in memory on every launch. An un-migratedconfig.tomlkeeps working exactly as before; Aura does not rewrite the user's file behind their back.- Old key names keep resolving in the CLI.
aura config get display.tray_coloranswers withtray.colorand prints a note saying where the key went. Same forsetanddescribe. aura config migraterewrites the file into the current layout, carrying every value over. It is idempotent, so running it on a current config just says so. The installer runs it on every install and upgrade.aura doctorandaura config validatereport a pending migration, so you find out without having to know the command exists.
aura config migrate --check # report what would change; exit 1 if anything is pending
aura config migrate # rewrite config.toml (also refreshes the inline docs)A key the migration has no descriptor for — something you hand-wrote into a
section that moved — is reported and left alone in the file, but note that the
rewrite re-serializes from the parsed struct, so it does not survive
migrate. The --check output names any such key before you commit to the
rewrite.
[display] had grown to cover four unrelated jobs. It now means only where
the window goes, under the clearer name [window]:
| Old key | New key |
|---|---|
display.anchor |
window.anchor |
display.linux_backend |
window.linux_backend |
display.show_in_app_switcher |
window.show_in_app_switcher |
display.dismiss_on_focus_loss |
window.dismiss_on_focus_loss |
display.window_chrome |
window.chrome |
display.auto_resize |
window.auto_resize |
display.max_height |
window.max_height |
display.tray_status |
tray.indicator |
display.tray_progress |
tray.progress |
display.tray_color |
tray.color |
display.tray_pulse |
tray.pulse |
display.tray_status_interval_secs |
tray.refresh_secs |
display.default_period |
content.default_period |
display.plugin_order |
content.plugin_order |
display.goblin_mode |
content.goblin_mode |
tray_status became tray.indicator because "enabled" would read as "is there
a tray icon at all"; what it actually toggles is whether the icon is a live
indicator or a plain button that opens the modal.
kind |
Description | Default config_path |
|---|---|---|
claude-code |
Claude Code CLI agent | ~/.claude (dir containing stats-cache.json / projects/) |
codex |
OpenAI Codex CLI | ~/.codex (dir containing sessions/) |
gemini |
Gemini CLI | ~/.gemini |
A leading ~ in config_path is expanded to the user's home directory.
Aura writes the active profile selection to
~/.local/share/aura/state.json. This file is managed automatically —
toggle_window reloads it each time the modal opens, so a profile change made
in one session is visible the next time you click the tray icon. Do not edit
by hand; use aura state set-profile <name> (validated against
config.agents).
{
"active_profile": "Claude Code (Personal)"
}Aura ships with a built-in dark theme that you can override on a per-token basis
via ~/.config/aura/theme.toml. Every key is optional — anything you don't set
falls back to the built-in default. Clicking the Themes entry in the more
menu (•••) opens the file in your editor, seeding it from the defaults on first
click.
[colors]
bg = "#0e0e10"
surface = "#1a1a1f"
accent = "#8b5cf6"
error = "#ff6b6b"
warning = "#e0a96d"
agent_fallback = "#b8b8c0" # used when a brand color would wash out on bg
[typography]
font_family = "JetBrains Mono"
[spinner]
style = "braille" # "braille" | "dot"
interval_ms = 80
# Per-agent overrides. Keys must match the agent's `name` from config.toml
# (quote names with spaces or parentheses).
[agents."Claude Code (Personal)"]
accent = "#d97757"For an agent's accent color, the first match wins:
[agents."<name>"].accentintheme.toml[[agents]] color = "..."inconfig.toml- Per-kind brand default (Claude orange, OpenAI white, Gemini blue)
A luminance fallback applies after all of the above: a resolved color whose
relative luminance exceeds 0.85 is silently swapped for colors.agent_fallback
so the accent never washes out against the dark surface.
The refresh button in the header reloads theme.toml alongside config.toml
— no restart required. A malformed file logs a warning and falls back to the
built-in defaults rather than blanking the UI.
See .design/customization.md for the full theme schema reference.