How it works
Two front-ends over one configuration file, and one generated artefact.
The CLI and the web interface both read and write the same configuration file. Neither runs when you open a shell.
What lives where
Section titled “What lives where”| Location | Holds | Who owns it |
|---|---|---|
| The binary | The catalog, embedded at compile time | The release |
~/.config/loadout/config.yaml | Your particulars | You |
~/.config/loadout/local.yaml | Machine-specific overrides, never synced | You, per machine |
~/.config/loadout/init.zsh | The generated shell file | loadout apply |
~/.local/share/loadout/bin | Managed executables, appended to PATH | loadout bin |
| OS keychain | Credential values, and nothing else | Your keychain |
The catalog is compiled into the binary with go:embed. There is no catalog directory to install and nothing to keep in sync on disk.
The render
Section titled “The render”loadout apply does four things:
- Load. Read
config.yaml, overlaylocal.yamlon top of it, validate the result. Validation is strict and errors name the file, the key, what was expected and what to do. - Resolve. Filter the catalog for the target platform, drop disabled entries, add entries from enabled packs, and generate the aliases your configuration implies — one per host with
shortcut: true, one per workspace, one per clone source. - Render. Run every alias and function through the backend for your shell dialect. The backends produce byte-identical alias lines; only the preamble differs, because bash needs
shopt -s expand_aliaseswhere zsh needs nothing. - Write. Compare against the existing file, print the diff, write it.
The output is a single static file. Sourcing it defines aliases and functions and appends the managed bin directory to PATH. It spawns no processes and calls no binaries, so it costs nothing measurable at shell startup.
Why the binary stays out of the hot path
Section titled “Why the binary stays out of the hot path”An alias cannot be a program. gpush and .. are shell state, so a binary can only ever emit text for the shell to evaluate. The alternative design — a shell hook that calls loadout on every prompt, or an eval "$(loadout init)" in your rc file — puts a process spawn in the path of every terminal you open, for a result that is identical every time.
Generating a file once and sourcing it makes that cost zero, and makes the result inspectable: the file is plain text, you can read it, and loadout apply shows you the diff before it writes.
The consequence people trip on
Section titled “The consequence people trip on”Upgrading the binary does not update your shell.
brew upgrade loadout replaces the binary. It does not touch init.zsh, because that file is generated output that loadout has no business rewriting behind your back — you might have deliberately not applied a change.
So after upgrading, run:
loadout applyRelease notes say so explicitly whenever the shipped catalog changed, and the release process detects that from the golden files rather than relying on anyone remembering.
Requirements, and why nothing is silently dropped
Section titled “Requirements, and why nothing is silently dropped”Every alias and function declares what it needs — a binary, a path, a section of configuration:
pw: requires: - binary: code - config: workspacesWhen a requirement is unmet, the entry is still generated. It is not dropped and not commented out. What happens instead is that loadout doctor reports it, loadout describe explains what to install, and the web interface shows it amber.
That is deliberate. Dropping an alias because a tool is missing means the alias silently disappears on a machine where you have not finished installing things, and you get “command not found” with no explanation. Generating it and reporting the gap tells you the truth.
Two shells, two platforms
Section titled “Two shells, two platforms”The generator has a backend per shell dialect. The catalog has per-platform command variants where the underlying tool differs — systemctl on Linux, launchctl on macOS — and entries that make no sense on a platform are not generated there at all.
Correctness across that matrix is pinned by golden files covering all four combinations. A change to a template shows up as a diff in each, which is what a reviewer reads, because that diff is what lands in every user’s shell. More on the traps in Shell portability.