Configuration
Configuration
outl reads two TOML files at launch. They are read in this order; the second can override fields from the first.
| Layer | Path | Scope | Written by |
|---|---|---|---|
| Global | ~/.config/outl/config.toml | The user’s machine — every workspace, every client | The desktop app’s Settings modal; you can also edit it by hand |
| Per-workspace | <workspace>/.outl/config.toml | One workspace only | outl init (for the actor id); hand-edit for the rest |
The path layout is XDG-style on every OS — including macOS.
outl is keyboard-first and CLI-friendly; the macOS-native ~/Library/Application Support/… location would split the TUI and desktop into two config files for no real benefit.
The reader for both files is the outl-config crate (crates/outl-config/).
TUI and desktop import the same module so a field can’t drift between clients — extending the schema in one place lights up in both.
Global config (~/.config/outl/config.toml)
Every field is optional; missing values fall back to the documented default. A malformed file is logged and replaced with defaults rather than refused to boot — preferences aren’t worth blocking the app on.
# ~/.config/outl/config.toml — full example with every supported field
[workspace]
# Absolute path to the workspace the user last opened. The desktop
# writes this on every `set_workspace` call; the TUI / CLI read it
# when no `--workspace` flag and no positional path is given.
last = "/Users/me/iCloud/outl"
[theme]
# Palette preset name from `outl_theme::PRESETS`.
# Choices: "outl" (default), "default-dark", "light", "dracula",
# "solarized-dark", "nord", "monokai".
preset = "outl"
[editor]
# Vim-style modal bindings (Normal / Insert / Visual). Defaults to
# `true` — outl is keyboard-first. The desktop honours this; the TUI
# is vim-style by definition and ignores the flag.
vim_mode = true
# Outline font size in pixels (desktop only — the TUI uses your
# terminal font).
font_size = 15
Field reference
[workspace]
| Field | Type | Default | Read by | Effect |
|---|---|---|---|---|
last | absolute path | none | desktop, TUI, CLI | Where the next outl (with no args) opens. The desktop persists this on every workspace switch. If the path no longer exists, every reader silently falls through to its next fallback (CLI: cwd; desktop: workspace picker). |
[theme]
| Field | Type | Default | Read by | Effect |
|---|---|---|---|---|
preset | string | "outl" | TUI, desktop | Active palette. Unknown names fall through to outl. |
Available presets: outl, default-dark, light, dracula, solarized-dark, nord, monokai. See theming.md for the look of each.
[editor]
| Field | Type | Default | Read by | Effect |
|---|---|---|---|---|
vim_mode | bool | true | desktop | When false, the desktop drops the modal Normal / Insert / Visual model and only listens to OS-standard chrome chords (⌘P, ⌘B, …). The TUI is vim-style by definition and ignores this. |
font_size | integer (pixels) | 15 | desktop | Outline body font size. The TUI uses the user’s terminal font; setting this has no effect there. |
Per-workspace config (<workspace>/.outl/config.toml)
Written by outl init; carries the device’s per-workspace identity and (optionally) workspace-scoped overrides.
# Per-workspace config — auto-generated by `outl init`, can be
# hand-edited.
[workspace]
# The per-device-per-workspace actor id (a ULID). DO NOT copy this
# file between machines: every device needs its own id so the op
# log can attribute writes correctly.
actor_id = "01HKZX9YBPDC5XJZ3R8K2QGM7E"
[theme]
# Workspace-only override. When set, takes precedence over the
# global `[theme] preset` while you're inside this workspace.
preset = "monokai"
The
[workspace] actor_idfield cannot move to the global config — it’s per-device-per-workspace by design. A peer’s op log identifies writes by this id; sharing it across machines silently breaks convergence.
Precedence chains
Workspace path
When you type outl (no args):
- Subcommand-positional path (
outl page get … <PATH>). - Global flag
--workspace <DIR>. [workspace] lastfrom~/.config/outl/config.toml.- Current working directory (the
cd ~/notes && outlfallback).
A path from config.toml that no longer exists on disk is skipped silently and the chain falls through to the next step.
Theme
When the TUI / desktop decides which palette to render:
--theme <preset>CLI flag (TUI only).- Per-workspace
[theme] presetfrom<workspace>/.outl/config.toml. - Global
[theme] presetfrom~/.config/outl/config.toml. - Built-in default —
outl.
Unknown preset names fall through to the next step rather than erroring.
Editing safely
The TOML reader (outl-config::load) is forgiving by design:
- Missing file → defaults, no warning (first launch is normal).
- Malformed TOML → defaults + a
tracing::warnlog line, the app boots normally. - Unknown fields → ignored. Older binaries reading a newer config don’t choke; you can add fields ahead of time.
- Partial schema (e.g. only
[theme]populated) → other sections fall back to their per-sectionDefault.
Saving (outl-config::save) writes atomically — the new content lands in config.toml.tmp and the file is renamed on top. A crash mid-write never leaves a truncated config.
Migrating from earlier versions
The desktop briefly stored its settings as JSON at ~/Library/Application Support/app.outl.desktop/settings.json (and the actor at the same directory).
That path is no longer read.
If you upgrade from one of those builds:
- The desktop picks up
~/.config/outl/config.toml(creates it on first save). - The actor ULID at
~/.config/outl/actoris generated fresh on first run; your local op log keeps writing under the new id. - Your workspace’s
ops/directory is unchanged — only the client’s identity rotates, not the workspace’s history.
If you want to preserve the old actor id, copy it from the old path into ~/.config/outl/actor before launching the desktop.