This is the full developer documentation for varlock
# Best Practices for using @env-spec
> Recommended best practices for writing .env files with @env-spec
While the parser itself is fairly flexible to maintain backwards-compatibility, we recommend some best practices if you are writing a new .env file or cleaning up an existing one.
* Only use triple quotes for multi-line strings. Either backticks or double quotes are fine, but stay consistent.
* Only use unwrapped values for simple strings that do not contain any spaces or special characters.
* Single quotes do not support expansion, so use them if an item contains $ characters you do not want expanded.
* Stay consistent with quote usage in general
* Be generous with descriptions, unless it is totally obvious
* Add documentation links wherever possible
* For ref expansion, always use brackets - `` GOOD=`pre-${OTHERVAR}` `` `BAD=pre-$OTHERVAR`
* use `# --- dividers ---` to organize sections of related items
* Make all keys `ALL_CAPS`, and don’t use any ”-” or ”.”
* Don’t use extra whitespace around item definitions (ex: `KEY="good!"` `KEY = "bad!"`)
* Don’t use optional `export` prefix
# About @env-spec
> Understanding the env-spec specification and how varlock implements it
Contribute to @env-spec
The `@env-spec` specification is currently in development. If you’d like to contribute, please join the [discussion](https://github.com/dmno-dev/varlock/discussions/17) in the RFC on GitHub.
## Overview
[Section titled “Overview”](#overview)
@env-spec is a DSL that extends normal `.env` syntax. It allows adding structured metadata using `@decorator` style comments (similar to [JSDoc](https://jsdoc.app/)) and a syntax for setting values via explicit function calls.
This lets us express a declarative schema of our environment variables in a familiar format, not tied to any specific programming language or framework.
### A short example:
[Section titled “A short example:”](#a-short-example)
.env.schema
```env-spec
# Stripe secret api key
# @required @sensitive @type=string(startsWith="sk_")
# @docsUrl=https://docs.stripe.com/keys
STRIPE_SECRET_KEY=encrypted("asdfqwerqwe2374298374lksdjflksdjf981273948okjdfksdl")
```
### Why is this useful?
[Section titled “Why is this useful?”](#why-is-this-useful)
Loading a schema file full of structured metadata gives us:
* additional validation, coercion, type-safety for your env vars
* extra guard-rails around handling of `@sensitive` data
* more flexible loading logic without hand-rolled application code or config files
* a place to store default values, clearly differentiated from placeholders
This schema information is most valuable when it is **shared across team members** and machines. So in most cases, this means creating a git-committed `.env.schema` file, instead of the familiar `.env.example` file used by many projects. The difference is that now the schema can be used on an ongoing basis, instead of just once to create an untracked local copy.
Building on this, you could use additional files which set values. They could add additional items or override properties of existing ones. Whether you want to use a single git-ignored `.env` file, or apply a cascade of environment-specific files (e.g., `.env`, `.env.local`, `.env.test`, etc) is up to you. However the new ability to use function calls to safely decrypt data, or load values from external sources, means you’ll likely be tempted to use git-committed `.env` files much more.
An env-spec enabled tool would load all env files appropriately, merging together both schema and values, as well as additional values read from the shell/process. Then the schema would be applied which could transform and fill values, for example decrypting or fetching from an external source, as well as applying coercion and validation.
Backwards compatibility
This is designed to be mostly backwards compatible with traditional .env files. However, as there is no standard .env spec and various tools have different rules and features, we made some decisions to try to standardize things. Our tools may support additional compatibility flags if users want to opt in/out of specific behaviours that match other legacy tools.
The extended feature set means an env-spec enabled parser will successfully parse env files that other tools may not.
### What is included in env-spec?
[Section titled “What is included in env-spec?”](#what-is-included-in-env-spec)
This package defines a parser and related tools for parsing an @env-spec enabled .env file. It does not provide anything past this parsing step, such as actually loading environment variables.
### Why did we create this?
[Section titled “Why did we create this?”](#why-did-we-create-this)
We previously created DMNO and saw immense value in this schema-driven approach to configuration. With env-spec, we wanted to provide a standard that could benefit anyone who uses .env files (and even those who don’t!). There’s an incredible ecosystem of libraries and tools that have adopted .env, and we want to make it easier for everyone to benefit from additional guardrails, with as little upfront work as possible.
We’ve also seen the explosion of AI-assisted coding tools which means that users are even more likely to leak sensitive configuration items, like API keys. If we can help to improve the security posture for these users, then hopefully that improves things for everyone. How can I help? If you’re a maintainer, author, contributor, or an opinionated user of tools that rely on .env files, please read through our RFC. We are not trying to build in a vacuum and we want your input. We’d also love your feedback on varlock which is built on top of @env-spec since it provides (we hope!) a solid reference implementation.
*If this resonates with you, please reach out. We welcome your feedback and we welcome additional contributors.*
***
# @env-spec Reference
> Reference docs and details for @env-spec
We don’t make any assumptions about the meaning of specific decorators, or function calls. Comments
Comments in env-spec (like dotenv) start with a `#`. Comments can be either on their own line, or at the end of a line after something else.
* `# this is a comment`
* `KEY=val # so is this`
* `# but this is invalid`
We give these comments additional meaning by letting them contain `@decorators` and attaching them to specific config items.
`Comment` A regular comment is a comment that does not start with `@`
* Leading whitespace is optional — `#these`, `# are`, `# all valid`
* Decorators within are ignored — `# this @decorator is ignored`
`DecoratorComment` A decorator comment is a comment that starts with an `@` and contains decorators
* It may contain one or multiple decorators — `# @type=integer`, `# @sensitive @required`
* There may be an additional regular comment after the decorator(s) — `# @sensitive=false # key is published in final build` `Divider` A divider is a comment that serves as a separator, like a horizontal line
* A comment starting with `---` or `===` is considered a divider — `# ---`, `# ===`
* A *single* leading whitespace is optional — `# ---`, `#---`
* Anything after that is ignored and valid — `# --- some info`, `# ------------` `CommentBlock` A comment block is a group of continuous comments that is not attached to a specific config item.
* The comment block is ended by an empty line, a `Divider`, or the end of the file.
* Both `DecoratorComment`s and `RegularComment`s may be interpersed
`DocumentHeader` If a `CommentBlock` ends with a `Divider` and is the first element of the document, it will be considered the Header.
* Decorators from this header can be used to configure all contained elements, or the loading process itself
### Decorators
[Section titled “Decorators”](#decorators)
A `Decorator` is used within comments to attach structured data to specific config items or to the entire document and loading process.
* Each decorator has a name and optional value — `@name=value`
* Using the name only is equivalent to setting the value to true — `@required` === `@required=true`
* Decorator values will be parsed using the common value-handling rules (see below)
**Valid decorator examples:**
.env.schema
```env-spec
# @willBeTrue @willBeFalse=false @explicitTrue=true @undef=undefined @trueString="true"
# @int=123 @float=123.456 @willBeString=123.456.789
# @doubleQuoted="with spaces" @singleQuote='hi' @backTickQuote=`hi`
# @unquoted=this-works-too @withNewline="new\nline"
# @funcCallNoArgs=func() @dec=funcCallArray(val1, "val2") @dec=funcCallObj(k1=v1, k2="v2")
```
**Invalid decorator examples:**
.env.schema
```env-spec
# @
# @int=
# @noNewLines="new
# laksdjf"
```
### Config Items
[Section titled “Config Items”](#config-items)
Config items define individual env vars. Each has a key, an optional value, and optional attached comments.
* Keys must start with \[a-ZA-Z\_], followed by any of \[a-ZA-Z0-9\_] — ✅ `SOME_ITEM`, ❌ `BAD-KEY`, ❌ `2BAD_KEY`
* Setting no value is allowed and will be treated as `undefined` — `UNDEF_VAR=`
* An explicit empty string is allowed — `EMPTY_STRING_VAR=""`
* Single-line values may be wrapped in quotes or not, and will follow the common value-handling rules (see below)
* Multi-line string values may be wrapped in either `( ' | " | """ | ``` )` - but we **strongly** recommend using triple backticks only for consistency
* Only comments *directly* preceeding the item will be attached to the item
* However a `Divider` will break the above comments into a `CommentBlock` that is not attached to the item
* An additional post-comment can appear after an item `ITEM1=foo # post comment`
* This post-comment may contain decorators too `ITEM1=foo # @required` (not recommended)
### Common Value-Handling Rules
[Section titled “Common Value-Handling Rules”](#common-value-handling-rules)
Values are interpreted similarly for config item values, decorator values, and values within function call arguments. Values may be wrapped in quotes or not, but handling varies slightly.
* Values may never contain actual newlines, but may contain the string `\n`
* Values do not have to be wrapped in quotes if they do not contain spaces
* Unquoted values also may not contain other characters depending on the context:
* `ConfigItem` values may not contain `[ #]`
* `Decorator` values may not contain `[ #]`
* `FunctionCall` args may not contain `[ ,)]`
* Unquoted values will coerce `true`, `false`, `undefined` — `@foo=false`
* Unquoted values will coerce numeric values — `@int=123 @float=123.456`
* Otherwise unquoted values will be treated as a string
* A value in quotes is *always* be treated as a string — `@d1="with spaces" @trueString="true"`, `@numStr="123"`
* All quote styles ``[`'"]`` are ok — ``@dq="c" @bt=`b` @sq='a'``
* Escaped quotes matching the wrapping quote style are ok — `@ok="escaped\"quote"`
* In quote-wrapped values, the string `\n` will be converted to an actual newline
### Function calls
[Section titled “Function calls”](#function-calls)
If a value is not wrapped in quotes and looks like a function call - for example `encrypted(ASDF123...)` - we will interpret it as a `FunctionCall`. This is relevant both for config item values and decorator values.
* function names must start with a letter, and can then contain letters, numbers, and underscores `/[a-ZA-Z][a-ZA-Z0-9_]*/`
* function args are always interpreted as either an array or object
* you can pass no args, a single value, or multiple
* or you may pass key value pairs, and the args will be interpreted as an object
* each value will be interpreted using common value-handling rules (see above)
**Examples:**
.env.schema functions
```env-spec
# @noArgs=fn()
# @oneArg=fn(asdf) @oneArgQuoted=fn("with quotes")
# @multipleArgs=fn(one, "two", three, 123.456)
# @objArgs=fn(key1=v1, key2="v2", key3=true)
```
***
# @env-spec VS Code extension
> Syntax highlighting and tooling for @env-spec enabled .env files
The @env-spec VS Code extension provides language support for @env-spec enabled .env files.
## Installation
[Section titled “Installation”](#installation)
The extension is available on the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=varlock.env-spec-language).
## IntelliSense
[Section titled “IntelliSense”](#intellisense)
You get hover information for known @decorators. For example:

# Installation
> How to install and set up varlock in your project
There are two ways to install `varlock`:
1. Install as a dependency in JavaScript/TypeScript projects
2. Install as a standalone binary
## As a JavaScript/TypeScript dependency
[Section titled “As a JavaScript/TypeScript dependency”](#as-a-javascripttypescript-dependency)
Requires:
* Node.js version 22 or higher
### Installation
[Section titled “Installation”](#installation)
To install `varlock` in your project, run:
* npm
```bash
npx varlock init
```
* yarn
```bash
yarn dlx varlock init
```
* pnpm
```bash
pnpm dlx varlock init
```
This will install `varlock` as a dependency and scan your project for `.env` files and create a `.env.schema` file in the root of your project. Depending on your project configuration, it will optionally:
* Remove your existing `.env.example` file
* Add decorators to your `.env.schema` file to specify the type of each environment variable
* Encrypt secrets in your `.env` file
## As a binary
[Section titled “As a binary”](#as-a-binary)
To install `varlock` CLI as a binary, run:
```bash
# Install via homebrew
brew install dmno-dev/tap/varlock
# OR Install via cURL
curl -sSfL https://varlock.dev/install.sh | sh -s
```
After installation, you can run `varlock --help` to see the available commands or read the [CLI Reference](/reference/cli-commands/).
# AI Tools
> Using varlock with AI tools - preventing secrets from being leaked to your AI agents
AI tools like [Cursor](https://www.cursor.com/), [Gemini](https://gemini.google.com), [Claude](https://claude.ai/), and [GitHub Copilot](https://github.com/features/copilot) have accelerated workflows for many development teams. This, however, comes with some risks. Unless your team has gone to great lengths to hide secrets from your AI tools, you’re probably sending secrets to their AI agents and potentially leaking them in the code it generates.
`varlock` exists to solve both of those problems. By never storing the secret *values*, you never have to worry about sending them to AI servers. And because of the new [@env-spec](/env-spec/overview) format you will have better AX (agent experience) when dealing with environment variables.
## Allowing schema files for AI tools
[Section titled “Allowing schema files for AI tools”](#allowing-schema-files-for-ai-tools)
Most AI tools ignore `.env.*` files by default. To ensure your AI tool can access your environment schema, add the following to your `.gitignore`:
```txt
!.env.schema
```
If you use a tool with its own ignore file, check that tool’s documentation to see how it handles ignore files and make sure `.env.schema` is allowed.
## Custom instructions and rules
[Section titled “Custom instructions and rules”](#custom-instructions-and-rules)
To give your AI tool full context about `varlock`, you can provide it with the [full Varlock `llms.txt`](https://varlock.dev/llms-full.txt). In Cursor, this is accomplished via ‘Add New Custom Docs’.
If your tool supports custom rules, you can use our own varlock [Cursor rule file from this repo](https://github.com/dmno-dev/varlock/blob/main/.cursor/rules/varlock.mdc) as a starting point to create your own that is most suited to your workflow.
# Environments
> Best practices for managing multiple environments with varlock
One of the main benefits of using environment variables is the ability to boot your application with configuration intended for different environments (e.g., development, preview, staging, production, test).
You can use functions and environment-specific `.env` files (e.g., `.env.production`) to alter configuration accordingly in a declarative way. Plus the additional guardrails provided by `varlock` also make this much safer no matter how values are set.
While many have traditionally shied away from using environment-specific `.env` files due to fear of committing sensitive values, the new ability to set values using [functions](/reference/functions) makes it much easier to manage these values in a secure way.
### Process overrides
[Section titled “Process overrides”](#process-overrides)
`varlock` will always treat environment variables passed into the process with the most precedence. This means you can rely on your external hosting platform to inject environment-specific values and still benefit from `varlock`’s validation and coercion logic.
However we recommend using injected overrides sparingly, and instead moving more config into your `.env` files.
### Loading environment-specific `.env` files
[Section titled “Loading environment-specific .env files”](#loading-environment-specific-env-files)
`varlock` automatically detects all `.env.*` files in the current directory. However any environment-specific files (e.g., `.env.development`) will only be loaded if they match the value of the env var set by the [`@envFlag`](/reference/root-decorators/#envflag) root decorator.
The files are applied with a specific precedence (increasing):
* `.env.schema` - your schema file, which can also contain default values
* `.env` - default values only (not recommended)
* `.env.[envFlag]` - environment-specific values
* `.env.local` - local overrides (gitignored)
* `.env.[envFlag].local` - environment-specific local overrides (gitignored)
For example, consider the following `.env.schema`:
.env.schema
```env-spec
# @envFlag=APP_ENV
# ---
# @type=enum(development, test, staging, production)
APP_ENV=development
```
Your environment flag key is set to `APP_ENV`, which has a default value of `development`. By default, `.env.development` and `.env.development.local` will be loaded if they exist.
To tell `varlock` to load `.env.staging` instead, you must set `APP_ENV` to `staging` - usually using an override passed into the process. For example:
```bash
APP_ENV=staging varlock run -- node my-test-script.js
```
## Advanced logic using functions
[Section titled “Advanced logic using functions”](#advanced-logic-using-functions)
On some platforms, you may not have full control over a build or boot command or the env vars passed into them. In this case, we can use functions to transform other env vars provided by the platform into the environment flag value we want. We can use [`remap()`](/reference/functions#remap) to transform a value according to a lookup, along with [`regex()`](/reference/functions#regex) if we need to match a pattern instead of an exact value.
For example, on the Cloudflare Workers CI platform, we get the current branch name injected as `WORKERS_CI_BRANCH`, which we can use to determine which environment to load:
.env.schema
```env-spec
# @envFlag=APP_ENV
# ---
# set to current branch name when build is running on Cloudflare CI, empty otherwise
WORKERS_CI_BRANCH=
# @type=enum(development, preview, production, test)
APP_ENV=remap($WORKERS_CI_BRANCH, production="main", preview=regex(.*), development=undefined)
```
You’ll notice that `test` is one of the possible enum values, but it is not listed in the remap. When running tests, you would just explicitly set `APP_ENV` when invoking your command.
```bash
APP_ENV=test varlock run -- your-test-command
# or if your command is loading varlock internally
APP_ENV=test your-test-command
```
or you could run a production style build locally `APP_ENV=production varlock run -- your-build-command`
## Setting a *default* environment flag
[Section titled “Setting a default environment flag”](#setting-a-default-environment-flag)
You can set the default environment flag directly when running CLI commands using the `--env` flag:
```bash
varlock load --env production
```
This is only useful if you do not want to create a new env var for your env flag, and you are only using varlock via CLI commands. Mostly it is used internally by some integrations to match existing default behavior, and should not be used otherwise.
Caution
If `@envFlag` is used, this will be ignored!
# Example Guide
> A guide in my new Starlight docs site.
Guides lead a user through a specific task they want to accomplish, often with a sequence of steps. Writing a good guide requires thinking about what your users are trying to do.
## Further reading
[Section titled “Further reading”](#further-reading)
* Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework
# Migrate from dotenv
> How to migrate from dotenv (CLI and npm package) to varlock
## Why migrate from dotenv?
[Section titled “Why migrate from dotenv?”](#why-migrate-from-dotenv)
* **Validation**: Catch misconfigurations early with schema-driven validation.
* **Security**: Redact secrets and prevent accidental leaks.
* **Type-safety**: Generate types automatically for your config.
* **External secrets**: Load secrets from providers like 1Password, AWS, and more.
***
## Migrating from dotenvx CLI
[Section titled “Migrating from dotenvx CLI”](#migrating-from-dotenvx-cli)
If you use `dotenvx` via the CLI, you can switch to `varlock run`:
```bash
# Before (dotenv CLI)
dotenvx run -- node app.js
# env specific
dotenvx run -f .env.staging -- node app.js
# install varlock
brew install dmno-dev/tap/varlock
# After (varlock CLI)
varlock run -- node app.js
# To specify an environment, set your env flag (see your .env.schema)
APP_ENV=staging varlock run -- node app.js
```
> You can use multiple `.env` files (see [Environments guide](/guides/environments)).
***
## Migrating from dotenv npm package
[Section titled “Migrating from dotenv npm package”](#migrating-from-dotenv-npm-package)
Initialize your project with `varlock init` to install `varlock` and generate a `.env.schema` from any existing `.env` files.
* npm
```bash
npx varlock init
```
* yarn
```bash
yarn dlx varlock init
```
* pnpm
```bash
pnpm dlx varlock init
```
Then to use `varlock` in your code, you can replace `dotenv/config` with `varlock/auto-load`:
index.js
```diff
// Before (dotenv)
import 'dotenv/config';
import 'varlock/auto-load';
```
Finally, you can remove `dotenv` from your dependencies:
* npm
```bash
npm uninstall dotenv
```
* yarn
```bash
yarn remove dotenv
```
* pnpm
```bash
pnpm remove dotenv
```
## Using overrides
[Section titled “Using overrides”](#using-overrides)
If `dotenv` is being used under the hood of one of your dependencies, you can use `overrides` to seamlessly swap in `varlock` instead.
* npm
See [NPM overrides docs](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#overrides)
package.json
```diff
{
+"overrides": {
+"dotenv": "varlock"
+ }
}
```
* yarn
See [yarn resolutions docs](https://yarnpkg.com/configuration/manifest#resolutions)
package.json
```diff
{
+"resolutions": {
+"**/dotenv": "npm:varlock"
+ },
}
```
**In a monorepo, this override must be done in the monorepo’s root package.json file!**
* pnpm
* pnpm version 10+
See [pnpm v10 overrides docs](https://pnpm.io/settings#overrides)
pnpm-workspace.yaml
```diff
+overrides:
+"dotenv": "npm:varlock"
```
**This must be set in `pnpm-workspace.yaml`, which lives at the root of your repo, regardless of whether you are using a monorepo or not.**
* pnpm version 9
### pnpm version 9
[Section titled “pnpm version 9”](#pnpm-version-9)
See [pnpm v9 overrides docs](https://pnpm.io/9.x/package_json#pnpmoverrides)
package.json
```diff
{
+"pnpm": {
+"overrides": {
+"dotenv": "npm:varlock"
+ }
+ }
}
```
**In a monorepo, this override must be done in the monorepo’s root package.json file!**
* pnpm version 10+
See [pnpm v10 overrides docs](https://pnpm.io/settings#overrides)
pnpm-workspace.yaml
```diff
+overrides:
+"dotenv": "npm:varlock"
```
**This must be set in `pnpm-workspace.yaml`, which lives at the root of your repo, regardless of whether you are using a monorepo or not.**
* pnpm version 9
### pnpm version 9
[Section titled “pnpm version 9”](#pnpm-version-9)
See [pnpm v9 overrides docs](https://pnpm.io/9.x/package_json#pnpmoverrides)
package.json
```diff
{
+"pnpm": {
+"overrides": {
+"dotenv": "npm:varlock"
+ }
+ }
}
```
**In a monorepo, this override must be done in the monorepo’s root package.json file!**
***
## Further reading
[Section titled “Further reading”](#further-reading)
* [Environments guide](/guides/environments)
* [Schema guide](/guides/schema)
* [Reference: CLI commands](/reference/cli-commands)
* [Reference: Item decorators](/reference/item-decorators)
# Schema
> Using the schema to manage your environment variables
One of the core features of varlock is its schema-driven approach to environment variables - which is best when shared with your team and committed to version control. We recommend creating a new `.env.schema` file to hold schema info set by [config item decorators](/reference/item-decorators), non-sensitive default values, and [root decorators](/reference/root-decorators) to specify global settings that affect `varlock` itself.
This schema should include all of the environment variables that your application depends on, along with comments and documentation about them, and decorators which affect coercion, validation, and generated types / documentation.
The more complete your schema is, the more validation and coercion `varlock` can perform, and the more it can help you catch errors earlier in your development cycle.
> Running [`varlock init`](/reference/cli-commands#init) will attempt to convert an existing `.env.example` file into a `.env.schema` file. It must be reviewed, but it should be a good starting point.
## Root Decorators
[Section titled “Root Decorators”](#root-decorators)
The *header* section of a `.env` file is a comment block at the beginning of the file that ends with a divider. Within this header, you can use [root decorators](/reference/root-decorators) to specify global settings and default behavior for all config items.
.env.schema
```env-spec
# This is the header, and may contain root decorators
# @envFlag=APP_ENV
# @defaultSensitive=false @defaultRequired=false
# @generateTypes(lang=ts, path=env.d.ts)
# ---
# This is a config item comment block and may contain decorators which affect only the item
# @required @type=enum(dev, test, staging, prod)
APP_ENV=dev
```
More details:
* [Root decorators reference](/reference/root-decorators)
## Config Items
[Section titled “Config Items”](#config-items)
Config items are the environment variables that your application depends on. Like normal `.env` syntax, each item is a key-value pair of the form `KEY=value`. The key is the name of the environment variable, and a value may be specified or not.
While simply enumerating all of them in your `.env.schema` is useful (like a `.env.example` file), [@env-spec](/env-spec/) allows us to attach additional comments and [item decorators](/reference/item-decorators), making our schema much more powerful.
### Item Values
[Section titled “Item Values”](#item-values)
Values may be static, or set using [functions](/reference/functions), which can facilitate loading values from external sources without exposing any sensitive values.
**Quote rules:**
* Static values can be wrapped in quotes or not — all quotes styles (`` ` ``, `"`, `'`) are supported
* Values wrapped in single quotes do not support [expansion](#expansion)
* Single line values may not contain newlines, but `\n` will be converted to an actual newline except in single quotes
* Multiline values can be wrapped in ` ``` `, `"""`. Also supported is `"` and `'` but not recommended.
* Unquoted values will be parsed as a number/boolean/undefined where possible (`ITEM=foo` -> `"foo"`, while `ITEM=true` -> `true`), however data-types may further coerce values
.env.schema
```env-spec
NO_VALUE=
STATIC_VALUE_UNQUOTED=quotes are optional # but are recommended!
STATIC_VALUE_QUOTED="#hashtag" # and are necessary in some cases
FUNCTION_VALUE=exec(`op read "op://api-config/item/credential"`)
MULTILINE_VALUE="""
multiple
lines
"""
```
### Item comments
[Section titled “Item comments”](#item-comments)
Comments are used to attach additional documentation and metadata to config items using [item decorators](/reference/item-decorators). This additional metadata is used by varlock to perform validation, coercion, and generate types / documentation.
Multiple comment lines *directly* preceeding an item will be attached to that item. A blank line or a divider (`# ---`) break a comment block, and detach it from the following config item. Comment lines can either contain regular comments or [item decorators](/reference/item-decorators). Note that if a line does not start with a decorator, it will be treated as a regular comment.
```env-spec
# description of item can be multiple lines
# this @decorator will be ignored because the line does not start with @
# @sensitive=false @required # decorator lines can end with a comment
# @type=string(startsWith=pk-) # multiple lines of decorators are allowed
SERVICE_X_PUBLISHABLE_KEY=pk-abc123
```
More details:
* [Item decorators reference](/reference/item-decorators)
* [@type data types reference](/reference/data-types)
* [Functions reference](/reference/functions)
### Value Expansion
[Section titled “Value Expansion”](#expansion)
`varlock` supports *expansion* like other .env tools - such as [dotenv-expand](https://github.com/motdotla/dotenv-expand). However unlike other tools, it uses [function calls](/reference/functions) to implement this.
* `prefix-${OTHER}` -> `concat("prefix", ref("OTHER"))`
* `${OTHER}` -> `ref("OTHER")`
* `${OTHER:-defaultval}` -> `fallback(ref("OTHER"), "defaultval")`
* `$(whoami)` -> `exec("whoami")`
While you could call those functions directly, in many cases it will be more clear to use expansion, or a mix of function calls and expansion. For example:
```env-spec
OP_VAULT_NAME=api-config-prod
MY_KEY=exec(`op read "op://${OP_VAULT_NAME}/service/api-key"`)
```
More details:
* [Functions reference](/reference/functions)
# Secrets Management
> Best practices for managing secrets and sensitive environment variables with varlock
## Handling Secrets
[Section titled “Handling Secrets”](#handling-secrets)
`varlock` uses the term *sensitive* to describe any value that should not be committed to version control. This includes secrets, passwords, and other generally sensitive information.
Coming soon
We’ll be adding support for our own trustless, cloud-based secret storage in the very near future.
## Using 3rd party tools
[Section titled “Using 3rd party tools”](#using-3rd-party-tools)
`varlock` is compatible with any 3rd party tool that supports fetching secrets via a CLI. With `exec`function syntax, you can use any 3rd party tool to fetch secrets.
Here’s an example using 1Password:
```bash
# A secret in 1Password
# @sensitive @required
MY_SECRET=exec(`op read "op://devTest/myVault/credential"`);
```
This way no secrets are ever left in plaintext on your system, even if they are gitignored.
# Telemetry
> Learn about varlock's anonymous usage analytics and how to opt out
Varlock collects anonymous usage statistics to help us understand how the tool is being used and improve the product. This data is completely anonymous and is only used to make varlock better.
## What We Collect
[Section titled “What We Collect”](#what-we-collect)
We collect anonymous information about:
* Which commands are being used
* Basic command execution statistics
* No personal or sensitive data is ever collected
## How to Opt Out
[Section titled “How to Opt Out”](#how-to-opt-out)
You can opt out of analytics in two ways:
### Using the CLI
[Section titled “Using the CLI”](#using-the-cli)
Run the following command to permanently opt out:
```bash
varlock opt-out
```
This will create a configuration file at `~/.varlock/config.json` with the opt-out setting.
### Using an Environment Variable
[Section titled “Using an Environment Variable”](#using-an-environment-variable)
You can also opt out temporarily by setting the `PH_OPT_OUT` environment variable:
```bash
export PH_OPT_OUT=true
```
This will disable analytics for the current session only, which is useful in CI/CD pipelines.
## Privacy
[Section titled “Privacy”](#privacy)
* All analytics data is completely anonymous
* No personal or sensitive information is collected
* Data is only used to improve the product
* You can opt out at any time
* Analytics are handled by [PostHog](https://posthog.com/), a privacy-friendly analytics platform
## Data Usage
[Section titled “Data Usage”](#data-usage)
The anonymous usage data helps us:
* Understand which features are most used
* Identify areas for improvement
* Make informed decisions about future development
* Prioritize bug fixes and new features
If you have any questions about our analytics or privacy practices, please [start a discussion](https://github.com/dmno-dev/varlock/discussions) on GitHub.
# esbuild / tsup
> How to integrate Varlock with simple JS build tools like esbuild and tsup
Here is a simple example of an integration with [tsup](https://tsup.egoist.dev/).
tsup.config.ts
```ts
import { defineConfig } from 'tsup';
import 'varlock/auto-load';
import { getBuildTimeReplacements } from 'varlock';
export default defineConfig({
// ...
esbuildOptions(options) {
options.define ||= {};
Object.assign(options.define, getBuildTimeReplacements());
},
});
```
# JavaScript / Node.js
> How to integrate Varlock with JavaScript and Node.js for secure, type-safe environment management
There are a few different ways to integrate Varlock into a JavaScript / Node.js application.
Some tools/frameworks may require an additional package, or have more specific instructions. The following integrations/guides are available, with more to come soon:
* [Next.js](/integrations/nextjs)
**Want to help us build more integrations? Join our [Discord](https://chat.dmno.dev)!**
## Node.js - `varlock/auto-load`
[Section titled “Node.js - varlock/auto-load”](#nodejs---varlockauto-load)
The best way to integrate varlock into a Node.js application (⚠️ version 22 or higher) is to import the `varlock/auto-load` module. This uses `execSync` to call out to the varlock CLI, sets resolved env vars into `process.env`, and initializes varlock’s runtime code, including:
* varlock’s `ENV` object
* log redaction (if enabled)
* leak detection (if enabled)
example-index.js
```js
import 'varlock/auto-load';
import { ENV } from 'varlock/env';
const FROM_VARLOCK_ENV = ENV.MY_CONFIG_ITEM; // ✨ recommended
const FROM_PROCESS_ENV = process.env.MY_CONFIG_ITEM; // 🆗 still works
```
dotenv drop-in replacement
If you are using [`dotenv`](https://www.npmjs.com/package/dotenv), or a package you are using is using it under the hood - you can seamlessly swap in varlock using your package manager’s override feature. See the [migrate from dotenv](/guides/migrate-from-dotenv) guide for more information.
## Boot via `varlock run`
[Section titled “Boot via varlock run”](#boot-via-varlock-run)
A less invasive way to use varlock with your application is to run your application via [`varlock run`](/reference/cli-commands/#varlock-run).
```bash
varlock run --
```
This will load and validate your environment variables, then run the command you provided with those environment variables injected into the process. This will not inject any runtime code, and varlock’s `ENV` object will not be available.
If you have installed varlock as a project dependency instead of globally, you should run this via your package manager:
* npm
```bash
npm exec -- varlock run --
```
* yarn
```bash
yarn exec -- varlock run --
```
* pnpm
```bash
pnpm exec varlock run --
```
In `package.json` scripts, calling `varlock` directly will work, as your package manager handles path issues:
package.json
```json
"scripts": {
"start": "varlock run -- node index.js"
}
```
Even when using a deeper integration for your code, you may still need to use `varlock run` when calling external scripts/tools, like database migrations, to pass along resolved env vars.
## Front-end frameworks
[Section titled “Front-end frameworks”](#front-end-frameworks)
While environment variables are not available in the browser, many frameworks expose some env vars that are available *at build time* to the client by embedding them into your bundled code. This is best accomplished using tool-specific integrations, especially for frameworks that are handling both client and server-side code.
Isomorphic env vars
The `varlock/env` module is designed to be imported on both the client and server, so frameworks that run code in both places (like Next.js) can import it.
Help us build more integrations!
If you are using a tool/framework that is not listed here, and you’d like to see support for it, or collaborate on building it, we’d love to hear from you. Please hop into our [Discord](https://chat.dmno.dev)!
# Next.js
> How to integrate Varlock with Next.js for secure, type-safe environment management
[](https://www.npmjs.com/package/@varlock/nextjs-integration)
Varlock provides a huge upgrade over the [default Next.js environment variable tooling](https://nextjs.org/docs/pages/guides/environment-variables) - adding validation, type safety, flexible multi-environment management, log redaction, leak detection, and more.
To integrate varlock into a Next.js application, you must use our [`@varlock/nextjs-integration`](https://www.npmjs.com/package/@varlock/nextjs-integration) package. This package provides a drop-in replacement for [`@next/env`](https://www.npmjs.com/package/@next/env), the internal package that handles .env loading, plus a small config plugin which extends the Next.js webpack config to inject our additional security features.
Turbopack not yet supported
Turbopack does not yet provide any plugin system, so using the config plugin is not supported. You can use the `@next/env` override only, but you will not get the additional security features.
## Setup
[Section titled “Setup”](#setup)
1. **Install varlock and the Next.js integration package**
* npm
```bash
npm install @varlock/nextjs-integration varlock
```
* yarn
```bash
yarn add @varlock/nextjs-integration varlock
```
* pnpm
```bash
pnpm add @varlock/nextjs-integration varlock
```
2. **Run `varlock init` to set up your `.env.schema` file**
This will guide you through setting up your `.env.schema` file, based on your existing `.env` file(s). Make sure to review it carefully.
* npm
```bash
npm exec -- varlock init
```
* yarn
```bash
yarn exec -- varlock init
```
* pnpm
```bash
pnpm exec varlock init
```
3. **Override `@next/env` with our drop-in replacement**
* npm
See [NPM overrides docs](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#overrides)
package.json
```diff
{
+"overrides": {
+"@next/env": "npm:@varlock/nextjs-integration"
+ }
}
```
* yarn
See [yarn resolutions docs](https://yarnpkg.com/configuration/manifest#resolutions)
package.json
```diff
{
+"resolutions": {
+"**/@next/env": "npm:@varlock/nextjs-integration"
+ },
}
```
**In a monorepo, this override must be done in the monorepo’s root package.json file!**
* pnpm
* pnpm version 10+
See [pnpm v10 overrides docs](https://pnpm.io/settings#overrides)
pnpm-workspace.yaml
```diff
+overrides:
+"@next/env": "npm:@varlock/nextjs-integration"
```
**This must be set in `pnpm-workspace.yaml`, which lives at the root of your repo, regardless of whether you are using a monorepo or not.**
* pnpm version 9
See [pnpm v9 overrides docs](https://pnpm.io/9.x/package_json#pnpmoverrides)
package.json
```diff
{
+"pnpm": {
+"overrides": {
+"@next/env": "npm:@varlock/nextjs-integration"
+ }
+ }
}
```
**In a monorepo, this override must be done in the monorepo’s root package.json file!**
* pnpm version 10+
See [pnpm v10 overrides docs](https://pnpm.io/settings#overrides)
pnpm-workspace.yaml
```diff
+overrides:
+"@next/env": "npm:@varlock/nextjs-integration"
```
**This must be set in `pnpm-workspace.yaml`, which lives at the root of your repo, regardless of whether you are using a monorepo or not.**
* pnpm version 9
See [pnpm v9 overrides docs](https://pnpm.io/9.x/package_json#pnpmoverrides)
package.json
```diff
{
+"pnpm": {
+"overrides": {
+"@next/env": "npm:@varlock/nextjs-integration"
+ }
+ }
}
```
**In a monorepo, this override must be done in the monorepo’s root package.json file!**
4. **Enable the Next.js config plugin**
At this point, varlock will now load your .env files into `process.env`. But to get the full benefits of this integration, you must add `varlockNextConfigPlugin` to your `next.config.ts` (or `.js`) file.
next.config.ts
```diff
import type { NextConfig } from "next";
+import { varlockNextConfigPlugin } from '@varlock/nextjs-integration/plugin';
const nextConfig: NextConfig = {
// your existing config...
};
-export default nextConfig;
+export default varlockNextConfigPlugin()(nextConfig);
```
***
## Accessing Environment Variables
[Section titled “Accessing Environment Variables”](#accessing-environment-variables)
You can continue to use `process.env.SOMEVAR` as usual, but we recommend using Varlock’s imported `ENV` object for better type-safety and improved developer experience:
example.ts
```ts
import { ENV } from 'varlock/env';
console.log(process.env.SOMEVAR); // 🆗 still works
console.log(ENV.SOMEVAR); // ✨ recommended
```
Caution
If you are not using the `varlockNextConfigPlugin`, only `process.env` will work.
### Type-safety and IntelliSense
[Section titled “Type-safety and IntelliSense”](#type-safety-and-intellisense)
To enable type-safety and IntelliSense for your env vars, enable the [`@generateTypes` root decorator](/reference/root-decorators/#generatetypes) in your `.env.schema`. Note that if your schema was created using `varlock init`, it will include this by default.
.env.schema
```diff
+# @generateTypes(lang='ts', path='env.d.ts')
# ---
# your config items...
```
This generates types for both `process.env` and `ENV`.
#### Why use `ENV` instead of `process.env`?
[Section titled “Why use ENV instead of process.env?”](#why-use-env-instead-of-processenv)
* Non-string values (e.g., number, boolean) are properly typed and coerced
* All non-sensitive items are replaced at build time (not just `NEXT_PUBLIC_`)
* Better error messages for invalid or unavailable keys
* Enables future DX improvements and tighter control over what is bundled
***
## Managing Multiple Environments
[Section titled “Managing Multiple Environments”](#managing-multiple-environments)
Varlock can load multiple *environment-specific* `.env` files (e.g., `.env.development`, `.env.preview`, `.env.production`).
By default, the environment flag is determined as follows (matching Next.js):
* `test` if `NODE_ENV` is `test`
* `development` if running `next dev`
* `production` otherwise
Tip
Without a custom env flag, you cannot use non-production env files (like `.env.preview`, `.env.staging`) for non-prod deployments.
Instead, we recommend explicitly setting your own environment flag using the [`@envFlag` root decorator](/reference/root-decorators/#envflag), e.g. `APP_ENV`. See the [environments guide](/guides/environments) for more information.
### Setting the Environment Flag
[Section titled “Setting the Environment Flag”](#setting-the-environment-flag)
When running locally, or on a platform you control, you can set the env flag explicitly as an environment variable. However on some cloud platforms, there is a lot of magic happening, and the ability to set environment variables per branch is limited. In these cases you can use functions to transform env vars injected by the platform, like a current branch name, into the value you need.
#### Local/Custom Scripts
[Section titled “Local/Custom Scripts”](#localcustom-scripts)
You can set the env var explicitly when you run a command, but often you will set it in `package.json` scripts:
package.json
```json
"scripts": {
"build:preview": "APP_ENV=preview next build",
"start:preview": "APP_ENV=preview next start",
"build:prod": "APP_ENV=production next build",
"start:prod": "APP_ENV=production next start",
"test": "APP_ENV=test jest"
}
```
#### Vercel
[Section titled “Vercel”](#vercel)
You can use the injected `VERCEL_ENV` variable to match their concept of environment types:
.env.schema
```env-spec
# @envFlag=APP_ENV
# ---
# @type=enum(development, preview, production)
VERCEL_ENV=
# @type=enum(development, preview, production, test)
APP_ENV=fallback($VERCEL_ENV, development)
```
For more granular environments, use the branch name in `VERCEL_GIT_COMMIT_REF` (see Cloudflare example below).
#### Cloudflare Workers Build
[Section titled “Cloudflare Workers Build”](#cloudflare-workers-build)
Use the branch name in `WORKERS_CI_BRANCH` to determine the environment:
.env.schema
```env-spec
# @envFlag=APP_ENV
# ---
WORKERS_CI_BRANCH=
# @type=enum(development, preview, production, test)
APP_ENV=remap($WORKERS_CI_BRANCH, production="main", preview=regex(.*), development=undefined)
```
***
## Managing Sensitive Config Values
[Section titled “Managing Sensitive Config Values”](#managing-sensitive-config-values)
Next.js uses the `NEXT_PUBLIC_` prefix to determine which env vars are public (bundled for the browser). Varlock lets you control this with the [`@defaultSensitive`](/reference/root-decorators/#defaultsensitive) root decorator.
To keep the prefix behavior:
.env.schema
```diff
+# @defaultSensitive=inferFromPrefix('NEXT_PUBLIC_')
# ---
FOO= # sensitive
NEXT_PUBLIC_FOO= # non-sensitive, due to prefix
```
Or, set a default and explicitly mark items:
.env.schema
```diff
+# @defaultSensitive=true
# ---
SECRET_FOO= # sensitive by default
# @sensitive=false
NON_SECRET_FOO=
```
Bundling behavior
All non-sensitive items are bundled at build time via `ENV`, while `process.env` replacements only include `NEXT_PUBLIC_`-prefixed items.
## Extra setup for standalone
[Section titled “Extra setup for standalone”](#standalone)
**⚠️ This is only needed if you are using `output: standalone`**
The standalone build process will not copy all our `.env` files to the `.next/standalone` directory, so we must copy them manually. Add this to your build command:
package.json
```json
{
"scripts": {
"build": "next build && cp .env.* .next/standalone",
}
}
```
*you may need to adjust if you don’t want to copy certain .local files*
Standalone builds do not copy dependency binaries, and varlock depends on the CLI to load. So wherever you are booting your standalone server, you will also need to [install the varlock binary](/getting-started/installation/) and boot your server via [`varlock run`](/reference/cli-commands/#varlock-run)
```bash
varlock run -- node .next/standalone/server.js
```
***
## Reference
[Section titled “Reference”](#reference)
* [Root decorators reference](/reference/root-decorators)
* [Item decorators reference](/reference/item-decorators)
* [Functions reference](/reference/functions)
* [Next.js environment variable docs](https://nextjs.org/docs/pages/guides/environment-variables)
# Other Languages
> Integrating varlock into other languages
To use varlock with other languages, you’ll likely want to [install the standalone binary](/getting-started/installation/#as-a-binary), rather than using a JS package manager.
To use it with your applciation code, you must use [`varlock run`](/reference/cli-commands/#varlock-run) to load and validate your environment variables, then run the command you provided with those environment variables injected into the process.
```bash
varlock run --
```
We are working on language-specific helper libraries, that will make this integration better, and provide additional security features like we do in JavaScript.
We will also be implementing automatic type-generation based on your schema for various languages.
**Want to help us build these integrations? Join our [Discord](https://chat.dmno.dev).**
# CLI Commands
> Reference documentation for Varlock CLI commands
Varlock provides a command-line interface for managing environment variables and secrets. This reference documents all available CLI commands.
## Installation
[Section titled “Installation”](#installation)
See [installation](/docs/getting-started/installation) for instructions on how to install Varlock.
## Commands
[Section titled “Commands”](#commands)
### `varlock init`
[Section titled “varlock init”](#varlock-init)
* CLI
```bash
varlock init
```
* npm
```bash
npx varlock init
```
* yarn
```bash
yarn dlx varlock init
```
* pnpm
```bash
pnpm dlx varlock init
```
Scans your project for `.env.*` files and scans code for environment variable usage and creates a `.env.schema` file with the appropriate variables.
It will prompt you to mark items as required and sensitive and do its best to infer the type.
Tip
All further examples assume you are using the CLI, but the same commands can be run via your package manager if `varlock` is installed as a dependency. For example, `varlock doctor` can be run as `pnpm exec varlock doctor`.
### `varlock load`
[Section titled “varlock load”](#varlock-load)
Loads environment variables from your `.env.schema` file and any additional `.env.*` files it finds colocated. This command ensures all required variables are validated per the schema.
```bash
varlock load [options]
Options:
--format # Format of output [pretty|json|env]
--show-all # Shows all items, not just the failing ones
--env # Set the default environment flag (e.g., --env production)
```
Examples:
```bash
# Load and validate environment variables
varlock load
# Load and validate for a specific environment
varlock load --env production
# Output validation results in JSON format
varlock load --format json
# Show all environment variables, including valid ones
varlock load --show-all
```
Caution
Setting `envFlag` in your `.env.schema` will override the `--env` flag.
### `varlock run`
[Section titled “varlock run”](#varlock-run)
Executes a command with the environment variables loaded from your schema. This is useful for running your application with the correct environment configuration.
```bash
varlock run [options]
Options:
--watch Watch for changes and reload the environment variables
```
Examples:
```bash
# Run a Node.js application
varlock run node app.js
# Run with environment variable watching enabled
varlock run --watch npm start
# Run a Python script
varlock run python script.py
```
### `varlock opt-out`
[Section titled “varlock opt-out”](#varlock-opt-out)
Opts out of anonymous usage analytics. This command creates a configuration file at `~/.varlock/config.json` that permanently disables analytics collection.
```bash
varlock opt-out
```
Note
You can also temporarily opt out by setting the `PH_OPT_OUT=true` environment variable. See the [Telemetry guide](/guides/telemetry/) for more information about our analytics and privacy practices.
### `varlock help`
[Section titled “varlock help”](#varlock-help)
Displays help information about Varlock commands and options.
```bash
varlock help [command]
Examples:
varlock help # Show general help
varlock help init # Show help for init command
varlock init --help # Alternative way to show command help
```
# @type Data Types
> A reference page of available data types to be used with the `@type` item decorator
The [`@type` item decorator](/reference/item-decorators/#type) sets the data type associated with an item. The data type affects coercion, validation, and [generated type files](/reference/root-decorators/#generatetypes).
### Additional data type options
[Section titled “Additional data type options”](#additional-data-type-options)
All types (except `enum`) can be used without any arguments, but most take optional arguments that further narrow the type’s behavior.
```env-spec
# @type=string
NO_ARGS=
# @type=string(minLength=5, maxLength=10, toUpperCase=true)
WITH_ARGS=
```
### Coercion & Validation Process
[Section titled “Coercion & Validation Process”](#coercion--validation-process)
Once a raw value is resolved - which could from a static value in an `.env` file, a [function](/reference/functions), or an override passed into the process - the raw value will be coerced and validated based on the type, respecting additional arguments provided to the type.
Consider the following example:
```env-spec
# @type=number(precision=0, max=100)
ITEM="123.45"
```
The internal coercion/validation process would look like — `"123.45"` -> `123.45` -> `123` -> ❌ invalid (greater than max)
### Default Behavior
[Section titled “Default Behavior”](#default-behavior)
When no `@type` is specified and the schema contains a static value, a type will be inferred. Otherwise, the item type will default to `string`.
```env-spec
INFERRED_STRING="foo"
INFERRED_NUMBER=123
INFERRED_BOOLEAN=true
DEFAULTS_TO_STRING_FN=someFn()
DEFAULTS_TO_STRING=
```
## Built-in Data types
[Section titled “Built-in Data types”](#built-in-data-types)
These are the built-in data types. In the future, plugins will be able to register additional data types.
### `string`
[Section titled “string”](#string)
**Options:**
* `minLength` (number): Minimum length of the string
* `maxLength` (number): Maximum length of the string
* `isLength` (number): Exact length required
* `startsWith` (string): Required starting substring
* `endsWith` (string): Required ending substring
* `matches` (string|RegExp): Regular expression pattern to match
* `toUpperCase` (boolean): Convert to uppercase
* `toLowerCase` (boolean): Convert to lowercase
* `allowEmpty` (boolean): Allow empty string (default: false)
```env-spec
# @type=string(minLength=5, maxLength=10, toUpperCase=true)
MY_STRING=value
```
### `number`
[Section titled “number”](#number)
**Options:**
* `min` (number): Minimum allowed value (inclusive)
* `max` (number): Maximum allowed value (inclusive)
* `coerceToMinMaxRange` (boolean): Coerce value to be within `min`/`max` range
* `isDivisibleBy` (number): Value must be divisible by this number
* `isInt` (boolean): Value must be an integer (equivalent to `precision=0`)
* `precision` (number): Number of decimal places to keep
```env-spec
# @type=number(min=0, max=100, precision=1)
MY_NUMBER=42.5
```
### `boolean`
[Section titled “boolean”](#boolean)
The following values will be coerced to a boolean and considered valid:
* True values: `"t"`, `"true"`, `true`, `"yes"`, `"on"`, `"1"`, `1`
* False values: `"f"`, `"false"`, `false`, `"no"`, `"off"`, `"0"`, `0`
Anything else will be considered invalid.
```env-spec
# @type=boolean
MY_BOOL=true
```
### `url`
[Section titled “url”](#url)
**Options:**
* `prependHttps` (boolean): Automatically prepend “https\://” if no protocol is specified
```env-spec
# @type=url(prependHttps=true)
MY_URL=example.com/foobar
```
### `enum`
[Section titled “enum”](#enum)
Checks a value is contained in a list of possible values - it must match one exactly.
**NOTE** - this is the only type that cannot be used without any additional arguments
```env-spec
# @type=enum(development, staging, production)
ENV=development
```
### `email`
[Section titled “email”](#email)
**Options:**
* `normalize` (boolean): Convert email to lowercase
```env-spec
# @type=email(normalize=true)
MY_EMAIL=User@Example.com
```
### `port`
[Section titled “port”](#port)
Checks for valid port number. Coerces to a number.
**Options:**
* `min` (number): Minimum port number (default: 0)
* `max` (number): Maximum port number (default: 65535)
```env-spec
# @type=port(min=1024, max=9999)
MY_PORT=3000
```
### `ip`
[Section titled “ip”](#ip)
Checks for a valid [IP address](https://en.wikipedia.org/wiki/IP_address).
**Options:**
* `version` (`4|6`): IPv4 or IPv6
* `normalize` (boolean): Convert to lowercase
```env-spec
# @type=ip(version=4, normalize=true)
MY_IP=192.168.1.1
```
### `semver`
[Section titled “semver”](#semver)
Checks for a valid [semantic version](https://semver.org/).
```env-spec
# @type=semver
MY_VERSION=1.2.3-beta.1
```
### `isoDate`
[Section titled “isoDate”](#isodate)
Checks for valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date strings with optional time and milliseconds.
```env-spec
# @type=isoDate
MY_DATE=2024-03-20T15:30:00Z
```
### `uuid`
[Section titled “uuid”](#uuid)
Checks for valid [UUID](https://en.wikipedia.org/wiki/UUID) (versions 1-5 per RFC4122, including `NIL`).
```env-spec
# @type=uuid
MY_UUID=123e4567-e89b-12d3-a456-426614174000
```
### `md5`
[Section titled “md5”](#md5)
Checks for valid [MD5 hash](https://en.wikipedia.org/wiki/MD5).
```env-spec
# @type=md5
MY_HASH=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
```
### `simple-object`
[Section titled “simple-object”](#simple-object)
Validates and coerces JSON strings into objects.
```env-spec
# @type=simple-object
MY_OBJECT={"key": "value"}
```
# Item Value Functions
> A comprehensive reference of all available function resolvers in varlock
You may set config items values using *functions* rather than simple static values. Functions can be composed together to create complex value resolution logic.
```env-spec
ITEM=fn(arg1, arg2)
COMPOSITION=fn1(fn1Arg1, fn2(fn2Arg1, fn2Arg2))
```
Note that many built-in utility functions have *expansion* equivalents and often it will be more clear to use them that way. For example:
```env-spec
EXPANSION_EQUIVALENT="pre-${OTHER}-post"
USING_FN_CALLS=concat("pre-", ref(OTHER), "-post")
# mixed example
CONFIG=exec(`aws ssm get-parameter --name "/config/${APP_ENV}" --with-decryption`)
```
Currently, there are built-in utility functions, and soon there will be functions to handle values encrypted using varlock provided tools.
In the future, plugins will be able to register additional functions that fetch values from external providers.
### `concat()`
[Section titled “concat()”](#concat)
Concatenates multiple values into a single string.
Expansion uses `concat()` to combine multiple parts of strings when they include multiple parts.
```env-spec
PATH=concat("base/", ref("APP_ENV"), "/config.json")
PATH2=`base/${APP_ENV}/config.json` # equivalent using expansion
```
### `exec()`
[Section titled “exec()”](#exec)
Executes a CLI command and uses its output as the value. This is particularly useful for integrating with external tools and services.
NOTE - many CLI tools output an additional newline. `exec()` will trim this automatically.
Expansion equivalent: `exec(command)` === `$(command)`
```env-spec
# Using 1Password CLI
API_KEY=exec(`op read "op://dev test/service x/api key"`)
# Using AWS CLI
AWS_CREDENTIALS=exec(`aws sts get-session-token --profile prod`)
```
### `ref()`
[Section titled “ref()”](#ref)
References another environment variable - which is very useful when composing multiple functions together.
Expansion equivalent: `ref(OTHER_VARL)` === `${OTHER_VAR}` (and `$OTHER_VAR`)
```env-spec
API_URL=https://api.example.com
USERS_API_URL=concat(ref("API_URL"), "/users")
```
### `fallback()`
[Section titled “fallback()”](#fallback)
Returns the first non-empty value in a list of possible values.
```env-spec
POSSIBLY_EMPTY=
ANOTHER=
EXAMPLE=fallback(ref(POSSIBLY_EMPTY), ref(ANOTHER), "default-val")
```
### `remap()`
[Section titled “remap()”](#remap)
Maps a value to a new value based on a set of remapping rules. This is useful for translating one value, often provided by an external platform, into another.
* The first argument is the value to remap (often a `ref()` to another variable).
* All following arguments are key=value pairs, where the key is the new value and the value is what to match against, which can be a string, `undefined`, or a `regex()` call.
* If no match is found, the original value is returned.
```env-spec
# env var that is set by CI/platform
CI_BRANCH=
# @type=enum(development, preview, production)
APP_ENV=remap($CI_BRANCH, production="main", preview=regex(.*), development=undefined)
```
### `regex()`
[Section titled “regex()”](#regex)
Creates a regular expression for use in other functions, such as `remap()`.
* Takes a single string argument, which is the regex pattern (using JavaScript regex syntax)
* **This cannot be used as a standalone value. It must be used only as a function argument.**
```env-spec
# Example usage within remap
ENV_TYPE=remap($APP_ENV, dev=regex("^dev.*"), prod="production")
```
# Config Item @decorators
> A reference page of available env-spec decorators for items
Item decorators in a comment block *directly* preceeding an env item are attached to that item. Multiple decorators can be specified on the same line. A comment block is broken by either an empty line or a divider.
```env-spec
# @required @sensitive @type=string(startsWith=sk-)
# @docsUrl=https://docs.servicex.com/api-keys
SERVICE_X_API_KEY=
```
### `@required`
[Section titled “@required”](#required)
Sets whether an item is *required* - meaning validation will fail if the value resolves to `undefined` or an empty string.
Default behavior for all items can be toggled using the [`@defaultRequired` root decorator](/reference/root-decorators/#defaultRequired)
```env-spec
# @defaultRequired=false
# ---
# @required
REQUIRED_ITEM=
```
### `@optional`
[Section titled “@optional”](#optional)
Opposite of [`@required`](#required). Equivalent to writing `@required=false`.
```env-spec
# @defaultRequired=true
# ---
# @optional
REQUIRED_ITEM=
```
### `@sensitive`
[Section titled “@sensitive”](#sensitive)
Sets whether the item should be considered *sensitive* - meaning it must be protected from leaking. The value will be always be redacted in CLI output, and client integrations can take further action to prevent leaks.
Default behavior for all items can be set using the [`@defaultSensitive` root decorator](/reference/root-decorators/#defaultSensitive)
```env-spec
# @sensitive
SERVICE_X_PRIVATE_KEY=
# @sensitive=false
SERVICE_X_CLIENT_ID=
```
### `@type`
[Section titled “@type”](#type)
Sets the data type of the item - which affects validation, coercion, and generated types. Note that some data types take additional arguments. See [data types reference](/reference/data-types) for more details.
If not specified, a data type will be inferred if a static value is set, or default to `string` otherwise.
```env-spec
# @type=url
SOME_URL=
# @type=string(startsWith=abc)
EXAMPLE_WITH_TYPE_OPTIONS=
INFER_NUMBER=123 # data type of `number` will be inferred from the value
```
### `@example`
[Section titled “@example”](#example)
Provide an example value for the item. This lets you avoid setting placeholder values that are not meant to be used.
```env-spec
# @example="sk-abc123"
SECRET_KEY=
```
### `@docsUrl`
[Section titled “@docsUrl”](#docsurl)
URL of documentation related to the item.
```env-spec
# @docsUrl=https://platform.openai.com/docs/api-reference/authentication
OPENAI_API_KEY=
```
# Root @decorators
> A reference page of available env-spec decorators that apply to the schema itself, rather than individual items
Root decorators appear in the *header* section of a .env file - which is a comment block at the beginning of the file that ends with a divider. Usually root decorators are used only in your `.env.schema` file.
### `@envFlag`
[Section titled “@envFlag”](#envflag)
Sets the key of your *environment flag*, which will be used when determining if environment-specific .env files will be loaded (e.g. `.env.production`). This will usually be something like `APP_ENV`.
This will override the `--env` CLI flag if it is set.
**⚠️ NOTE: We do not recommend using `NODE_ENV` as your environment flag, as it has other implications, and is often set out of your control.**
See [environments guide](/guides/environments) for more info
```env-spec
# @envFlag=APP_ENV
# ---
# @type=enum(development, staging, production)
APP_ENV=development
```
### `@defaultRequired`
[Section titled “@defaultRequired”](#defaultrequired)
Sets the default behavior of each item being *required*. Can be overridden on individual items using [`@required`](/reference/item-decorators/#required) or [`@optional`](/reference/item-decorators/#optional).
**Options:**
* `true` (default): All items are required unless marked optional.
* `false`: All items are optional unless marked required.
* `infer`: Items with a value set in `.env.schema` will be required; items with an empty string or no value are optional. Can be overridden per item.
```env-spec
# @defaultRequired=infer
# ---
FOO=bar # required (static value)
BAR=fnCall() # required (function value)
BAZ= # optional (no value)
QUX='' # optional (empty string)
# @optional
OPTIONAL_ITEM= # optional (explicit)
# @required
REQUIRED_ITEM= # required (explicit)
```
### `@defaultSensitive`
[Section titled “@defaultSensitive”](#defaultsensitive)
Sets the default state of each item being treated as *sensitive*. Can be overridden on individual items using [`@sensitive`](/reference/item-decorators/#sensitive).
**Options:**
* `true` (default): All items are sensitive unless marked otherwise.
* `false`: All items are not sensitive unless marked otherwise.
* `inferFromPrefix(PREFIX)`: Items whose key starts with the given `PREFIX` are *not* sensitive; all others are sensitive. Useful for marking e.g. `PUBLIC_` keys as non-sensitive by default.
```env-spec
# @defaultSensitive=inferFromPrefix(PUBLIC_)
# ---
PUBLIC_FOO= # not sensitive (due to matching prefix)
OTHER_FOO= # sensitive (default when prefix does not match)
# @sensitive=true
PUBLIC_BAR= # sensitive (explicit decorator overrides prefix)
# @sensitive=false
OTHER_BAR= # not sensitive (explicit)
```
### `@disable`
[Section titled “@disable”](#disable)
Skips loading config items from a file or data source. If true, the file is ignored and no items are loaded from it. Useful for conditionally disabling a schema or env file.
```env-spec
# @disable # (shorthand for @disable=true)
# ---
FOO=bar # will be ignored
BAR=baz # will be ignored
```
### `@generateTypes`
[Section titled “@generateTypes”](#generatetypes)
Enables automatic type generation based on your schema.
**Key-value args:**
* `lang`: Language to generate types for. Supported languages:
* `ts` - TypeScript
* `path`: Relative filepath to output generated type file
```env-spec
# @generateTypes(lang=ts, path=./env.d.ts)
# ---
```