API key hygiene
the complete guide
The six principles from Part 1 of the workshop, deepened. Provider-specific setup. Secret-manager integration. A 15-minute incident playbook. Web-UI vs dotfile comparison.
Why key placement is a board-level issue
Tip: Ctrl+P (Cmd+P) prints the 6-principle cards as a 1-page A4 reference.
API keys aren't a developer-side ops item anymore. They're a risk asset the board should know about. A single key leaked to GitHub can rack up millions of yen in API charges inside a few hours, and we've seen public examples every month since 2024, inside Japan and out. OpenAI, Anthropic, and Google all run automated detection and emergency revocation, but the charges can land before revocation fires. And the bill isn't the deep wound โ the data behind the key is.
At MIXI, running multiple agents (CEO / CFO / CTO, and so on) means you're not managing one key โ you're managing four, then ten. The moment those keys scatter across chat windows, Slack threads, and Notion pages, control collapses. The opposite works too: when the CEO can say the six principles out loud โ don't paste into chat, use .env, check .gitignore, rotate on a schedule, least privilege, separate production from development โ the team tightens up. This guide gives an executive 20 minutes of the operational knowledge needed to get there.
6 principles โ one deep dive each
1. Never paste into a chat window
The moment you paste a key into a chat input, the key rides into the provider's input log. OpenAI, Anthropic, and Google all state on commercial plans that input data isn't used for model training, but it's retained for some period for operational logs, safety classification, and incident triage. Putting a key on screen means accepting that a trace of it now sits on someone else's servers. Paste it into an internal Slack or Notion and it's also inside your Workspace admin's search scope โ a former employee's export history can surface it too. The CEO testing tools is exactly when the rule shouldn't have exceptions. When you ask Claude Code to "create a .env for me", don't echo the value in the terminal either โ type it directly into the editor. "Don't look at the key with your eyes, don't show it on screen, don't hand it to an LLM" โ those three together are the first gate. Domestic example: in 2024 an executive at a Japanese startup pasted a key into a chat window during a demo, and within minutes high-rate calls from overseas racked up a bill in the hundreds of thousands of yen. It showed up on Twitter.
2. Route through .env
The industry standard is to keep keys out of source code and hold them in environment variables or a .env file. Python, Node.js, and Go all have dotenv-style libraries that expect startup-time loading. Drop the file at the project root and have the app read process.env.OPENAI_API_KEY. No key text ever lives in code, so you can share the repo, paste chat transcripts, and nothing leaks. For ai-executive it's the same idea โ the four keys (OPENAI_API_KEY / OPENAI_API_KEY_CEO / OPENAI_API_KEY_CFO / OPENAI_API_KEY_CTO) live in .env. In the workshop we ask Claude Code to "create a .env, I'll fill the values by hand later", so the template is generated without the values ever reaching the LLM. A typical .env looks like this.
# .env (not tracked by git)
OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxxxxxxxxx
OPENAI_API_KEY_CEO=sk-proj-yyyyyyyyyyyyyyyy
OPENAI_API_KEY_CFO=sk-proj-zzzzzzzzzzzzzzzz
OPENAI_API_KEY_CTO=sk-proj-wwwwwwwwwwwwwwww
The matching .env.example lists only the key names, with empty values. That one you can commit. New team members see at a glance which slots need filling.
3. Check .gitignore, always
Splitting the key into .env means nothing if .env isn't in .gitignore. An accidental git add, a push, and then โ best case โ GitHub Secret Scanning catches it and reports automatically to the provider, and the key dies. Worst case, an attacker's scanner finds it first. And remember: if a repo gets flipped to public, every past commit is exposed at once. The required checks are three: (1) is .env in .gitignore, (2) does git status show .env as untracked, (3) if it's already committed, detach it with git rm --cached .env. GitHub Secret Scanning runs free on public repos and auto-detects key formats from OpenAI, Anthropic, AWS, Stripe, and other major providers. Turn on push protection and commits containing a key get blocked at push time.
# .gitignore (minimum)
.env
.env.local
.env.*.local
*.pem
secrets/
A 2024 Japanese example: a consumer-app company committed an OpenAI key, Secret Scanning caught it, OpenAI revoked it immediately โ but for the ~20 minutes in between, logs showed attackers probing with adjacent keys. The detection system is there, but it's not absolute.
4. Rotate on a schedule
Treat every key as "will leak eventually" from the moment it's created. The usual cadence is every 90 days, plus immediate rotation on leak detection, departures, or vendor change. To avoid service outages from sudden swaps, document a staged rotation in the SOP. Five steps: (1) generate the new key, store in Secrets Manager, (2) update the app's env vars to the new key and verify with canary traffic, (3) run both in parallel for 7 days, (4) disable the old key, (5) confirm retirement at monthly review and reconcile logs. If you're on a cloud Secret Manager, versioning lets you flip between old and new with a single flag. Once rotation is a routine procedure, the team stops being afraid of it โ same principle as the safety KY meeting at a construction site. The CEO putting it on the annual calendar is what makes it stick. Two KPIs for exec review: rotation completion rate over the last 12 months (target 100%) and longest-lived key (target โค 90 days).
5. Least privilege
One key should never serve multiple purposes. The ai-executive model โ "4 agents = 4 keys" โ is the ideal. If one agent gets compromised, the others are untouched; the blast radius stays small. In OpenAI, you can create per-Project API keys within an Organization and set model restrictions, monthly caps, IP restrictions, and allowed endpoints per Project. Anthropic Console lets you set spend caps and permission boundaries per Workspace. Same thinking as AWS IAM โ narrow what each key can do. Production key gets admin, dev key gets read-only, CI/CD key only calls specific models. And โ retire unused keys immediately. Don't keep keys around "in case we need them later"; they leak when no one's watching. Both Anthropic and OpenAI show last-used timestamps per key on the dashboard. Make "30 days unused = candidate for retirement" an explicit rule.
6. Separate production and development keys
Never mix production and dev keys. It's the bank-vault-vs-wallet distinction. If dev code accidentally grabs a production key, one afternoon of "just playing around" burns through a hundred thousand yen of tokens. The most reliable split is naming. Recommended template: {env}-{service}-{role}-{YYYYMMDD}. Examples: prod-oai-ceo-20260421 / dev-oai-ceo-20260421 / workshop-20260421-demo. The prefix tells you prod or dev at a glance. Do the same at the env-var level โ PROD_OPENAI_API_KEY vs DEV_OPENAI_API_KEY โ and have the app pick based on environment. For workshops and one-off demos, the ideal is a disposable key made the day before and revoked within 24 hours. The keys for today's workshop follow that rule โ they'll be invalidated as soon as we finish. When this rhythm becomes habit, you stop second-guessing "is this prod, demo, or my personal dev key?"
Web UI vs dotfile (.env) โ which one should users touch?
There are two ways to get an API key from the user into the app:
- Edit the dotfile directly โ open
.envin a terminal or IDE and typeOPENAI_API_KEY=sk-.... Engineer-standard. - A web-UI settings page โ the app exposes a
/settingspage with a password input. The app writes to.envbehind the scenes.
For this workshop we chose (2), the web UI. Why:
| Aspect | dotfile | Web UI |
|---|---|---|
| Friction on Windows | Hidden dotfiles, complex paths, launch an editor | Zero. One browser tab, done |
| UX for non-engineers | Needs a terminal or editor | Same password field they see in every SaaS |
| Screen-share risk | Editor shows the value | Password input hides it |
| Principle 2 (via .env) | Direct write | App writes to the same .env (same mechanism) |
| Principle 3 (.gitignore) | Human must remember | App auto-checks and appends on Save |
| Principle 4 (rotation) | Manual edit | Dedicated button, one click |
| Principle 6 (prod/dev split) | Separate files or environments | dev/prod tabs in the UI |
| Validation | You find out it's wrong at runtime | On Save, the app makes a test call to OpenAI |
| Audit log | Just a file write | Who / when / which key changed โ recordable |
Minimum requirements for a web-UI settings page
<input type="password">with a show/hide toggle- On Save, validate each key with a lightweight provider API call
- After save, display masked (last 4 chars only)
- App writes
.envatomically (rename trick) - App checks
.gitignoreat startup, appends if missing - Rotate button (validate new key โ replace old key)
- Accept requests from localhost only โ no remote access to the settings page
- Chmod 600 on the written file
- Audit log (who, when, which key changed โ never the value itself)
- If no key is set on startup, auto-redirect to
/settings
Anti-patterns
- Key value in
<input type="text">โ leaks via screen share, screen recording, or just eyeballs - Passing the key to the frontend โ browser JS, DevTools, and memory dumps will find it. Keep it on the backend
- Skipping validation on Save โ a bad key gets persisted, you find out during the demo
- Logging the full key โ violates principle 1, and leaks will follow
- /settings accessible from remote โ anyone can rewrite keys. Localhost-only, no exceptions
- Requiring reload after save โ bad UX, and ambiguous about when the new key took effect. In-memory cache with instant apply is the right answer
Provider-specific setup
The four major providers lined up by console path, env-var name, and scoping features. ai-executive runs on OpenAI, but this is the reference table if you later switch to Anthropic, Google, or Azure.
| Provider | Console | Env var | Scope controls |
|---|---|---|---|
| OpenAI | platform.openai.com โ Settings โ API keys โ Create (Project scope) | OPENAI_API_KEY |
Project / Model / IP allow / monthly cap |
| Anthropic | console.anthropic.com โ Settings โ API Keys โ select Workspace | ANTHROPIC_API_KEY |
Workspace / spend cap / Admin vs Member |
| Google AI Studio / Vertex | aistudio.google.com / console.cloud.google.com โ IAM & Admin โ Service Accounts | GOOGLE_API_KEY / GOOGLE_APPLICATION_CREDENTIALS |
IAM role / Project / region pin |
| Azure OpenAI | portal.azure.com โ Azure OpenAI resource โ Keys and Endpoint | AZURE_OPENAI_API_KEY + AZURE_OPENAI_ENDPOINT |
Per Deployment / RBAC / Private Endpoint |
All of these show the key value only once at creation time. Save it to a Secret Manager the moment it appears. If you lose it, the standard answer is to regenerate.
Secret-manager integration
A .env file is fine on a developer's laptop. For production, CI/CD, and team sharing, promote to a proper secret manager. Four options, where each one fits.
AWS Secrets Manager
- Dynamic fetch from Lambda / EC2 / ECS via IAM role
- Built-in auto-rotation (Rotation Lambda)
- Audit lands in CloudTrail
aws secretsmanager get-secret-value \
--secret-id prod/openai/ceo \
--query SecretString --output text
1Password (Business / Teams)
- Best fit for human teams sharing secrets โ fine-grained Share permissions by role
- 1Password CLI (
op) injects into local dev machines - Access logs are on the admin dashboard for monthly review
export OPENAI_API_KEY=$(op read "op://Shared/OpenAI-CEO/credential")
Doppler
- Central cloud management of env vars โ same UI distributes to CI/CD, prod, dev
- The smoothest GitHub Actions integration of any of these
- A cross-project dashboard that makes inventory easy
doppler run -- npm start
GitHub Secrets (for Actions)
- Repo Settings โ Secrets and variables โ Actions
- Reference from workflows as
${{ secrets.OPENAI_API_KEY }} - Use Environments to separate prod and staging with required approvals
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
The 15-minute leak playbook
Treat any of these as an incident and start acting within 15 minutes: an API key accidentally committed, GitHub Secret Scanning alert, unusual cost spike (5x week-over-week or more), API calls from an unexpected geography, suspected prompt injection, or unauthorized tool execution.
| Time | Owner | Action | Deliverable |
|---|---|---|---|
| T+0 | Detector | Incident report in Slack #security-alerts |
Short written report |
| T+3 | Security lead | Escalate to Anthropic / OpenAI support | Support ticket ID |
| T+5 | Dev lead | Revoke / disable the suspect key from the console immediately | Revocation confirmed |
| T+7 | Auditor | Pull last 24h of API logs, check for anomalies (who called what) | Log analysis report |
| T+10 | Security lead | Update all teams + decide on exec notification | Risk assessment |
| T+15 | Named owner | Generate and deploy new keys; lock in the rotation schedule | Recovery plan |
[email protected]; Security HackerOne anthropic-vdp. For OpenAI, open a ticket at help.openai.com; severe incidents go direct to your account manager.
MIXI recommended operating template
The principles above, dropped into concrete MIXI operating rules โ the granularity an IT lead can paste into an approval memo.
Naming convention
Format: {env}-{service}-{role}-{YYYYMMDD}
prod-oai-ceo-20260421dev-oai-cfo-20260421workshop-20260421-democi-oai-deploy-20260421
Prefix plus date gives you readability and searchability at the same time.
Rotation cadence
- Production keys: every 90 days (end of quarter)
- Dev keys: every 180 days
- Workshop / demo keys: revoke within 24h of the event
- Departure: rotate all relevant keys that same day
Put it in the shared calendar. Anything automatable goes into a Rotation Lambda.
Monthly audit log review
- Diff of key creation and deletion history
- Abnormal call patterns (geo, rate, error %)
- List of keys unused for 30 days โ retire decision
- Reconcile Secret Scanning notifications
Standing agenda item at the monthly InfoSec meeting, signed off by an exec.
Escalation chain
- Detector โ
#security-alerts - Security lead โ on-call Dev lead
- Dev lead โ provider Support
- T+15: exec notification (CTO / CISO)
- T+24h: post-incident review
Print on an escalation card. One on each owner's desk.
Training touchpoints
- New-hire onboarding: 15-min lecture on the 6 principles
- Built into the annual all-hands security training
- Latest incident cases at the quarterly KY meeting
- For executives: re-run this workshop once a year
Rules beat intentions. Culture beats rules. Catchphrases from the CEO beat culture.
- Anthropic Trust Center
- Privacy Center
- Claude Help Center
- Claude API Documentation
- Claude Code Docs
- Anthropic Legal Docs
- Security Report โ HackerOne
- Zero Data Retention โ Claude Code Docs
- Set up Single Sign-On (SSO)
- Restrict Access with IP Allowlisting
- GitHub Secret Scanning
- OpenAI API keys โ Authentication
- AWS Secrets Manager Documentation