---
name: varlock
description: "Secure-by-default environment variable management for Claude Code sessions."
risk: unknown
source: "https://github.com/dmno-dev/varlock"
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

```bash
# ❌ 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

```bash
# ❌ 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

```bash
# ❌ 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

```bash
# ❌ 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

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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)

```bash
# 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

```bash
# 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

```bash
# 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

```yaml
# 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

```dockerfile
# 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"

```bash
# ✅ Safe approach
varlock load 2>&1 | grep "API_KEY"
# Shows: ✅ API_KEY 🔐sensitive └ ▒▒▒▒▒

# ❌ Never do
echo $API_KEY
```

### When User Asks to "Debug authentication"

```bash
# ✅ 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

```bash
# In .env.schema
# @type=string @sensitive
API_KEY=exec('op read "op://vault/item/field"')
```

### AWS Secrets Manager

```bash
# In .env.schema
# @type=string @sensitive
DB_PASSWORD=exec('aws secretsmanager get-secret-value --secret-id prod/db')
```

### Environment-Specific Values

```bash
# In .env.schema
# @type=url
API_URL=env('API_URL_${NODE_ENV}', 'http://localhost:3000')
```

---

## Troubleshooting

### "varlock: command not found"

```bash
# 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"

```bash
# 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"

```bash
# 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:

```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.schema` with all variables defined
- [ ] Mark all secrets with `@sensitive` annotation
- [ ] Add `@defaultSensitive=true` to schema header
- [ ] Add `.env` to `.gitignore`
- [ ] Commit `.env.schema` to version control
- [ ] Add `npm run env:validate` to CI/CD
- [ ] Document secret rotation procedure
- [ ] Never use `cat .env` or `echo $SECRET` in 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 `.env` file, never copy secrets to image
- Use `varlock run` as entrypoint
- See: `~/.claude/skills/docker/SKILL.md`

---

*Last updated: December 22, 2025*
*Secure-by-default environment management for Claude Code*
