Secure environment variable management with Varlock. Use when handling secrets, API keys, credentials, or any sensitive configuration. Ensures secrets are never exposed in terminal, logs, or LLM context.
Provides guidance around integrating varlock into a project, reading/editing .env.schema and other .env files, using the varlock CLI, adding plugins and framework integrations.
Trigger phrases include "environment variable", "env var", "secrets", ".env", "API key", "credentials", "sensitive", "varlock"
Secure environment variable management with Varlock. Use when handling secrets, API keys, credentials, or any sensitive configuration. Ensures secrets are never exposed in terminal, logs, or LLM context.
Provides guidance around integrating varlock into a project, reading/editing .env.schema and other .env files, using the varlock CLI, adding plugins and framework integrations.
Trigger phrases include "environment variable", "env var", "secrets", ".env", "API key", "credentials", "sensitive", "varlock"
Varlock
This skill helps securely manage env vars and secrets in your project using varlock.
Varlock uses .env.schema (instead of .env.example) to provide a single source of truth for your project's env vars. Schema info is expressed using @decorator style comments. Sensitive values can be set in git-ignored .env.local files, passed in via the environment, or use functions to load from secure backends like 1Password, Vault, AWS, etc.
Basic .env.schema example:
# @defaultSensitive=false @defaultRequired=infer
# @currentEnv=$APP_ENV
# @generateTsTypes(path=env.d.ts)
# ---
# @type=enum(dev, staging, prod)
APP_ENV=dev
# @type=url
API_URL=https://api.example.com
# Description of this var
# @sensitive @required @type=string(startsWith=sk-)
# @docs(https://xyzapi.com/docs/auth)
XYZ_API_KEY=
Your .env.schema is committed to version control and safe for agents to read and update. The varlock CLI helps load and validate env vars while masking anything sensitive, and can securely inject env vars into commands.
NOTE: If varlock is installed locally via package.json (not as a standalone binary), invoke it via your package manager — e.g., , , . Check the project's package manager before running CLI commands.
pnpm exec varlock load
bunx varlock load
npm exec varlock load
CRITICAL: Security rules
These rules are non-negotiable:
Do not expose secrets
# NEVER do these - exposes secrets to agent contextcat .envcat .env.local
echo$SECRET_KEYprintenv | grep API
# SAFE alternatives
varlock load --agent # JSON output, sensitive values redacted
varlock load # human-readable, sensitive values maskedcat .env.schema # schema only, no secret values
If the user needs to see a sensitive value, tell them to run varlock reveal VAR_NAME.
File access rules
Safe to read and edit:.env.schema and any other git-committed .env files (usually env-specific files like .env.development)
Do not read or edit:.env, .env.local, .env.[env].local, or other gitignored value/override files — these may contain unencrypted secrets
Do not log or quote raw secret values in code, comments, or chat
Sensitivity rules
Items marked @sensitive must not have that decorator removed without confirming with the user
Ask the user to edit secret values in their local/gitignored env files or their secret provider (1Password, AWS, etc.) — never fill in secrets yourself
When the user asks to "show me the .env file"
Do not read .env or .env.local directly. Instead run varlock load to show masked values, or read .env.schema to show the schema. Explain that reading env files directly could expose secrets.
When the user asks to "update/set a secret"
Do not write secret values yourself. Tell the user to either:
Update it in their secret provider (1Password, AWS, etc.) and then help them wire it up
Edit the value in their .env.local file manually
ideally encrypt it by using varlock(prompt) as the value, then run varlock load to be prompted
Ensure .env.schema and tracked env-specific files are not gitignored (!.env.schema, !.env.production, etc. in .gitignore if needed).
Environment-specific files and precedence
When @currentEnv is set in .env.schema (e.g., @currentEnv=$APP_ENV), varlock automatically loads matching environment-specific files. Files are applied in increasing precedence order:
For example, if APP_ENV=staging, then .env.staging and .env.staging.local will be loaded automatically if they exist. A value in .env.local overrides one in .env.schema, and process.env always wins.
Schema syntax
Root decorators (file header)
Root decorators go in comment blocks at the top of the file, before the first item. A # --- divider usually separates the header from items.
Import schema/values from another .env file or directory
—
@plugin(@varlock/name-plugin)
Load a plugin
—
@setValuesBulk(resolver)
Inject multiple values from an external source
—
@disable
Disable loading this file (can use =forEnv(test))
false
@defaultSensitive defaults to true — all items are sensitive unless explicitly marked @public or @sensitive=false. Set @defaultSensitive=false to flip the default.
@defaultRequired=infer (the default): items with a value in the schema are required, items without are optional
@defaultSensitive=inferFromPrefix(PUBLIC_): items with keys starting with PUBLIC_ are not sensitive, all others are
@import() accepts enabled=expr for conditional imports and allowMissing=true for optional imports
Item decorators
Decorators in comment lines directly preceding a config item are attached to that item. A blank line breaks the association.
Decorator
Purpose
@required / @optional
Override default required state
@sensitive / @public
Override default sensitive state
@type=dataType
Set validation/coercion type
@example="value"
Example value (for docs, not used at runtime)
@docs(url) or @docs(label, url)
Link to related documentation (can be used multiple times)
@icon=collection:name
Iconify icon ID for generated docs
@auditIgnore
Suppress "unused in code" warning from varlock audit
Decorator values can use resolver functions: @required=forEnv(prod), @sensitive=not(forEnv(dev)).
Plain string is the default — do not add @type=string, just omit @type entirely. Only use @type when you need a specific type or string constraints. See https://varlock.dev/reference/data-types/
Resolver functions (values)
Instead of static values, items can use resolver functions:
# Reference another item ($VAR and ${VAR} are shorthand for ref(VAR))
FULL_URL=${API_URL}/v2/users
# Execute a CLI command
SECRET=exec(`op read "op://vault/item/field"`)
# Conditional logic
API_URL=if(eq($APP_ENV, prod), https://api.example.com, http://localhost:3000)
# First non-empty value
FALLBACK_VAR=fallback($PRIMARY, $SECONDARY, "default")
# Map one value to another
APP_ENV=remap($CI_BRANCH, "main", production, /.*/, preview, undefined, development)
# Check environment (based on @currentEnv)
# @required=forEnv(prod, staging)
PROD_ONLY_KEY=
Varlock plugins let you declaratively reference secrets from external providers directly in your .env.schema. The references are safe to commit — actual values are fetched at load time.
Each plugin provides its own resolver functions (e.g., op() for 1Password, awsSecret() for AWS). See Plugins below for the full list and https://varlock.dev/guides/plugins/ for setup details.
Approach 2: Local encryption with varlock() (git-ignored files)
For secrets stored locally in git-ignored files like .env.local, use the varlock() function for device-local encryption so nothing is stored in plaintext:
# Encrypted value — decrypted automatically at load time
API_KEY=varlock("local:<encrypted-payload>")
# Prompt mode — on next `varlock load`, user is prompted to enter the value
# which is encrypted and written back to this file automatically
NEW_SECRET=varlock(prompt)
How to encrypt values:
Interactive prompt: Set the value to varlock(prompt) and run varlock load — the user will be prompted securely, and the encrypted value replaces the placeholder automatically
Encrypt in bulk:varlock encrypt --file .env.local encrypts all sensitive plaintext values in-place
Encrypt a single value:varlock encrypt prompts for a value and prints the encrypted result to copy/paste
Pipe via stdin: To encrypt a value without exposing it in your context (e.g., a generated key or a value read from another tool), pipe it into varlock encrypt:
This keeps the plaintext secret out of shell history and agent context.
Encryption is hardware-backed on macOS (Secure Enclave + Touch ID), Windows (DPAPI + Windows Hello), and Linux (TPM2), with a file-based fallback on all platforms. On macOS, keychain() is also available as a built-in alternative that stores values in the system keychain.
Pick the official integration for the project's framework — do not guess. Check https://varlock.dev/integrations/overview/ for the specific guide (Next.js, Vite, Astro, SvelteKit, Bun, Cloudflare, Expo, etc.).
Typical steps:
Confirm varlock is installed (varlock init --agent or existing dependency)
Follow the integration guide for build/dev wiring, generated types, and any required config
Prefer the integration's recommended entry point (varlock/auto-load, Vite plugin, etc.) over ad-hoc process.env usage
When a framework integration is active, it handles loading and injecting env vars automatically — varlock run is not needed for the framework's own dev/build commands. Only use varlock run -- <cmd> for other scripts or tools that the integration doesn't cover (e.g., one-off migrations, CLI tools, non-JS commands).
Review auto-generated items — heuristics are not final
Add description comments where names are not self-explanatory
Set @type only when not a plain string (omit @type=string)
Mark @required / @optional as needed (or adjust root @defaultRequired)
Confirm @sensitive on secrets, keys, tokens, and credentials with the user
Move useful values to @example; delete dummy placeholders
Add @docs() links where helpful
Remove redundant values from other .env files after defaults move into the schema
Validation loop
After schema changes:
varlock load --agent
Fix schema and tracked env files based on validation errors. Do not patch gitignored .local value files to silence schema errors — ask the user to update secrets locally.
CLI quick reference
Run varlock --help or varlock <command> --help for full flags and options.
Scan files for leaked secrets (--staged for pre-commit, --install-hook to set up)
varlock audit
Detect drift between schema and code usage
varlock codegen
Explicitly trigger code generation from schema (usually triggered automatically; typegen is a deprecated alias)
varlock lock
Lock biometric session (requires re-auth on next decrypt)
Updating an existing project
Keep .env.schema as the source of truth. Edit schema and tracked .env.[env] files only — not gitignored .local files.
Schema changes — add/remove/rename items in .env.schema, update code to match, then varlock load --agent
Secrets — leave sensitive values empty in schema; ask the user to set them locally or in their secret provider
Plugins — add @plugin() in the header and prefer plugin resolvers over raw exec() when available
Codegen — @generateTsTypes (and the other @generate*Env decorators) run on load by default; use auto=false and varlock codegen if you need explicit control
Before commit — varlock load --agent, then varlock scan --staged; run varlock audit if you renamed keys or suspect drift
For details beyond this skill, use the Varlock Docs MCP tool if installed in your AI tool, or refer to https://varlock.dev/guides/schema as a starting point.