The gist
An agent (an autonomous worker) holding the keys to every door is a disaster waiting to happen. Security in AI development isn't bureaucracy, it's architecture: well-designed boundaries protect you, your client and your reputation.
Terms in this lesson: permission (the right to perform an action), API (application programming interface), agent (an autonomous worker), MCP (Model Context Protocol), bash (the terminal's command language), git (a version control system), hook (a script that reacts to an event), sandbox (an isolated test environment), token (a unit of text for AI), settings.json (the project settings file), workflow (a work process).
Key concepts
.envis the only place for secrets- Claude Code's permission modes and when to use each one
- Storing secrets in production (the live environment)
- Limiting subagent access to protect against accidental damage
Theory
.env: where secrets live
The .env file is an isolated store for sensitive data: API keys, tokens, passwords. It lives in the root of your project and never goes into the repository (where your code is stored).
Example .env structure:
OPENAI_API_KEY=sk-proj-abc123...
ANTHROPIC_API_KEY=sk-ant-...
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
SLACK_BOT_TOKEN=xoxb-...
STRIPE_SECRET_KEY=sk_live_...In your code, you access these values through environment variables:
import os
api_key = os.environ.get("OPENAI_API_KEY")or in Node.js:
const apiKey = process.env.OPENAI_API_KEY;Why you should NEVER hardcode keys in your code:
GitHub and other platforms actively scan public repositories for patterns like sk-ant-, sk-proj-, AIza, Bearer . If a key ends up in a commit, it's considered compromised. Attackers use automated scanners and can start spending your money minutes after you push.
Even if the repository is private, leaks happen: someone accidentally changes the visibility, adds a collaborator, makes a fork. There's one rule: a secret that has ever been in Git history is considered public.
.gitignore: the first line of defense
Before your first git commit, always add .env to .gitignore:
# .gitignore
.env
.env.local
.env.*.local
*.pem
*.key
node_modules/To check, run git status before committing. The .env file should not appear in the list of changes. If it does, stop.
Claude Code permission modes
Claude Code gives you a flexible system for controlling what the agent can do. Which mode to pick depends on how much you trust the task. You switch modes with Shift+Tab right in the session, or with the --permission-mode flag at launch. As of October 2026 there are six modes; the list changes, so check the documentation and the What's current page.
Plan mode (planning only) The agent analyzes the task and builds a plan, but doesn't take any actions. Ideal for getting to know a new codebase or before a complex operation: look at the plan first, then approve it.
claude --permission-mode plan "Add an authentication system"Manual (called default in the config): ask before acting Only reading happens without asking. File edits, commands and network requests need your confirmation. Use it when you're working with someone else's code, with production configuration, or when the task is unusual. Slower, but safer.
Accept edits (acceptEdits): automatic edits The agent edits files and runs simple file commands on its own (create a folder, move, copy) and asks about everything else. Good for well-defined tasks in your own project, where you understand the consequences.
Auto (auto): a reviewing model Instead of you, a separate classifier model reviews every action: it blocks anything that goes beyond your request or looks like a command from untrusted text. Starting with Claude Code version 2.1.283, this is the default starting mode in the terminal and VS Code (if the mode isn't available on your plan or model, the session starts in Manual). Remember: auto reduces the number of questions, but it doesn't guarantee safety. For sensitive operations, check things yourself.
Don't ask (dontAsk) Only reading and pre-approved tools are allowed; everything else is declined without asking. Good for CI and scripts, where there's no person around.
Bypass permissions (bypassPermissions, full trust) The agent can do anything, including running arbitrary commands (--dangerously-skip-permissions turns it on). Never use it in a production environment or on your main computer. Only in isolated containers and virtual machines where losing data doesn't matter. Deny rules (deny) still work even here.
Limiting subagent access
Subagents are helper agents the main agent brings in for subtasks (see the Subagents lesson). You can control what each subagent can do: in the subagent's file, the tools field allows only the listed tools, and disallowedTools forbids specific ones.
Read-only The subagent can read files but not change them. Good for analyst agents: "look at the code and find problems," "read the logs and summarize them."
MCPs only (external tools) The subagent works only through external integrations (Notion, Slack, Google Docs) and doesn't touch local files. Useful for agents that send notifications or update external systems.
Bash only The subagent can run commands but can't edit files directly. For deployment, builds and testing.
A limited agent won't break what it isn't supposed to touch. This is the principle of least privilege, a cornerstone of information security.
Storing secrets in production
For local development, you use .env. In production (the live environment where your service runs), you need dedicated secret stores:
Cloudflare Workers Secrets
wrangler secret put ANTHROPIC_API_KEY
# Enter the value: it gets encrypted and stored in CloudflareOnce saved, the secret can't be viewed. Only the Worker can use it.
Vercel Environment Variables Through the Vercel dashboard or the CLI:
vercel env add ANTHROPIC_API_KEY productionSplit into development, preview and production: different keys for different environments.
The principle: different environments, different keys. Your test key should never be the same as your production key.
Rate limiting and cost control
API requests cost money. Without rate limiting, one bug in your code can drain your budget overnight.
Ways to protect yourself:
- Agent-level limit: at most N requests per minute/hour
- Hard cap: if more than $X is spent in a day, the agent stops and reports it
- Monitoring: a daily report of tokens and money spent
- Test with cheap models: develop on Haiku, and deploy on whatever model the quality requires (current prices and versions: What's current)
Practice
Assignment: Set up a secure environment for a practice project
- Create a new folder
my-agent-project - Initialize Git:
git init - Create a
.gitignorewith the lines.envandnode_modules/ - Create a
.envwith test (fake) variables:Code ANTHROPIC_API_KEY=sk-ant-test-placeholder APP_SECRET=my-super-secret-value - Create a
main.pythat reads the key from the environment (doesn't hardcode it) - Run
git add .andgit status, and make sure.envis NOT on the list - Make your first commit:
git commit -m "Initial setup with secure .env" - Check that
git show HEADdoesn't contain any secret values
Bonus: Try launching Claude Code with different permission modes (claude --permission-mode plan, claude --permission-mode default) and get a feel for how the agent behaves differently. Also add a rule that blocks reading secrets: in .claude/settings.json, under permissions → deny, add Read(./.env).
Claude Code permission levels: quick reference
| Level | What it can do | What it CAN'T do | When to use it |
|---|---|---|---|
| Yes (once) | One specific action | Repeat it without asking | An unfamiliar operation, the first time |
| Yes, and don't ask again | Actions of this type from then on without asking: for file edits until the end of the session, for Bash commands and domains permanently for this repository | Go beyond what you approved | Routine work in a trusted project (git commit, npm test) |
| Plan | Analyze and plan | Change files, run code | A first look at a new codebase |
| Auto | Work without asking, under a reviewing model's watch | Guarantee safety | Long tasks in a project you trust |
| Bypass | Absolutely everything | — | Only an isolated container or VM, NEVER in production |
Common mistakes
1. An API key ended up in a git commit (a saved change) Even if you delete the key in the next commit, it stays in the history forever. The fix: git-secrets + a pre-commit hook (a script that runs before each commit).
# Install git-secrets and add the check
brew install git-secrets
git secrets --install
git secrets --register-aws # for AWS keys2. No .gitignore before the first commit Create .gitignore BEFORE git init or right after. If .env has already been committed, removing it from the history is harder than preventing it.
3. One key for all environments Your test and production keys MUST be different. A bug in dev code shouldn't spend your production budget.
4. Forgetting .env.example Create a .env.example with empty values, so other developers (or you, six months from now) know which variables are needed:
OPENAI_API_KEY=
DATABASE_URL=
SLACK_BOT_TOKEN=5. Bypass permissions on a production project Full trust (--dangerously-skip-permissions) = no protection at all. One wrong rm -rf and the project is gone.
Tools and resources
- Claude Code Permissions: official documentation, code.claude.com/docs/en/permissions
- Claude Code Security: the security model, code.claude.com/docs/en/security
python-dotenv/dotenv(Node.js): loads.envinto environment variablesgit-secrets: a CLI tool that scans commits for secrets- Cloudflare Workers Secrets: production secret store for Workers
- Vercel Environment Variables: production secret store for Vercel deployments
- GitHub Secret Scanning: automatic detection of leaked keys (works automatically for public repositories; for private ones it depends on your plan and settings)
Key takeaways
A secret that ends up in Git history is considered public, even if the repository is private. A limited agent beats a broken production system: least privilege is the foundation of secure architecture.
.gitignoregoes in before the first commit, not after. After is too late.
Related lessons
- API keys and .env: how to get and manage API keys for different services
- Hooks and Hooks LIVE: automatic security checks with pre-commit and pre-tool-use hooks (you can block an accidental commit of a secret)
What's next
The mark stays in this browser only and is never sent anywhere. My progress