Skip to content

capture

Capture files outside of version control (such as .env and .dev.vars) from a repository and store them in the global envi store.

Usage

bash
envi capture

Description

The capture command traverses your repository to find files that hold local secrets and configuration outside of version control — .env, .env.local, .env.production, Cloudflare Workers' .dev.vars and per-environment variants, plus any extra patterns you declare via capture_patterns — and stores them in ~/.envi/store/.

What It Does

  1. Finds project root - Looks for version control markers (.git, .jj, .hg, .svn), or prompts you to confirm the current directory
  2. Discovers env files - Recursively searches for .env, .env.*, .dev.vars, and .dev.vars.* files
  3. Respects .gitignore - Only uses directories from .gitignore if present, not file patterns
  4. Parses files - Extracts key-value pairs and preserves comments
  5. Stores configuration - Saves to ~/.envi/store/{package-name}.maml
  6. Creates git commit (if GitHub integration is enabled)

File Matching

Envi finds files that match:

  • .env (exact match)
  • .env.* (anything with a dot separator, like .env.local, .env.production)
  • .dev.vars (Cloudflare Workers local secrets)
  • .dev.vars.* (per-environment variants — the suffix matches whatever environment names you've defined in your Wrangler configuration, e.g. wrangler.toml or wrangler.jsonc)

Files are discovered recursively in all subdirectories, except:

  • node_modules/
  • .git/
  • Other directories ignored in your .gitignore

Custom Capture Patterns

Many tools and frameworks store local secrets in their own files (.flaskenv for Flask, custom team-specific dotfiles, etc.). Rather than asking Envi to ship a baked-in list for every one of them, you can extend the matcher per repo by adding capture_patterns to envi.config.maml:

maml
{
  capture_patterns: [
    ".flaskenv",
    "secrets.env"
  ]
}

Behavior:

  • Additive. Built-in patterns (.env, .dev.vars, etc.) are always matched.
  • Auto-expansion for bare filenames. A pattern without a / (like .flaskenv) is matched both at the repo root and in any subdirectory — the same way .env already works.
  • Globs are passed through verbatim. A pattern containing / (like config/*.local) is handed to fast-glob as-is, so you can be as specific as you need.
  • Same parser, same shape. Matched files must be in KEY=value format — that's how Envi parses, stores, and restores them. Free-form config files (TOML, YAML, JSON) won't round-trip cleanly. Shell-style files like direnv's .envrc (which use export FOO=bar) are also not supported — Envi's parser treats export FOO as the key.

If you don't have an envi.config.maml yet, run envi create-key to create one (it'll add an encryption_key too — you can drop that line if you don't want at-rest encryption), or create the file by hand with just the capture_patterns field.

Storage Organization

With package.json

If your repository has a package.json with a name field:

json
{
  "name": "@myorg/myapp"
}

Files are stored at: ~/.envi/store/@myorg/myapp.maml

Scoped packages create subdirectories for better organization.

Without package.json

If no package.json or no name field exists, Envi:

  1. Warns you about potential naming conflicts
  2. Prompts for confirmation (default: Yes)
  3. Uses the folder name: ~/.envi/store/foldername.maml

Change Detection

When you run capture multiple times:

  • No changes: Shows info message, no file write, no commit
  • Changes detected: Updates file and creates commit (if GitHub enabled)

Files are sorted by path before comparison to ensure consistent results.

If encryption-at-rest is enabled, the comparison happens in plaintext space — fresh ciphertexts on every capture do not trigger spurious rewrites. Switching from a plaintext store to an encrypted one (or vice versa) always rewrites, so running envi create-key followed by envi capture reliably converts the existing store.

Encryption at Rest (Optional)

By default, captured env values are written to ~/.envi/store/ in plaintext.

If you generate a key with envi create-key, envi.config.maml is written to your repository root with an encryption_key. From then on, envi capture encrypts each file's env block before writing:

bash
$ envi capture
...
 Encrypting env values with key from envi.config.maml
 Saving to storage...
 Captured environment files for '@myorg/myapp'

The on-disk MAML then has encrypted_env (a base64 ciphertext) instead of env for each file. Anyone with read access to the source repo (and therefore envi.config.maml) can decrypt; anyone without it cannot.

This mostly only matters when you also use the GitHub integration — encryption-at-rest means a leak of the envi-store backup alone won't expose env values without also leaking the source repo. On a single laptop, an attacker with disk access has both. See envi create-key for the full workflow and trade-offs.

Examples

Basic Capture

bash
$ envi capture
 Finding repository root...
 Repository root: /Users/you/projects/myapp
 Package name: @myorg/myapp
 Searching for env files...
 Found 3 file(s):
  - .env.local
  - apps/web/.env.production
  - packages/api/.env
 Parsing files...
 Saving to storage...
 Captured environment files for '@myorg/myapp'

No Changes Detected

bash
$ envi capture
...
 No changes detected - environment files are identical to stored version.
 Stored at: ~/.envi/store/@myorg/myapp.maml

With GitHub Integration

bash
$ envi capture
...
 Captured environment files for '@myorg/myapp'
 Committing to version control...
 Committed and pushed to GitHub

Released under the MIT License.