Skip to main content
keyenv

🔐 Keep env secrets in Keychain. Inject them on demand. 🔐

keyenv is a macOS command-line tool for developers who want safer local credential storage. It keeps values in the login Keychain and injects them only into commands launched explicitly through keyenv run, while applications keep using their normal environment APIs.

Credential values are never printed or placed in command arguments. Launched applications and their child processes inherit the resolved environment.

Install

keyenv requires macOS and Python 3.11 or newer.

uv tool install keyenv-macos
keyenv --help

The distribution is named keyenv-macos; the installed command is keyenv.

Configure

Add a value-free .keyenv.toml to the root of each project:

[keyenv]
version = 1
# Optional additions to the built-in browser/mobile public-prefix denylist:
public_prefixes = ["MY_CLIENT_PUBLIC_"]

[secrets.OPENROUTER_API_KEY]
account = "my-project/OPENROUTER_API_KEY"
required = true

Authorize the account for this canonical project root, store the credential through the hidden interactive prompt, then check its source:

keyenv authorize OPENROUTER_API_KEY
keyenv set OPENROUTER_API_KEY
keyenv doctor

Commit .keyenv.toml, but keep credential values out of it. Authorization stores only a path digest in Keychain; it never reads the credential. If the project moves, transfer each account explicitly with keyenv authorize --rebind NAME.

Commands

Run these from a configured project directory:

keyenv authorize NAME           # bind one account to this project root
keyenv authorize --rebind NAME  # transfer an existing binding to this root
keyenv set NAME                 # store and verify one declared credential
keyenv doctor                   # report credential names and sources only
keyenv run -- COMMAND [ARGS...] # launch a command with resolved credentials
keyenv migrate                  # copy legacy entries and retain the originals
keyenv migrate --delete-legacy  # delete legacy entries after full verification
keyenv --version                # print the installed version

For example:

keyenv run -- uv run python app.py
keyenv run -- uv run jupyter lab
keyenv run -- pnpm dev

keyenv run replaces itself with the requested command, so the launched process owns its signals and exit status. Security or operational failures exit with status 1; invalid command-line usage exits with status 2.

Notes

  • Credentials resolve from a non-empty process environment value, the current Keychain service, the legacy Keychain service, and finally missing state, in that order. Existing environment values therefore keep CI and provider-native injection working.
  • Keychain accounts have one authorized project-root owner. Projects that need the same underlying value should use distinct account names. A run command whose declared values all come from the process environment performs no Keychain authorization or credential reads for those values.
  • keyenv run must start inside the manifest project root, and manifest files may not be symbolic links.
  • The native macOS Keychain backend is required. Configuring another keyring backend causes operational commands to fail safely.
  • keyenv run refuses to launch while a declared credential or VERCEL_OIDC_TOKEN has a populated assignment in a project dotenv file.
  • Dotenv filenames are matched case-insensitively. Scanning covers project output trees such as .next, build, and dist, while excluding only .git, Python virtual environments, node_modules, and __pycache__. Directory symlinks or broken links in the scanned tree cause a safe refusal. Dotenv candidates must resolve to regular files and may not exceed 1 MiB.
  • Secret names must be uppercase shell identifiers. Built-in browser and mobile public prefixes include NEXT_PUBLIC_, NUXT_PUBLIC_, VITE_, VUE_APP_, REACT_APP_, GATSBY_, EXPO_PUBLIC_, and PUBLIC_. Manifest additions are additive and cannot remove these defaults.
  • Migration copies and verifies legacy entries under io.github.tsilva.keyenv.v1. It retains the originals unless --delete-legacy is supplied and every required entry is safe.
  • For linked Vercel projects, use vercel env run -e development -- keyenv run -- COMMAND instead of vercel env pull, which writes plaintext files.
  • A launched application and its descendants can read injected values. Code already running as the same macOS user is outside this protection boundary. Report suspected vulnerabilities through SECURITY.md without including credential values.

Development

uv sync --locked --all-groups --no-config --exclude-newer "7 days"
uv run --locked ruff check .
uv run --locked ruff format --check .
uv run --locked mypy
uv run --locked python -m unittest discover -s tests -v
KEYENV_INTEGRATION=1 uv run --locked python -m unittest discover -s tests -p 'test_integration_keychain.py' -v
KEYENV_DIST_DIR="$(mktemp -d)"
UV_OFFLINE=1 uv build --no-build-isolation --no-sources --out-dir "$KEYENV_DIST_DIR"
rm -- "$KEYENV_DIST_DIR/.gitignore"
uv run --locked python scripts/check_artifacts.py "$KEYENV_DIST_DIR"

The integration test uses disposable synthetic entries in the login Keychain and removes them afterward.

Architecture

keyenv architecture

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

keyenv_macos-0.1.1.tar.gz (1.5 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

keyenv_macos-0.1.1-py3-none-any.whl (12.5 kB view details)

Uploaded Python 3

File details

Details for the file keyenv_macos-0.1.1.tar.gz.

File metadata

  • Download URL: keyenv_macos-0.1.1.tar.gz
  • Upload date:
  • Size: 1.5 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for keyenv_macos-0.1.1.tar.gz
Algorithm Hash digest
SHA256 594fe0aa1788a20236b3fda3317cd11f4a51b95f6efeab69ec83246267ea0f73
MD5 c51856157b1db05eb8d7ee9e5ce28532
BLAKE2b-256 dd5573ace195a07d200de6d58a50aa894bc4a43430f8f8e613e94776b9cd9a93

See more details on using hashes here.

Provenance

The following attestation bundles were made for keyenv_macos-0.1.1.tar.gz:

Publisher: release.yml on tsilva/keyenv

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file keyenv_macos-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: keyenv_macos-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 12.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for keyenv_macos-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5ded5b1f42ebc12d3a0bb9256c1ad5a0a84694a8908c9005ff14d6d87d72b87f
MD5 f84ca0e3a1144ab6567d188d86be8fd1
BLAKE2b-256 cadccc30533cb85f6698062df9ef35fdd3dc9eb471f58206b245eb4be02360cd

See more details on using hashes here.

Provenance

The following attestation bundles were made for keyenv_macos-0.1.1-py3-none-any.whl:

Publisher: release.yml on tsilva/keyenv

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page