Skip to content

Shell Plugins

The dotsecenv shell plugins automatically load secrets from .secenv files when you cd into a directory. When you leave, secrets are automatically unloaded—keeping your environment clean and secure.

  1. On cd into a directory: The plugin checks for .env and .secenv files
  2. Security check: Files must be owned by you (or root) and not world-writable
  3. Trust prompt: For .secenv files, you’re prompted to allow loading (can be remembered)
  4. Auto-load: Plain variables from .env are loaded, then secrets are fetched from your vault
  5. On cd out: Variables are automatically unset
~/project $ cd myapp/
dotsecenv: found .secenv in /home/user/myapp
Load secrets? [y]es / [n]o / [a]lways: y
dotsecenv: loaded 2 secret(s) from .secenv: DATABASE_URL, API_KEY
~/project/myapp $ cd ..
dotsecenv: unloaded 2 secret(s): DATABASE_URL, API_KEY

Oh-My-Zsh:

Terminal window
git clone https://github.com/dotsecenv/plugin.git \
${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/dotsecenv

Then add dotsecenv to your plugins in ~/.zshrc:

Terminal window
plugins=(... dotsecenv)

Antigen:

Terminal window
antigen bundle dotsecenv/plugin

Manual:

Terminal window
source /path/to/dotsecenv.plugin.zsh

The plugins provide several convenience commands:

CommandDescription
dseShorthand for dotsecenv
secret NAMEShorthand for dotsecenv secret get NAME
copysecret NAMECopy a secret directly to clipboard
reloadsecenvReload secrets in the current directory
pinsecenvPin current secrets to persist across directory changes

Use dse as a shorthand for the dotsecenv command:

Terminal window
# Instead of:
dotsecenv vault describe
# Use:
dse vault describe

Retrieve a secret with minimal typing:

Terminal window
# Instead of:
dotsecenv secret get DATABASE_PASSWORD
# Use:
secret DATABASE_PASSWORD

Copy a secret directly to your clipboard without displaying it:

Terminal window
copysecret API_KEY
# Output: dotsecenv: secret copied to clipboard

Clipboard support:

  • macOS: Uses pbcopy (built-in)
  • Wayland: Uses wl-copy
  • X11: Uses xclip or xsel

Re-trigger secret loading for the current directory without leaving and re-entering:

Terminal window
reloadsecenv

This is useful when:

  • You’ve added new secrets to your vault
  • You’ve modified the .secenv file
  • A secret fetch failed and you want to retry

Pin the current directory’s environment variables so they persist even after you cd away.

Terminal window
pinsecenv
# Output:
# dotsecenv: pinned secrets: DATABASE_URL, API_KEY
# dotsecenv: pinned env vars: NODE_ENV, PORT
# dotsecenv: these variables will persist when you leave this directory
# dotsecenv: you must unset them manually, e.g.: `unset VARIABLE_NAME`

By default, dotsecenv automatically unloads secrets when you leave a directory. This is a security feature—secrets don’t linger in your environment longer than needed.

However, there are scenarios where you want secrets to persist:

Terminal window
cd ~/myapp
# Secrets loaded: DATABASE_URL, API_KEY
# Start a background server that needs these env vars
npm run dev &
# Pin secrets before leaving
pinsecenv
cd ~/other-project
# Normally secrets would be unloaded, but they're pinned!
# Your background server continues working
Terminal window
cd ~/project-a
# Secrets loaded for project-a
pinsecenv # Pin them
cd ~/project-b
# Project-a secrets still available
# Project-b secrets also loaded (if .secenv exists)

If you’re working across multiple projects but need consistent access to certain secrets:

Terminal window
cd ~/main-project
pinsecenv
# Now work anywhere while keeping main-project secrets available
cd ~/scripts
./deploy.sh # Can use pinned DATABASE_URL

The plugin uses a trust system to prevent malicious .secenv files from auto-loading secrets:

When you cd into a directory with an untrusted .secenv:

dotsecenv: found .secenv in /path/to/project
Load secrets? [y]es / [n]o / [a]lways:
OptionBehavior
y / yesLoad secrets for this session only
n / noSkip loading for this session
a / alwaysPermanently trust this directory

Directories trusted with “always” are saved to:

~/.config/dotsecenv/trusted_dirs

To remove trust:

Terminal window
# Edit the file and remove the directory path
vim ~/.config/dotsecenv/trusted_dirs

Before loading any file, the plugin performs security checks:

  1. Ownership: File must be owned by you or root
  2. Permissions: File must not be world-writable

If a file fails these checks:

dotsecenv: refusing to load /path/.secenv - not owned by current user or root
dotsecenv: refusing to load /path/.env - world-writable

The .secenv file defines which secrets to load as environment variables:

Terminal window
# Load secret with matching name
DATABASE_PASSWORD={dotsecenv}
# Load secret with different name
DB_PASS={dotsecenv/DATABASE_PASSWORD}
# Plain values work too (loaded from .secenv, not encrypted)
NODE_ENV=production
# Load from a namespace
API_KEY={dotsecenv/production::API_KEY}

See Your First Secret for more details on .secenv syntax.

When both .env and .secenv exist in a directory:

  1. Phase 1: Load plain variables from .env
  2. Phase 2: Load plain variables from .secenv
  3. Phase 3: Load secrets from .env (if any {dotsecenv} patterns)
  4. Phase 4: Load secrets from .secenv

If a variable is defined in both files, .secenv wins with a warning:

dotsecenv: warning: DATABASE_URL from .secenv overrides value from .env
  1. Check the plugin is loaded:

    Terminal window
    type reloadsecenv # Should show it's a function
  2. Verify .secenv exists and has correct permissions:

    Terminal window
    ls -la .secenv
  3. Try manual reload:

    Terminal window
    reloadsecenv

The trust prompt requires an interactive terminal. This happens when:

  • Running in a script
  • Running in a non-interactive shell

Solution: Pre-trust the directory:

Terminal window
echo "/path/to/project" >> ~/.config/dotsecenv/trusted_dirs

If a secret fails to fetch, you’ll see:

dotsecenv: error fetching secret 'SECRET_NAME' for VAR:
<error details>

Common causes:

  • Secret doesn’t exist in vault
  • GPG agent not running
  • Vault locked