Skip to content

Security model

loadout writes a file your shell executes at every login, puts executables on your PATH, and holds the keys to your keychain entries. Each of those is a place where getting it wrong is serious rather than annoying.

Secrets never enter a file loadout controls

Section titled “Secrets never enter a file loadout controls”

Configuration holds credential names and which entry is active. Values live in the OS keychain.

Two mechanisms enforce it:

  • loadout scan matches high-signal credential shapes — ghp_, npm_, AKIA, JWTs, PEM blocks, literal password: fields — and refuses them.
  • sync push runs the same scan before the commit, not before the push, so a pasted token never enters git history at all. Once committed, removing it needs a rewrite and the value must be treated as leaked anyway.

Entropy heuristics were rejected deliberately: they fire constantly on hostnames and paths, and a check that cries wolf gets disabled.

Credential output is single-quoted with embedded quotes escaped, because it is eval-ed. A token containing $(...) or backticks cannot execute anything.

Managed binaries are the one feature that downloads code and then runs it.

  • HTTPS on every redirect hop, not just the configured URL. Release hosts redirect to CDNs, and a redirect to http:// would otherwise be followed silently.
  • Checksums are verified before install, not after.
  • Archive members with absolute or .. paths are refused, and extraction is size-capped, so an archive cannot write outside its target.
  • Writes land via rename, so an interruption cannot leave a half-written executable on your PATH.
  • Provenance is recorded. bin list shows the exact URL each binary came from.
  • The managed directory is appended to PATH, so a managed binary cannot shadow a system tool.

loadout update applies the same rules to itself, and refuses a release that publishes no checksum rather than installing it unverified.

The web interface is an RCE API for your account

Section titled “The web interface is an RCE API for your account”

This is the sharpest edge in the project, and it is worth stating in those terms: the interface rewrites what your shell runs at login. Anyone who can drive it can run arbitrary code as you, the next time you open a terminal.

Binding to 127.0.0.1 keeps other machines out. It does not keep other pages out — any site open in your browser can issue requests to localhost. Three checks precede every request:

  • A per-run token, generated at startup and compared in constant time, so the comparison cannot be used as an oracle to guess it byte by byte. It reaches the page in the query string, because a navigation cannot set a header, and the page moves it into sessionStorage and strips it from the URL immediately.
  • Origin must be ours. Any other site’s Origin is refused before the token is considered.
  • Host must be loopback. This is the DNS rebinding defence: an attacker pointing a hostname they control at 127.0.0.1 gets a Host header that does not match.

Static assets are the one deliberate exception, served without a token, because a <link> or <script> tag cannot present one. They are inert files compiled into the binary, identical for every user, and disclose nothing.

Credential values are never sent to the page. It is told only whether a value exists.

Putting the interface behind a public hostname does not work by design — the loopback check refuses it with a 403 before any handler runs.

Allowing a configured hostname would be a small change and is deliberately not offered. That check is the rebinding defence, and removing it leaves a single per-run token in a URL as the only thing between the internet and this API. URLs leak through history, referrer headers, proxy logs and screen shares. The token is valid for the whole life of the process, with no revocation and no audit trail.

SSH port forwarding gives the same access over a credential you already have, with nothing new exposed. That is the supported answer.

  • A compromised machine. loadout runs as you and can do what you can. It is not a sandbox.
  • Your keychain’s own security. loadout stores and retrieves; the keychain decides when to prompt.
  • What you put in a custom alias. Custom entries are rendered as given.
  • Your configuration repository’s access control. Use a private repository — even without secrets, it names your hosts, paths and identities.

Report privately through the security policy rather than opening a public issue.

Useful in a report: what you can make it do, not just what looks wrong. The distinction between a token in a URL and arbitrary code execution is the whole reason the loopback check exists, so a report that names the impact gets a faster answer.