The agent should offer migration whenever it encounters a .env, .env.local,
.env.development, .env.production, etc. in a project — especially if it's
tracked by git, has multiple API keys, or the user is about to add a new one.
Always ask first. Some .env files are intentionally project-local (loaded
by tooling that can't read env vars from the parent process — e.g. Next.js's
.env.development at build time). A migration might break them.
mxkey migrate <path-to-.env> [project-slug]The command:
- Parses the
.env(skips comments and blanks, stripsexportprefix and surrounding quotes). - Shows each key with a character count — values are never printed.
- Prompts
[y/N](non-interactive) or per-key[Y/n/a/q](interactive) to confirm migration. - For each key, pipes the value into
mxkey set project.<slug>.<key_lower> <KEY>via non-TTY stdin. Values never touch shell history or intermediate files. - Prints a summary and the list of keys to remove from the original
.env.
A .env.local like:
STRIPE_SECRET_KEY=sk_live_...
DATABASE_URL=postgres://...
OPENAI_API_KEY=sk-...
…migrated with slug myapp becomes:
| mxkey name | env var |
|---|---|
project.myapp.stripe_secret_key |
STRIPE_SECRET_KEY |
project.myapp.database_url |
DATABASE_URL |
project.myapp.openai_api_key |
OPENAI_API_KEY |
After migration, wrap the dev script with mxkey run:
# Before
pnpm dev
# After (all three keys injected)
mxkey run project.myapp.stripe_secret_key project.myapp.database_url project.myapp.openai_api_key -- pnpm devFor apps with many keys, the command gets long. Two alternatives:
# scripts/dev.sh
#!/usr/bin/env bash
exec mxkey run \
project.myapp.stripe_secret_key \
project.myapp.database_url \
project.myapp.openai_api_key \
-- pnpm dev# scripts/gen-env.sh
#!/usr/bin/env bash
for name in stripe_secret_key database_url openai_api_key; do
mxkey export "project.myapp.$name"
done > .env.localThen .env.local is gitignored and regenerated per dev session. Not as tidy as
mxkey run, but necessary for some build tools.
If the file isn't already covered, append:
echo '.env*' >> .gitignore
echo '!.env.example' >> .gitignore # keep the template, if any- Verify with
mxkey list— the new secrets should appear. - Test the dev command with
mxkey run .... - Trim the migrated keys out of the original
.env. The migration script prints the list of keys it moved at the end — remove exactly those lines from the file. If nothing non-secret remains, delete the file:rm .env.local. Leaving both copies means the secrets still live in plaintext and the two stores can drift when one is updated. - If the
.envwas ever committed, the secrets are already leaked — rotate each key at the provider before trusting the new setup.
- Multiline values (RSA keys, JSON credentials). The simple parser won't
handle
KEY="-----BEGIN..."spanning lines. Migrate manually:cat key.pem | mxkey set api.my-key MY_PEM - Interpolation (
API_URL=https://${DOMAIN}/api). The parser stores the literal value — interpolation won't happen. If your code expects expanded values, do the expansion yourself before migrating. - Duplicate keys in the same
.env. The last one wins (standard.envsemantics). Nothing special needed.