From mise-toolkit
Security-focused guide for AI CLI API keys — threat model, storage options (shell rc / keychain / 1Password / doppler), rotation cadence, detection of accidental plaintext leaks, and why `mise set` is never the right answer for secrets. Use when setting up or rotating keys.
npx claudepluginhub ray-manaloto/claude-code-marketplace --plugin mise-toolkitThis skill uses the workspace's default tool permissions.
API keys for AI providers are real money. A leaked Anthropic or OpenAI key can rack up thousands of dollars before you notice. Treat them with the same seriousness as database passwords.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
Guides code writing, review, and refactoring with Karpathy-inspired rules to avoid overcomplication, ensure simplicity, surgical changes, and verifiable success criteria.
Share bugs, ideas, or general feedback.
API keys for AI providers are real money. A leaked Anthropic or OpenAI key can rack up thousands of dollars before you notice. Treat them with the same seriousness as database passwords.
What you're defending against, ranked by likelihood:
.env gets staged, pushed, indexed by bots within minutes.env and the key appears in plaintext in a public CI log.echo $ANTHROPIC_API_KEY during a screen share.Most of your effort should go into preventing #1 and #2.
macOS Keychain (single user, local dev)
security add-generic-password -a "$USER" -s ANTHROPIC_API_KEY -w
# ~/.zshrc:
export ANTHROPIC_API_KEY="$(security find-generic-password -a $USER -s ANTHROPIC_API_KEY -w)"
1Password CLI (teams)
# ~/.zshrc:
export ANTHROPIC_API_KEY="$(op read 'op://Private/Anthropic/credential')"
pass (Linux, GPG-backed)
pass insert api/anthropic
# ~/.bashrc:
export ANTHROPIC_API_KEY="$(pass api/anthropic)"
GNOME libsecret (Linux GUI desktops)
secret-tool store --label='Anthropic API' service anthropic
# ~/.bashrc:
export ANTHROPIC_API_KEY="$(secret-tool lookup service anthropic)"
Doppler / Infisical / Vault (secret managers for teams)
doppler run -- mise run <task>
AWS Secrets Manager (AWS-native prod)
export ANTHROPIC_API_KEY="$(aws secretsmanager get-secret-value --secret-id anthropic/prod --query SecretString --output text)"
Plaintext in ~/.zshrc — readable by anything that reads your home dir. Fine for personal projects where the blast radius is your own account.
Plaintext in .env — same problem, plus the risk of git add . committing it. Always list .env* in .gitignore and double-check with git status before committing.
mise set ANTHROPIC_API_KEY=… — writes to mise.local.toml in plaintext. Not a secret store.mise.toml — commits the key to git. Instant leak.export ANTHROPIC_API_KEY="…" in a project's bin/dev script committed to git — same problem.docker build --build-arg — build args are visible in docker history.redact = true in mise.toml removes the value from mise env output and task logs:
[env]
ANTHROPIC_API_KEY = { required = "…", redact = true }
[redactions]
patterns = ["*_API_KEY", "*_TOKEN", "*_SECRET"]
This is useful: it stops accidents like mise task run deploy 2>&1 | tee log.txt from capturing the key in log.txt.
But redaction is after-the-fact. If the key is already in your shell env, anything that reads /proc/<pid>/environ or runs env by hand still sees it. Redaction protects mise-mediated output, not your whole system.
Rotate:
Providers let you create multiple concurrent keys — use this to rotate without downtime: create new, update env, test, revoke old.
Label keys in the provider dashboard so you know which machine / service is using which key. "ray-macbook-personal", "ci-github-actions", "laptop-2026-04" — specific labels make revocation easy.
The v0.3 lint-dockerfile.sh hook was limited to Dockerfiles. In v0.4, warn-plaintext-api-key.sh runs on every Edit / Write and grep-matches obvious key formats:
sk-ant-… — Anthropicsk-… — OpenAI legacysk-proj-… — OpenAI project keysAIzaSy… — Google API keysIt's non-blocking — just a stderr warning. If it fires, assume the key is compromised and rotate it. Even if the file hasn't been committed yet, the key has now been in your Claude Code session's context window — out of your hands.
Beyond the mise hook, add a real pre-commit hook that blocks commits containing keys:
# .git/hooks/pre-commit
#!/usr/bin/env bash
if git diff --cached | grep -qE '(sk-ant-api[0-9]{2}|sk-proj-|sk-[a-zA-Z0-9]{48}|AIzaSy[a-zA-Z0-9-_]{33})'; then
echo "ERROR: commit contains what looks like an API key. Refusing to commit."
exit 1
fi
Better: use gitleaks or trufflehog — dedicated tools with vastly better regex coverage.
[tools]
"aqua:gitleaks/gitleaks" = "latest"
[tasks."secrets:scan"]
run = "gitleaks detect --source=. --no-git"
Within minutes:
git filter-repo to rewrite history is messy; better to treat the key as burned and move on..env.enc with a simple password — security theater; the password usually ends up in the repo too.mise-ai-cli-setup — the mise.toml pattern.mise-ai-cli-overview — why you're installing these at all.mise-trust-and-security — the broader mise security posture./mise-ai-keys — guided setup that follows these rules.warn-plaintext-api-key.sh — the v0.4 hook that catches obvious leaks.github.com/gitleaks/gitleaks.