@varlock/pass-plugin
v0.0.5
Published
Varlock plugin to load secrets from pass (the standard unix password manager)
Readme
@varlock/pass-plugin
This package is a Varlock plugin that enables loading secrets from pass (the standard unix password manager) into your configuration.
Features
- Zero-config - Works with your existing pass store out of the box
- GPG-backed encryption - Leverages pass's native GPG security model
- Auto-infer entry paths from environment variable names
- Bulk loading with
passBulk()via@setValuesBulkto load all entries under a path - Multiple store instances for accessing different pass stores
- Name prefixing with
namePrefixoption for scoped entry access - Custom store paths via
storePathoption (overridesPASSWORD_STORE_DIR) allowMissingoption for graceful handling of optional secrets- In-session caching - Each entry is decrypted only once per resolution
- Helpful error messages with resolution tips
Installation
If you are in a JavaScript based project and have a package.json file, you can either install the plugin explicitly:
npm install @varlock/pass-pluginAnd then register the plugin without any version number:
# @plugin(@varlock/pass-plugin)Otherwise just set the explicit version number when you register it:
# @plugin(@varlock/[email protected])See our Plugin Guide for more details.
Prerequisites
You must have pass installed on your system:
# macOS
brew install pass
# Ubuntu/Debian
sudo apt-get install pass
# Fedora/RHEL
sudo yum install pass
# Arch
pacman -S passYour password store must be initialized (pass init "Your GPG Key ID"). See the pass documentation for setup details.
The plugin does not fail at load time if pass is not installed - it only fails when you actually try to access a secret.
Setup
After registering the plugin, initialize it with the @initPass root decorator.
Basic setup
For most use cases, no configuration is needed:
# @plugin(@varlock/pass-plugin)
# @initPass()This uses the default ~/.password-store directory and your existing GPG configuration.
Custom store path
If your password store is in a non-standard location:
# @plugin(@varlock/pass-plugin)
# @initPass(storePath=/path/to/custom/store)Name prefixing
Use namePrefix to automatically prepend a path prefix to all entry lookups:
# @plugin(@varlock/pass-plugin)
# @initPass(namePrefix=production/app/)
# ---
# Fetches "production/app/DATABASE_PASSWORD"
DATABASE_PASSWORD=pass()Multiple instances
Access multiple password stores:
# @plugin(@varlock/pass-plugin)
# @initPass(id=personal)
# @initPass(id=team, storePath=/shared/team-store)
# ---
MY_TOKEN=pass(personal, "tokens/github")
SHARED_KEY=pass(team, "api-keys/stripe")Loading secrets
Once initialized, use the pass() resolver function to fetch secrets.
Basic usage
# @plugin(@varlock/pass-plugin)
# @initPass()
# ---
# Auto-infer entry path from variable name
DATABASE_PASSWORD=pass()
# Explicit entry path
STRIPE_KEY=pass("services/stripe/live-key")
# Nested entries
DB_CREDS=pass("production/database/credentials")When called without arguments, pass() uses the config item key as the entry path in the pass store.
Handling optional secrets
Use allowMissing when a secret may not exist in the store:
# Won't error if the entry doesn't exist - returns empty string instead
OPTIONAL_KEY=pass("monitoring/datadog-key", allowMissing=true)Multiline entries
By default, pass() returns only the first line of the entry (the password), matching pass's own convention where the password lives on line 1 and metadata follows on subsequent lines. This is the same behavior as pass -c (copy to clipboard).
To retrieve the full multiline content, use multiline=true:
# Only returns the first line (the password)
DB_PASSWORD=pass("production/database")
# Returns all lines (password + metadata)
DB_FULL_ENTRY=pass("production/database", multiline=true)Example entry (pass show production/database):
mysecretpassword
URL: https://db.example.com
Username: admin
Port: 5432pass("production/database")returnsmysecretpasswordpass("production/database", multiline=true)returns the full content
Bulk loading secrets
Use passBulk() with @setValuesBulk to fetch all entries under a directory in your pass store in one go:
# @plugin(@varlock/pass-plugin)
# @initPass()
# @setValuesBulk(passBulk("services"))
# ---
# These will be populated from entries under services/ in the pass store
# e.g., services/STRIPE_KEY, services/DATABASE_URL
STRIPE_KEY=
DATABASE_URL=passBulk() lists all entries under the given path prefix, fetches each one (first line only, matching the pass() default), and returns a JSON map of { "entryPath": "password", ... }.
# Load everything from the store root
# @setValuesBulk(passBulk())
# Load from a specific subdirectory
# @setValuesBulk(passBulk("production/api"))
# With a named instance
# @setValuesBulk(passBulk(team, "shared"))Reference
Root decorators
@initPass()
Initialize a pass plugin instance.
Parameters:
storePath?: string- Custom password store path (overridesPASSWORD_STORE_DIR, defaults to~/.password-store)namePrefix?: string- Prefix automatically prepended to all entry pathsid?: string- Instance identifier for multiple instances (defaults to_default)
Resolver functions
pass()
Fetch a secret from the pass store.
Signatures:
pass()- Auto-infers entry path from variable namepass(entryPath)- Fetch by explicit entry pathpass(instanceId, entryPath)- Fetch from a specific instancepass(entryPath, allowMissing=true)- Fetch with graceful missing handlingpass(entryPath, multiline=true)- Fetch the full multiline content
Returns: The first line (password) of the pass entry by default, or the full content if multiline=true.
passBulk()
Fetch all entries under a directory in the pass store at once. Intended for use with @setValuesBulk.
Lists entries via pass ls, then fetches each one in parallel. Each entry returns the first line only (matching the pass() default).
Signatures:
passBulk()- Load all entries from the store rootpassBulk(pathPrefix)- Load entries under a specific path prefixpassBulk(instanceId, pathPrefix)- Load from a named instance with prefix
Returns: JSON string of { "entryPath": "firstLineValue", ... } pairs.
How it works
Under the hood, the plugin:
- Executes
pass show <path>as a subprocess for each secret - Leverages your existing GPG agent for passphrase caching (you may be prompted for your GPG passphrase on first access)
- Caches decrypted values in memory for the duration of a single resolution session (no secrets are persisted)
- For bulk operations, lists entries with
pass lsand fetches them in parallel
Since the plugin delegates entirely to the pass CLI, it respects all of your existing GPG and pass configuration, including:
- GPG agent passphrase caching
- Multiple GPG key recipients
- Git-backed password stores
- Custom
PASSWORD_STORE_DIRsettings
Troubleshooting
pass command not found
- Install pass using your system package manager (see Prerequisites)
- Ensure
passis in yourPATH
Entry not found
- Verify the entry exists:
pass show <path> - List available entries:
pass ls - Check for typos in the entry path
- If using
namePrefix, remember it's prepended automatically
GPG decryption failed
- Ensure your GPG key is available:
gpg --list-keys - Start the GPG agent:
gpgconf --launch gpg-agent - You may need to enter your GPG passphrase
Password store not initialized
- Run
pass init "Your GPG Key ID"to initialize the store - See
pass init --helpfor details
