env vars done right.
easyenv is a local-first env var manager for the shell. It stores secret values in the OS credential store, keeps non-secret metadata in SQLite, resolves project > global values, injects the final environment into child processes, and now supports encrypted share bundles for person-to-person secret handoff.
The implementation is optimized for a macOS-first MVP and uses:
- Rust for the single-binary CLI
- clap for subcommands, help, aliases, and completions
- rusqlite for local metadata (
projects,vars, migrations) - security-framework for macOS Keychain storage
- AES-256-GCM (
aes-gcm) for encrypted secret payloads before storage - zeroize for best-effort secret memory cleanup
- keyring-rs as the non-macOS native credential fallback
- age for encrypted share bundles (
push/pull)
Implemented now:
easyenv initeasyenv seteasyenv geteasyenv list/easyenv lseasyenv exec/easyenv runeasyenv walkthrough/easyenv walktrougheasyenv importeasyenv pusheasyenv pulleasyenv workspace showeasyenv workspace rotate-identityeasyenv doctoreasyenv completion
Still deferred:
- hosted relay / audit retention
- full team workspace membership management
- branch-aware profiles
- native Windows support
- global secrets: stored in the native credential store under
dev.easyenv.global - project secrets: stored in the native credential store under
dev.easyenv.project - master encryption key: stored separately under
dev.easyenv.master - workspace share identity: stored under
dev.easyenv.identity - metadata: stored in SQLite at:
- macOS:
~/Library/Application Support/easyenv/easyenv.sqlite - Linux: XDG data dir equivalent
- macOS:
- resolution order:
shell override > project > global
Project registration is local-first: easyenv init registers the canonical path in the metadata database, and subdirectories inherit that project automatically.
On macOS, global secrets now use the lower-level PasswordOptions API so easyenv can prefer synchronizable keychain entries while still being usable as an unsigned CLI during development.
Environment flags:
EASYENV_KEYCHAIN_GLOBAL_SYNC=prefer|force|neverprefer(default): try synchronizable storage first, then fall back to local-only storageforce: require synchronizable storagenever: keep global secrets local-only
EASYENV_KEYCHAIN_ACCESS_GROUP=...- optional access group for signed/distributed builds that want stricter keychain placement
Important note: true iCloud-keychain-grade behavior for a signed distributed app still depends on Apple signing and entitlements. This repo now uses the right API surface, but it does not claim full entitlement wiring for an unsigned development binary.
easyenv can now export and import encrypted age x25519 armored bundles.
Each machine gets a local workspace identity:
easyenv workspace showThat prints an age1... recipient. Another developer can encrypt selected secrets to that recipient:
easyenv push --to age1... --key OPENAI_API_KEY --out easyenv-share.ageThe recipient can then import the bundle:
easyenv pull easyenv-share.age --projectThis is intentionally local-first scaffolding:
- no relay server
- no hosted audit trail
- no team membership graph yet
- encrypted bundle exchange works today
Initialize a project:
easyenv initStore a global secret:
easyenv set OPENAI_API_KEY=sk-...Store a project secret:
easyenv set --project STRIPE_SECRET=whsec_...Read a value without fully revealing it:
easyenv get OPENAI_API_KEYExplain where a resolved value came from:
easyenv get STRIPE_SECRET --explainList resolved vars as JSON:
easyenv list --format json --revealRun a command with injected vars:
easyenv exec -- pnpm devGet a built-in guided tour of the product:
easyenv walkthrough(easyenv walktrough also works as a forgiving alias.)
Import an existing .env file into the active project and delete the source file:
easyenv import .env --deleteShow your local share recipient:
easyenv workspace show --jsonExport all visible vars to an encrypted share bundle:
easyenv push --all --to age1... --out easyenv-share.ageImport a share bundle into project scope:
easyenv pull easyenv-share.age --projectGenerate completions:
easyenv completion zshSee docs/install.md.
Quick source install:
cargo install --path crates/easyenv-cli --lockedBuild:
cargo buildRun tests:
cargo testIntegration tests use a file-backed secret store instead of the real keychain.
Relevant env vars:
EASYENV_HOME— overrides the application data directoryEASYENV_SECRET_BACKEND=file— uses a local file secret store for testing
crates/easyenv-cli— CLI binarycrates/easyenv-core— domain model, paths, crypto, metadata, resolvercrates/easyenv-keychain— native secret-store implementationsdocs/install.md— install instructions.github/workflows— CI and release packaging