Migrate literal credentials off config¶
If your tool's config still holds secrets in *.api.key / *.auth.value / bitbucket.username + bitbucket.app_password form, config migrate-credentials moves each of them to an environment-variable reference or to the OS keychain in a single pass.
The command is deliberately safe to run anywhere โ dry-run produces a plan without mutation, and re-runs skip already-migrated entries. It is the same tool for interactive workstations and for automated pipelines; only the flags differ.
TL;DR¶
# See what would happen
mytool config migrate-credentials --dry-run
# Migrate to env-var references, interactively (recommended for workstations)
mytool config migrate-credentials
# Migrate in CI / non-interactive, using defaults
mytool config migrate-credentials --yes
# Migrate to the OS keychain (requires keychain backend imported in the tool)
mytool config migrate-credentials --target=keychain --yes
Step-by-step¶
1. Preview¶
Start with a dry-run to confirm the command found the credentials you expect:
Sample output:
Migration plan (dry run โ no changes written):
anthropic.api.key โ anthropic.api.env = ANTHROPIC_API_KEY (target: env)
github.auth.value โ github.auth.env = GITHUB_TOKEN (target: env)
bitbucket.username + bitbucket.app_password โ bitbucket.username.env = BITBUCKET_USERNAME (target: env)
If the plan looks wrong โ missing entries, or a credential listed that isn't actually in your config โ re-check the loaded config path with mytool config list before running for real.
2. Run interactively¶
For each credential the command:
- Prompts for an env var name (default: the upstream-standard name).
- Prints the
export <NAME>=...instructions. - Waits for you to confirm you've exported the variable.
- Verifies the variable is set in the current process.
- Rewrites the config atomically โ the literal is removed and the
.envreference is added in a single file write.
If you bail at step 3 by answering "No, cancel migration", the config is left untouched for that credential and you can re-run later.
3. Skip the wait, override the names¶
If some env vars are already set via /etc/profile, a systemd drop-in, or a password manager's shell integration, skip the verification:
To pin specific env var names (useful when multiple tools share a host and their upstream names collide):
mytool config migrate-credentials \
--env-var anthropic.api.key=MYTEAM_ANTHROPIC_KEY \
--env-var openai.api.key=MYTEAM_OPENAI_KEY
4. Silent migration for CI/CD¶
Drop every prompt:
--yes implies --skip-verify โ the command trusts that the CI platform has injected the required env vars before the tool runs. Combine with --env-var to pin names explicitly, or with --target to pick a different destination mode.
5. Migrate to the OS keychain¶
The keychain target requires the tool's main to blank-import gitlab.com/phpboyscout/go-tool-base/pkg/credentials/keychain. Without that, the command refuses:
Error: keychain target requested but no keychain-capable Backend is registered
Hint: Import gitlab.com/phpboyscout/go-tool-base/pkg/credentials/keychain in your
tool's main, or pass --target=env.
Once a backend is registered:
Each literal credential becomes a keychain entry under <toolname>/<account>. Bitbucket's dual credentials are combined into a single JSON-blob entry under <toolname>/bitbucket.auth.
Cascading the default from config¶
Set the default target per tool so you don't need --target on every invocation:
Explicit --target on the command line still wins over the cascade.
What if it's interrupted?¶
The config file rewrite is atomic (temp-file + rename), so a SIGINT mid-migration leaves the original config.yaml in place. Re-run the command to resume โ already-migrated credentials skip cleanly, and partial-progress (e.g. the keychain Store succeeded but the rewrite didn't) is visible in the second run's dry-run preview.
FAQ¶
Q: Can I migrate back to literal mode?
No โ that's by design. Literal storage is what the command moves away from. If you genuinely need a literal value (throwaway environment, reproducing a bug) set it manually with config set <key> <value>.
Q: Does the command touch secrets that were already env-refs or keychain-refs?
No. Only plaintext literals (*.api.key, *.auth.value, Bitbucket literals) are scanned. Existing .env / .keychain entries are preserved verbatim.
Q: My Bitbucket username has no app_password or vice versa โ what happens?
The command still migrates the half you have (to env-var mode), but the keychain target requires both halves to be present. Partial-pair keychain migration errors with a message explaining which half is missing.
Q: What permissions does the rewritten config file have?
0600 โ owner read/write, nothing for group or world. Matches the initial setup-wizard invariant (R4 in the hardening spec).
Related¶
- Configure credentials โ pick the storage mode when running
init - Custom credential backend โ implement a Vault / AWS SSM / 1Password backend
- Migration guide for v1.12 โ full version upgrade notes