varlock
Secure-by-default environment variable management for Claude Code sessions.
- risk
- unknown
- version
- 1.0.0
<!-- security-allowlist: curl-pipe-bash -->
Varlock Security Skill
Secure-by-default environment variable management for Claude Code sessions.
Repository: https://github.com/dmno-dev/varlock Documentation: https://varlock.dev
Core Principle: Secrets Never Exposed
When working with Claude, secrets must NEVER appear in:
- Terminal output
- Claude's input/output context
- Log files or traces
- Git commits or diffs
- Error messages
This skill ensures all sensitive data is properly protected.
CRITICAL: Security Rules for Claude
Rule 1: Never Echo Secrets
# ❌ NEVER DO THIS - exposes secret to Claude's context echo $CLERK_SECRET_KEY cat .env | grep SECRET printenv | grep API # ✅ DO THIS - validates without exposing varlock load --quiet && echo "✓ Secrets validated"
Rule 2: Never Read .env Directly
# ❌ NEVER DO THIS - exposes all secrets cat .env less .env Read tool on .env file # ✅ DO THIS - read schema (safe) not values cat .env.schema varlock load # Shows masked values
Rule 3: Use Varlock for Validation
# ❌ NEVER DO THIS - exposes secret in error test -n "$API_KEY" && echo "Key: $API_KEY" # ✅ DO THIS - Varlock validates and masks varlock load # Output shows: API_KEY 🔐sensitive └ ▒▒▒▒▒
Rule 4: Never Include Secrets in Commands
# ❌ NEVER DO THIS - secret in command history curl -H "Authorization: Bearer sk_live_xxx" https://api.example.com # ✅ DO THIS - use environment variable curl -H "Authorization: Bearer $API_KEY" https://api.example.com # Or better: varlock run -- curl ...
Quick Start
Installation
# Install Varlock CLI curl -sSfL https://varlock.dev/install.sh | sh -s -- --force-no-brew # Add to PATH (add to ~/.zshrc or ~/.bashrc) export PATH="$HOME/.varlock/bin:$PATH" # Verify varlock --version
Initialize Project
# Create .env.schema from existing .env varlock init # Or create manually touch .env.schema
Schema File: .env.schema
The schema defines types, validation, and sensitivity for each variable.
Basic Structure
# Global defaults # @defaultSensitive=true @defaultRequired=infer # Application # @type=enum(development,staging,production) @sensitive=false NODE_ENV=development # @type=port @sensitive=false PORT=3000 # Database - SENSITIVE # @type=url @required DATABASE_URL= # @type=string @required @sensitive DATABASE_PASSWORD= # API Keys - SENSITIVE # @type=string(startsWith=sk_) @required @sensitive STRIPE_SECRET_KEY= # @type=string(startsWith=pk_) @sensitive=false STRIPE_PUBLISHABLE_KEY=
Security Annotations
| Annotation | Effect | Use For |
|---|---|---|
@sensitive | Redacted in all output | API keys, passwords, tokens |
@sensitive=false | Shown in logs | Public keys, non-secret config |
@defaultSensitive=true | All vars sensitive by default | High-security projects |
Type Annotations
| Type | Validates | Example |
|---|---|---|
string | Any string | @type=string |
string(startsWith=X) | Prefix validation | @type=string(startsWith=sk_) |
string(contains=X) | Substring validation | @type=string(contains=+clerk_test) |
url | Valid URL | @type=url |
port | 1-65535 | @type=port |
boolean | true/false | @type=boolean |
enum(a,b,c) | One of values | @type=enum(dev,prod) |
Safe Commands for Claude
Validating Environment
# Check all variables (safe - masks sensitive values) varlock load # Quiet mode (no output on success) varlock load --quiet # Check specific environment varlock load --env=production
Running Commands with Secrets
# Inject validated env into command varlock run -- npm start varlock run -- node script.js varlock run -- pytest # Secrets are available to the command but never printed
Checking Schema (Safe)
# Schema is safe to read - contains no values cat .env.schema # List expected variables grep "^[A-Z]" .env.schema
Common Patterns
Pattern 1: Validate Before Operations
# Always validate environment first varlock load --quiet || { echo "❌ Environment validation failed" exit 1 } # Then proceed with operation npm run build
Pattern 2: Safe Secret Rotation
# 1. Update secret in external source (1Password, AWS, etc.) # 2. Update .env file manually (don't use Claude for this) # 3. Validate new value works varlock load # 4. If using GitHub Secrets, sync (values not shown) ./scripts/update-github-secrets.sh
Pattern 3: CI/CD Integration
# GitHub Actions - secrets from GitHub Secrets - name: Validate environment env: DATABASE_URL: ${{ secrets.DATABASE_URL }} API_KEY: ${{ secrets.API_KEY }} run: varlock load --quiet
Pattern 4: Docker Integration
# Install Varlock in container RUN curl -sSfL https://varlock.dev/install.sh | sh -s -- --force-no-brew \ && ln -s /root/.varlock/bin/varlock /usr/local/bin/varlock # Validate at container start CMD ["varlock", "run", "--", "npm", "start"]
Handling Secret-Related Tasks
When User Asks to "Check if API key is set"
# ✅ Safe approach varlock load 2>&1 | grep "API_KEY" # Shows: ✅ API_KEY 🔐sensitive └ ▒▒▒▒▒ # ❌ Never do echo $API_KEY
When User Asks to "Debug authentication"
# ✅ Safe approach - check presence and format varlock load # Validates types and required fields # Check if key has correct prefix (without showing value) varlock load 2>&1 | grep -E "(CLERK|AUTH)" # ❌ Never do printenv | grep KEY
When User Asks to "Update a secret"
Claude should respond: "I cannot directly modify secrets for security reasons. Please: 1. Update the value in your .env file manually 2. Or update in your secrets manager (1Password, AWS, etc.) 3. Then run `varlock load` to validate I can help you update the .env.schema if you need to add new variables."
When User Asks to "Show me the .env file"
Claude should respond: "I won't read .env files directly as they contain secrets. Instead: - Run `varlock load` to see masked values - Run `cat .env.schema` to see the schema (safe) - I can help you modify .env.schema if needed"
External Secret Sources
1Password Integration
# In .env.schema # @type=string @sensitive API_KEY=exec('op read "op://vault/item/field"')
AWS Secrets Manager
# In .env.schema # @type=string @sensitive DB_PASSWORD=exec('aws secretsmanager get-secret-value --secret-id prod/db')
Environment-Specific Values
# In .env.schema # @type=url API_URL=env('API_URL_${NODE_ENV}', 'http://localhost:3000')
Troubleshooting
"varlock: command not found"
# Check installation ls ~/.varlock/bin/varlock # Add to PATH export PATH="$HOME/.varlock/bin:$PATH" # Or use full path ~/.varlock/bin/varlock load
"Schema validation failed"
# Check which variables are missing/invalid varlock load # Shows detailed errors # Common fixes: # - Add missing required variables to .env # - Fix type mismatches (port must be number) # - Check string prefixes match schema
"Sensitive value exposed in logs"
# 1. Rotate the exposed secret immediately # 2. Check .env.schema has @sensitive annotation # 3. Ensure using varlock commands, not echo/cat # Add missing sensitivity: # Before: API_KEY= # After: # @type=string @sensitive # API_KEY=
npm Scripts
Add these to your package.json:
{ "scripts": { "env:validate": "varlock load", "env:check": "varlock load --quiet || echo 'Environment validation failed'", "prestart": "varlock load --quiet", "start": "varlock run -- node server.js" } }
Security Checklist for New Projects
- Install Varlock CLI
- Create
.env.schemawith all variables defined - Mark all secrets with
@sensitiveannotation - Add
@defaultSensitive=trueto schema header - Add
.envto.gitignore - Commit
.env.schemato version control - Add
npm run env:validateto CI/CD - Document secret rotation procedure
- Never use
cat .envorecho $SECRETin Claude sessions
Quick Reference Card
| Task | Safe Command |
|---|---|
| Validate all env vars | varlock load |
| Quiet validation | varlock load --quiet |
| Run with env | varlock run -- <cmd> |
| View schema | cat .env.schema |
| Check specific var | varlock load | grep VAR_NAME |
| Never Do | Why |
|---|---|
cat .env | Exposes all secrets |
echo $SECRET | Exposes to Claude context |
printenv | grep | Exposes matching secrets |
| Read .env with tools | Secrets in Claude's context |
| Hardcode in commands | In shell history |
Integration with Other Skills
Clerk Skill
- Test user passwords are
@sensitive - Test emails are
@sensitive=false(contain +clerk_test, not secret) - See:
~/.claude/skills/clerk/SKILL.md
Docker Skill
- Mount
.envfile, never copy secrets to image - Use
varlock runas entrypoint - See:
~/.claude/skills/docker/SKILL.md
Last updated: December 22, 2025 Secure-by-default environment management for Claude Code