macrun
macrun is a macOS command-line tool for local development secrets.
It stores secret values in macOS Keychain, tracks non-secret metadata separately, and injects secrets into a child process only when you explicitly run a command.
If you want the convenience of environment variables without leaving plaintext .env files around your repo, this is the tool.
It also works with sensible defaults, so common usage does not require setup first.
Why Use It
Local secret handling tends to drift into one of a few bad patterns:
- large plaintext
.envfiles copied between projects - long-lived
exportcommands in a shell session - reusing the wrong project's credentials by accident
- handing every secret to every process whether it needs them or not
macrun is designed to tighten that up without trying to be a full secret platform.
It helps by:
- storing secret values in Keychain instead of repo files
- scoping secrets by project and env
- importing from existing
.envfiles when needed - keeping the main workflow centered on whole-scope
macrun exec -- ...
What It Is Not
macrun is for local development on macOS. It is not a replacement for:
- Vault or another server-side secret manager
- CI/CD secret distribution
- production secret storage
- process sandboxing
If a process receives a secret, that process can still leak it. macrun reduces exposure before process start; it does not make an unsafe program safe.
Install
From crates.io:
From this repository:
During development you can also run it directly:
To install the exact lockfile-resolved dependency set from a published release:
Quick Start
Store a value in the default project and default env:
Store a value in a named env while keeping the default project:
Initialize the current working tree:
Import an existing .env file:
List stored keys without printing values:
Run a command with only the secrets it needs:
Run a command with one secret replaced by Vault Transit ciphertext:
Print the full resolved environment for the active project/env:
Mental Model
Each stored secret is identified by:
- project
- env
- environment variable name
Example scope:
- project:
my-app - env:
dev - key:
APP_DATABASE_URL
When you run a command, macrun resolves the active project and env, reads every stored value for that scope from Keychain, and injects them into the child process you launched.
Core Commands
Implemented today:
initsetgetimportlistexecenvunsetpurge --yesdoctorvault encryptvault push
Global flags:
--project NAME--env NAME--json
Common Workflows
Set secrets manually:
Read a specific value:
Import a dotenv file into the active scope:
Inspect metadata:
Print a machine-readable environment snapshot:
Remove keys:
Project and Env Resolution
macrun can resolve the active scope from a local config file named .macrun.toml.
Project resolution order:
- explicit
--project .macrun.tomlin the current directory or nearest ancestor- internal default project scope
Env resolution order:
- explicit
--env default_envfrom.macrun.tomldev
That means a typical workflow is:
- run
macrun initonce in a working tree - store or import secrets for that project
- run local commands via
macrun exec
If you do not initialize a working tree, macrun falls back to:
- project:
(default) - env:
dev
So commands like macrun set URL=https://somewhere work immediately.
(default) is a display label for the fallback project scope, not a literal project name. If you run macrun --project default ..., the project name is exactly default.
Storage Model
Secret values live in macOS Keychain.
The current Keychain layout uses:
- service:
macrun/<project>/<env> - account: env var name
Non-secret metadata is stored in the app config directory so macrun can efficiently list entries and track source and update time.
Vault Bootstrap Transfer
macrun's Vault support exists for bootstrap transfer, not day-to-day runtime secret serving.
The useful cases are:
- get Vault Transit ciphertext for an app that must store a key in its database
- write one or more secrets into Vault KV so the app fetches them from Vault directly
Transit ciphertext for app storage
vault encrypt reads a plaintext secret from Keychain, sends it to Vault Transit, and prints the ciphertext.
That ciphertext can then be stored in an application database or handed to an admin API. At runtime, the app asks Vault to decrypt it and keeps plaintext only in memory.
If the app is being bootstrapped directly from macrun exec, you can also replace a plaintext env var with Transit ciphertext in the child process:
In that mode, macrun removes APP_CLIENT_SECRET from the child environment and injects APP_CLIENT_SECRET_CIPHERTEXT instead.
That removal is intentional. The bootstrap target process should receive ciphertext only, not both plaintext and ciphertext.
Example:
Vault KV as the source of truth
vault push reads one or more plaintext secrets from Keychain and writes them into Vault KV.
Example:
Security Notes
macrun helps reduce:
- accidental commits of plaintext secret files
- broad shell-session contamination
- wrong-project and wrong-env reuse
- oversharing secrets to processes that do not need them
It does not protect against:
- malware or a compromised user session
- root or admin compromise of the machine
- a child process that logs or forwards its environment
- terminal capture, clipboard leaks, or screen capture
Documentation
- USER_GUIDE.md for full usage and operational guidance
- TODO.md for implementation notes and future work
Release Workflow
Typical release flow:
What those steps do:
make bumpincrements VERSIONmake distincrements BUILD, builds a release binary, and stages release artifacts indist/cargo publishpublishes the crate so users can install it withcargo install macrun
The staged distribution currently includes:
dist/bin/macrundist/USER_GUIDE.mddist/README.mddist/LICENSE
BUILD is intentionally included in the published crate source because the binary reads both VERSION and BUILD at compile time to produce the custom --version output.
License
GPL-3.0-or-later
Copyright (c) Alexander R. Croft