skills/env-secrets-manager/SKILL.md
Env & Secrets Manager
npx skillsauth add leoyeai/openclaw-master-skills env-secrets-managerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Tier: POWERFUL Category: Engineering Domain: Security / DevOps / Configuration Management
Complete environment and secrets management workflow: .env file lifecycle across dev/staging/prod, .env.example auto-generation, required-var validation, secret leak detection in git history, and credential rotation playbook. Integrates with HashiCorp Vault, AWS SSM, 1Password CLI, and Doppler.
# .env.example — committed to git (no values)
# .env.local — developer machine (gitignored)
# .env.staging — CI/CD or secret manager reference
# .env.prod — never on disk; pulled from secret manager at runtime
# Application
APP_NAME=
APP_ENV= # dev | staging | prod
APP_PORT=3000 # default port if not set
APP_SECRET= # REQUIRED: JWT signing secret (min 32 chars)
APP_URL= # REQUIRED: public base URL
# Database
DATABASE_URL= # REQUIRED: full connection string
DATABASE_POOL_MIN=2
DATABASE_POOL_MAX=10
# Auth
AUTH_JWT_SECRET= # REQUIRED
AUTH_JWT_EXPIRY=3600 # seconds
AUTH_REFRESH_SECRET= # REQUIRED
# Third-party APIs
STRIPE_SECRET_KEY= # REQUIRED in prod
STRIPE_WEBHOOK_SECRET= # REQUIRED in prod
SENDGRID_API_KEY=
# Storage
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=eu-central-1
AWS_S3_BUCKET=
# Monitoring
SENTRY_DSN=
DD_API_KEY=
Add to your project's .gitignore:
# Environment files — NEVER commit these
.env
.env.local
.env.development
.env.development.local
.env.test.local
.env.staging
.env.staging.local
.env.production
.env.production.local
.env.prod
.env.*.local
# Secret files
*.pem
*.key
*.p12
*.pfx
secrets.json
secrets.yaml
secrets.yml
credentials.json
service-account.json
# AWS
.aws/credentials
# Terraform state (may contain secrets)
*.tfstate
*.tfstate.backup
.terraform/
# Kubernetes secrets
*-secret.yaml
*-secrets.yaml
#!/bin/bash
# scripts/gen-env-example.sh
# Strips values from .env, preserves keys, defaults, and comments
INPUT="${1:-.env}"
OUTPUT="${2:-.env.example}"
if [ ! -f "$INPUT" ]; then
echo "ERROR: $INPUT not found"
exit 1
fi
python3 - "$INPUT" "$OUTPUT" << 'PYEOF'
import sys, re
input_file = sys.argv[1]
output_file = sys.argv[2]
lines = []
with open(input_file) as f:
for line in f:
stripped = line.rstrip('\n')
# Keep blank lines and comments as-is
if stripped == '' or stripped.startswith('#'):
lines.append(stripped)
continue
# Match KEY=VALUE or KEY="VALUE"
m = re.match(r'^([A-Z_][A-Z0-9_]*)=(.*)$', stripped)
if m:
key = m.group(1)
value = m.group(2).strip('"\'')
# Keep non-sensitive defaults (ports, regions, feature flags)
safe_defaults = re.compile(
r'^(APP_PORT|APP_ENV|APP_NAME|AWS_REGION|DATABASE_POOL_|LOG_LEVEL|'
r'FEATURE_|CACHE_TTL|RATE_LIMIT_|PAGINATION_|TIMEOUT_)',
re.I
)
sensitive = re.compile(
r'(SECRET|KEY|TOKEN|PASSWORD|PASS|CREDENTIAL|DSN|AUTH|PRIVATE|CERT)',
re.I
)
if safe_defaults.match(key) and value:
lines.append(f"{key}={value} # default")
else:
lines.append(f"{key}=")
else:
lines.append(stripped)
with open(output_file, 'w') as f:
f.write('\n'.join(lines) + '\n')
print(f"Generated {output_file} from {input_file}")
PYEOF
Usage:
bash scripts/gen-env-example.sh .env .env.example
# Commit .env.example, never .env
git add .env.example
→ See references/validation-detection-rotation.md for details
# Setup
export VAULT_ADDR="https://vault.internal.company.com"
export VAULT_TOKEN="$(vault login -method=oidc -format=json | jq -r '.auth.client_token')"
# Write secrets
vault kv put secret/myapp/prod \
DATABASE_URL="postgres://user:pass@host/db" \
APP_SECRET="$(openssl rand -base64 32)"
# Read secrets into env
eval $(vault kv get -format=json secret/myapp/prod | \
jq -r '.data.data | to_entries[] | "export \(.key)=\(.value)"')
# In CI/CD (GitHub Actions)
# Use vault-action: hashicorp/vault-action@v2
# Write (SecureString = encrypted with KMS)
aws ssm put-parameter \
--name "/myapp/prod/DATABASE_URL" \
--value "postgres://..." \
--type "SecureString" \
--key-id "alias/myapp-secrets"
# Read all params for an app/env into shell
eval $(aws ssm get-parameters-by-path \
--path "/myapp/prod/" \
--with-decryption \
--query "Parameters[*].[Name,Value]" \
--output text | \
awk '{split($1,a,"/"); print "export " a[length(a)] "=\"" $2 "\""}')
# In Node.js at startup
# Use @aws-sdk/client-ssm to pull params before server starts
# Authenticate
eval $(op signin)
# Get a specific field
op read "op://MyVault/MyApp Prod/STRIPE_SECRET_KEY"
# Export all fields from an item as env vars
op item get "MyApp Prod" --format json | \
jq -r '.fields[] | select(.value != null) | "export \(.label)=\"\(.value)\""' | \
grep -E "^export [A-Z_]+" | source /dev/stdin
# .env injection
op inject -i .env.tpl -o .env
# .env.tpl uses {{ op://Vault/Item/field }} syntax
# Setup
doppler setup # interactive: select project + config
# Run any command with secrets injected
doppler run -- node server.js
doppler run -- npm run dev
# Export to .env (local dev only — never commit output)
doppler secrets download --no-file --format env > .env.local
# Pull specific secret
doppler secrets get DATABASE_URL --plain
# Sync to another environment
doppler secrets upload --project myapp --config staging < .env.staging.example
Check if staging and prod have the same set of keys (values may differ):
#!/bin/bash
# scripts/check-env-drift.sh
# Pull key names from both environments (not values)
STAGING_KEYS=$(doppler secrets --project myapp --config staging --format json 2>/dev/null | \
jq -r 'keys[]' | sort)
PROD_KEYS=$(doppler secrets --project myapp --config prod --format json 2>/dev/null | \
jq -r 'keys[]' | sort)
ONLY_IN_STAGING=$(comm -23 <(echo "$STAGING_KEYS") <(echo "$PROD_KEYS"))
ONLY_IN_PROD=$(comm -13 <(echo "$STAGING_KEYS") <(echo "$PROD_KEYS"))
if [ -n "$ONLY_IN_STAGING" ]; then
echo "Keys in STAGING but NOT in PROD:"
echo "$ONLY_IN_STAGING" | sed 's/^/ /'
fi
if [ -n "$ONLY_IN_PROD" ]; then
echo "Keys in PROD but NOT in STAGING:"
echo "$ONLY_IN_PROD" | sed 's/^/ /'
fi
if [ -z "$ONLY_IN_STAGING" ] && [ -z "$ONLY_IN_PROD" ]; then
echo "✅ No env drift detected — staging and prod have identical key sets"
fi
.env to .gitignore on day 1; use pre-commit hooksecho $SECRET; mask vars in CI settingsAPP_SECRET=mysecret is not a secret. Use openssl rand -base64 32testing
AI-powered diary generation for agents - creates rich, reflective journal entries (400-600 words) with Quote Hall of Fame, Curiosity Backlog, Decision Archaeology, Relationship Evolution, mood analytics, weekly digests, "On This Day" resurfacing, and cron auto-generation. Works best with Claude models (Haiku, Sonnet, Opus).
development
Multi-agent UX for OpenClaw Control UI — agent selector, per-agent sessions, session history viewer with search, agent-filtered Sessions tab with friendly names, Create Agent wizard, emoji picker, and backend agent CRUD.
tools
Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", or any task requiring programmatic web interaction.
tools
Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", or any task requiring programmatic web interaction.