outl docs
Nix
outl is a flake. One input exposes two surfaces: a set of packages and a home-manager module for per-user configuration and background sync.
| Flake output | Source | What it is |
|---|---|---|
packages.outl (default) | flake.nix | The CLI (outl) and the TUI (outl-tui). |
packages.outl-desktop | flake.nix | The Tauri 2 desktop app. |
homeManagerModules.default | hm-module.nix | programs.outl — manages ~/.config/outl/config.toml, optionally installs the packages and a per-user sync service. |
Linux only. Packages build for
x86_64-linuxandaarch64-linux. There is no Nix-built macOS package: the Tauri desktop build needs Apple SDK frameworks that current nixpkgs no longer exposes as stable attributes. On macOS the home-manager module still writes yourconfig.toml; you supply the binary yourself (see the Homebrew tap).
The flake
The flake pins nixpkgs-unstable, flake-utils, rust-overlay, and home-manager.
You do not need a Rust toolchain on your machine — the flake supplies one via rust-overlay. There is no binary cache, so the first build of each package compiles from source.
Packages
CLI + TUI — packages.outl
One derivation builds both binaries (cargo build -p outl-cli -p outl-tui):
outl— the CLI (workspace ops,serve,peer,doctor, …).outl-tui— the terminal UI.
nix profile add github:outlmd/outl # default package = outl
outl --help
outl-tui
Or run without installing:
nix run github:outlmd/outl -- --help
Desktop — packages.outl-desktop
The Tauri 2 app.
The build compiles the Solid frontend with Bun first, then builds the Rust shell with the tauri/custom-protocol feature so the webview embeds the frontend instead of pointing at a dev server.
The package also emits a freedesktop .desktop entry and hicolor icons into its $out/share, so a full desktop session picks up the launcher and icon with no extra setup (home-manager extends this to ~/.local/share for bare Wayland launchers — see the module below).
nix profile add github:outlmd/outl#outl-desktop
outl-desktop
Dev shell
devShells.default gives you the pinned Rust toolchain plus cargo-tauri, bun, and nodejs, with the package dependencies already on the path:
nix develop github:outlmd/outl
Home-manager module
hm-module.nix is exposed as homeManagerModules.default under the programs.outl namespace.
Its job is configuration management: it renders ~/.config/outl/config.toml from typed options, and (on Linux) can install the packages and start a per-user background sync.
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
home-manager.url = "github:nix-community/home-manager/release-25.05";
outl.url = "github:outlmd/outl";
};
outputs = { nixpkgs, home-manager, outl, ... }: {
homeConfigurations.you = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
modules = [
outl.homeManagerModules.default
{
programs.outl = {
enable = true;
installDesktop = true; # also put outl-desktop on the PATH
settings = {
theme.preset = "gruvbox";
editor.vimMode = true;
sync.transport = "iroh";
reminders.quietHours = "22:00-07:00";
};
# Optional per-user background sync (Linux only):
services.sync = {
enable = true;
workspace = "/home/you/notes";
};
};
}
];
};
}
}
When enabled, the module:
- Writes
~/.config/outl/config.tomlfromprograms.outl.settings(every key is typed; anything not yet modeled goes insettings.extraConfig). - On Linux, adds
programs.outl.packagetohome.packages(andoutl-desktoptoo, ifinstallDesktop). - If
installDesktop(Linux), also installs the freedesktop launcher entry and the hicolor icons into~/.local/share(~/.local/share/applicationsand~/.local/share/icons), symlinked from theoutl-desktoppackage so the app menu, the “Open With” menu and Wayland launchers (e.g.rofi -show drununder a tiling WM) show the right entry and icon. The package is the single source — these are symlinks, not a second copy. - If
services.sync.enable, starts a user systemd unitoutl-syncthat runsoutl serve --workspace <path>and restarts on failure.services.sync.watchand.synctoggle the watcher and endpoint halves (--no-watch/--no-sync);.rustLogsetsRUST_LOG. The service runs as you, using your own identity (~/.outl) and device store (~/.config/outl) — pairing is plainoutl peer pair, and the daemon picks up new peers on its own.
The service runs while you are logged in. For a dedicated always-on box (a NAS, a VPS) where nobody sits at a login, enable lingering so your user manager stays alive without a session:
loginctl enable-linger <user>. That is what keeps the box paired 24/7.
Settings
programs.outl.settings mirrors outl.toml.
A few of the keys:
| Option | Type | Default | Meaning |
|---|---|---|---|
managed | bool | true | Emits the top-level managed = true directive so no client rewrites config.toml. See Declarative config. |
theme.preset | enum | "outl-light" | Light side of the pair. One of outl, outl-light, default-dark, light, logseq-light, dracula, solarized-dark, nord, monokai, gruvbox. |
theme.presetDark | enum | null | null | Dark side — the preset the TUI renders under mode = "auto"/"dark". null follows preset; the terminal is themed by a lone theme.preset. |
theme.mode | enum | "auto" | "light", "dark", or "auto". A terminal reads auto as dark, so "auto" themes it by the dark side. |
editor.vimMode | bool | true | Vim-style modal bindings (desktop). |
sync.transport | enum | "iroh" | "iroh" (P2P QUIC) or "file" (iCloud / shared FS). |
reminders.quietHours | str? | null | e.g. "22:00-07:00". |
backup.enabled / backup.intervalMinutes | bool / int | true / 30 | Automatic git snapshots of the workspace. |
tui.icons | enum | "emoji" | TUI chrome icons: "emoji" (works with any terminal font) or "nerd-font" (needs a patched font). |
extraConfig | attrs | {} | Merged last, for keys the module does not model yet. |
The full key list is in the module source (hm-module.nix); the meaning of each key is in Configuration.
Declarative config
programs.outl writes config.toml as a symlink into the Nix store.
That only survives if nothing replaces the file — and outl’s clients write their settings by atomic rename, which detaches the symlink and leaves a plain regular file.
The next home-manager switch then refuses to overwrite its own managed target.
So the module sets managed = true in the generated file by default, and outl treats that as “do not rewrite”:
-
Every client (
outl,outl-desktop,outl-tui, mobile) skips its config write, so the symlink stays a symlink across switches. -
The desktop Settings modal disables Save and points you at
programs.outl.settings; the theme picker still previews live. -
Because the modal can no longer persist it,
workspace.lastfreezes unless you pin it. Pin it declaratively to keep the desktop reopening your workspace:programs.outl.settings.workspace.last = "/home/you/outl";(Omit it and the desktop just opens the workspace picker on launch — every reader falls through cleanly.)
To go back to app-managed settings (the client persists its own state again), set programs.outl.settings.managed = false.
A client write will then detach the symlink again, so also set xdg.configFile."outl/config.toml".force = true to let home-manager reclaim it on the next switch.
Migrating a machine that already hit the conflict. If you were using this module before the
manageddefault, your~/.config/outl/config.tomlis already a stray regular file and the switch is failing with “file exists and cannot be overridden”. The module deliberately keeps Home Manager’s collision guard on, so it will not silently clobber that file. Setxdg.configFile."outl/config.toml".force = trueand runhome-manager switchto reclaim the target — that is the whole migration. The module’smanageddefault (true) keeps it a symlink afterwards; you do not need to touchmanaged. If you had edited the stray file by hand, move it aside first soforcedoes not discard your edits.
Theme precedence
The theme is the one setting whose effect is easy to misread, because outl stores a light/dark pair and resolves it through layers the module does not fully control.
-
Light/dark pair vs. the terminal.
presetis the light side;presetDarkis the dark side. A terminal cannot read OS appearance, so the TUI renders the dark side whenevermodeis"auto"(the default) or"dark". This module makespresetDarkdefault to followingpreset, so a singlesettings.theme.preset = "x"themes the terminal and the desktop together. SetpresetDarkexplicitly only when you want the two sides to differ — for example a custom light side with the brand"outl"dark theme. When you customise neither, the module keeps outl’s brand pairoutl-light/outl, so the terminal defaults to the brand dark theme rather than to the light preset. -
Global vs. workspace (less common). This module writes the global
~/.config/outl/config.toml, which is outl’s lowest theme precedence. Precedence is first-hit-wins:--theme <preset>→ a per-workspace<root>/.outl/config.toml[theme] preset→ the global file → the built-in default. A defaultoutl initworkspace has no[theme]section, so home-manager’s global value applies everywhere. Only a workspace whose.outl/config.tomlyou have hand-added a[theme] presetto overrides home-manager for that workspace — the “I setpresetin Nix but this one folder still shows another theme” case.
Which do I use?
| Situation | Use |
|---|---|
| Just want the CLI/TUI on any Nix machine | packages.outl |
Want config.toml managed + optional logged-in sync on a laptop/desktop | homeManagerModules.default |
| A dedicated box that must stay paired 24/7 (NAS, VPS) | homeManagerModules.default + loginctl enable-linger <user> |