Skip to content

Global Config

Dan Riddell edited this page Sep 30, 2026 · 1 revision

Global config

letsgo.mod describes a release, and travels with the repository. config.mod describes a machine, and does not.

The rule that separates them: nothing in the global file may change what a release is. Not a byte of output, not a gate, not a version, not where something is published. If a setting could make your laptop and CI produce different releases from the same commit, it does not belong here — and letsgo rejects it rather than letting it quietly work.

Where it lives

os.UserConfigDir()/letsgo/config.mod

which is ~/.config/letsgo/config.mod on Linux, ~/Library/Application Support/letsgo/config.mod on macOS, and %AppData%\letsgo\config.mod on Windows. $LETSGO_CONFIG points somewhere else. A missing default file is fine; a LETSGO_CONFIG pointing at a missing file is an error, because that one was asked for.

It is the same directive syntax as letsgo.mod, and letsgo fmt formats it.

// ~/.config/letsgo/config.mod

go           /usr/local/go1.27.1/bin/go
git          /usr/bin/git
cache        /mnt/big/letsgo-cache
plugins      /mnt/big/letsgo-plugins
plugin-repo  acme/letsgo-plugins-mirror
proxy        https://goproxy.acme.internal
color        auto
update-check weekly

The directives

Directive What it sets
go <path> The Go toolchain to run
git <path> The git to run
tool <name> <path> Where to find a named tool, such as govulncheck or apidiff
cache <dir> or cache off The build cache
plugins <dir> The plugin store root
plugin-repo <owner/name> Where letsgo plugin install fetches from
proxy <url> The module proxy to warm
token-command <argv...> A credential helper for the forge token
`color auto always
`update-check off daily

That is the complete set. Anything else — build, brew, image, disable, project — is an error naming the directive and saying it belongs in letsgo.mod. The error is the feature: a build linux/amd64 that worked here would mean a release whose targets depend on whose machine ran it.

Precedence

flag > environment > global > default.

A flag beats everything, because it was typed for this run. The environment beats the global file, because CI sets the environment and CI should not have to know what is in a developer's home directory. The global file beats the built-in default, which is the only thing it is allowed to beat.

letsgo plan --explain names the source of every machine value, so you can see which layer won:

  proxy    https://goproxy.acme.internal   ~/.config/letsgo/config.mod
  go       /usr/local/go1.27.1/bin/go      ~/.config/letsgo/config.mod
  cache    /mnt/big/letsgo-cache           ~/.config/letsgo/config.mod

No global value ever reaches the manifest — with one exception that was already there, the Go version, which is a build input and recorded as one. The path you found that Go at is not.

token-command

A credential helper, for people who keep forge tokens in a password manager or a short-lived issuer rather than in an environment variable:

token-command  op read op://vault/github/token

It runs only when neither a flag nor the environment has already supplied a token, so it never overrides something more explicit. Its output is used and not written anywhere: not logged, not cached, not put in the manifest.

A token-command that exits non-zero falls through to "no token", with its standard error shown. That is the useful failure — a broken helper should look like a missing token and say why, rather than aborting a command that might not have needed one.

Why this file exists at all

Before it, the machine-specific settings had nowhere to go but letsgo.mod, which meant either committing your own paths to a shared repository or carrying a local diff forever. Splitting them is what makes it possible to check the global file against a hard rule — and a rule that is checked is worth more than a convention that is documented.

Clone this wiki locally