Migrating to secure credential storage¶
GTB v1.12 completes Phases 1โ3 of the credential-storage-hardening spec. This guide helps existing users and automated pipelines move off plaintext config.yaml storage onto one of the two secure modes GTB supports:
| Mode | Config records | Secret lives |
|---|---|---|
| Env-var reference (default) | <prefix>.env: <VAR_NAME> |
Your shell profile / CI platform secret |
| OS keychain | <prefix>.keychain: <service>/<account> |
macOS Keychain / Linux Secret Service / Windows Credential Manager |
The gtb config migrate-credentials command automates the move. This guide covers both interactive (workstation) and non-interactive (CI/CD) workflows.
What will be migrated¶
Running migrate-credentials scans the loaded config for every known literal credential key:
- AI providers:
anthropic.api.key,openai.api.key,gemini.api.key - VCS tokens:
github.auth.value,gitlab.auth.value,gitea.auth.value,codeberg.auth.value,direct.auth.value - Bitbucket dual-credential pair:
bitbucket.username+bitbucket.app_password(migrated together as one unit)
Credentials stored in other modes โ <prefix>.env or <prefix>.keychain โ are left untouched.
Interactive migration on a workstation¶
The default flow prompts for each credential: env var name, copy-to-shell instructions, and a verification check that the variable is exported before rewriting the config.
# Preview without mutating anything
mytool config migrate-credentials --dry-run
# Run interactively
mytool config migrate-credentials
The interactive flow looks like:
Env var name for anthropic.api.key
Credential currently stored as literal. Choose the env var that will
hold it. Default is the upstream ecosystem standard.
> ANTHROPIC_API_KEY
Set ANTHROPIC_API_KEY in your shell profile:
export ANTHROPIC_API_KEY=<paste the current anthropic.api.key value>
The config file's literal entry will be cleared; the env var will be the
single source of truth going forward.
Have you set the env var? Yes, continue
After confirmation, the wizard verifies $ANTHROPIC_API_KEY is set in the current process. If not, the migration aborts for that credential โ your literal remains in place until you export the variable and re-run.
Non-interactive migration (CI/CD and scripts)¶
Pass --yes to skip every prompt. Env-var names fall back to the upstream-standard defaults:
# Silent migrate to env-var references using defaults
mytool config migrate-credentials --yes
# Pin a specific env var name for a particular credential
mytool config migrate-credentials --yes \
--env-var anthropic.api.key=MYAPP_ANTHROPIC_KEY \
--env-var openai.api.key=MYAPP_OPENAI_KEY
# Skip the "did you export it?" verification โ useful when the env
# vars are injected by systemd, CI runners, or /etc/profile rather
# than an interactive shell
mytool config migrate-credentials --yes --skip-verify
Migrating to the OS keychain¶
Keychain target requires the tool to have pkg/credentials/keychain imported in its main. The command refuses with an actionable error otherwise:
# Migrate all literals to the registered keychain backend
mytool config migrate-credentials --target=keychain --yes
# Override the keychain service (default: tool name)
mytool config migrate-credentials --target=keychain --yes \
--keychain-service=myteam-mytool
Entries land under <service>/<account>. For Bitbucket, the dual credentials are serialised into a single JSON blob under one account (matching the format the resolver expects). Other credentials use one entry per secret.
Cascading target from config¶
Tools that don't want to require a --target flag on every invocation can set the default in their config:
When present, this acts as the fallback for --target when omitted. Explicit --target on the command line still wins.
Idempotence¶
The command is safe to re-run. Credentials that already have the target configuration (e.g. a prior migration already created anthropic.api.env) are reported as skipped:
Migration complete:
SKIP anthropic.api.key (already migrated)
github.auth.value โ github.auth.env = GITHUB_TOKEN (target: env)
Before / after¶
Before (typical post-interactive-init literal config):
anthropic:
api:
key: sk-ant-api03-<redacted>
github:
auth:
value: ghp_<redacted>
bitbucket:
username: alice
app_password: ATBB-<redacted>
After migrate-credentials --yes (env-var target):
anthropic:
api:
env: ANTHROPIC_API_KEY
github:
auth:
env: GITHUB_TOKEN
bitbucket:
username:
env: BITBUCKET_USERNAME
app_password:
env: BITBUCKET_APP_PASSWORD
After migrate-credentials --target=keychain --yes:
anthropic:
api:
keychain: mytool/anthropic.api
github:
auth:
keychain: mytool/github.auth
bitbucket:
keychain: mytool/bitbucket.auth # single JSON-blob entry for both fields
The rewritten config file is written with 0600 permissions (matching the initial setup) via an atomic temp-file + rename, so an interrupted migration does not corrupt the existing config.
Flag reference¶
| Flag | Default | Effect |
|---|---|---|
--dry-run |
false |
Print the plan; don't write config or touch keychain |
--target=env\|keychain |
env (or credentials.migrate.default_target from config) |
Destination storage mode |
-y, --yes |
false |
Skip all prompts; use defaults for env var names |
--env-var <key>=<name> |
(none) | Override the env var name for a specific credential. Repeatable. |
--skip-verify |
false |
Don't wait for the user to export the variable before rewriting config |
--keychain-service <name> |
props.Tool.Name |
Service portion of the <service>/<account> keychain ref |
Related¶
- How to configure credentials โ the three storage modes in depth
- Credentials component reference โ
Backendinterface,Probe, architecture - Custom credential backend how-to โ implementing Vault / SSM / 1Password backends