Bitwarden Plugin
Our Bitwarden plugin enables secure loading of secrets from Bitwarden using declarative instructions within your .env files. It supports two distinct Bitwarden products:
- Secrets Manager — programmatic secret storage, accessed via machine account access tokens over the REST API. Best for CI/CD and production. Uses
@initBitwarden()+bitwarden(). - Password Manager (and self-hosted Vaultwarden) — your personal/team password vault, accessed via the
bwCLI. Best for local development. Uses@initBwp()+bwp().
Features
Section titled “Features”- Zero-config authentication - Just provide your machine account access token
- UUID-based secret access - Fetch secrets by their unique identifiers
- Self-hosted Bitwarden support - Configure custom API and identity URLs
- Multiple instances - Connect to different organizations or self-hosted instances
- Comprehensive error handling with helpful tips
- Lightweight implementation using REST API (48 KB bundle, no native SDK dependencies)
Installation and setup
Section titled “Installation and setup”In a JS/TS project, you may install the @varlock/bitwarden-plugin package as a normal dependency.
Otherwise you can just load it directly from your .env.schema file, as long as you add a version specifier.
See the plugins guide for more instructions on installing plugins.
# 1. Load the plugin# @plugin(@varlock/bitwarden-plugin)## 2. Initialize the plugin - see below for more details on options# @initBitwarden(accessToken=$BITWARDEN_ACCESS_TOKEN)# ---
# 3. Add a machine account access token config item# @type=bitwardenAccessToken @sensitive @internalBITWARDEN_ACCESS_TOKEN=Machine account setup
Section titled “Machine account setup”-
Create a machine account in your Bitwarden organization
Navigate to your Bitwarden organization’s Secrets Manager → Machine accounts → Click New machine account.
Provide a name (e.g., “Production App”) and save it.
-
Copy the access token (displayed only once!)
After creating the machine account, you’ll see an Access token. Copy it immediately - it will only be displayed once.
-
Grant access to secrets
Grant your machine account access to the specific projects or secrets you need.
Via Projects:
- Create or select a project in Secrets Manager
- Add secrets to the project
- Grant your machine account access to the project
Direct Secret Access:
- Navigate to a specific secret
- Click Access
- Add your machine account with “Can read” permissions
-
Wire up the token in your config
.env.schema # @plugin(@varlock/bitwarden-plugin)# @initBitwarden(accessToken=$BITWARDEN_ACCESS_TOKEN)# ---# @type=bitwardenAccessToken @sensitive @internalBITWARDEN_ACCESS_TOKEN= -
Set your access token in environments
Use your CI/CD system or platform’s env var management to securely inject the
BITWARDEN_ACCESS_TOKENvalue.
Self-hosted Bitwarden
Section titled “Self-hosted Bitwarden”For self-hosted Bitwarden instances, you’ll need to provide both the API and identity URLs:
# @plugin(@varlock/bitwarden-plugin)# @initBitwarden(# accessToken=$BITWARDEN_ACCESS_TOKEN,# apiUrl="https://bitwarden.yourcompany.com/api",# identityUrl="https://bitwarden.yourcompany.com/identity"# )Multiple instances
Section titled “Multiple instances”If you need to connect to multiple organizations or instances, register multiple named instances:
# @initBitwarden(id=prod, accessToken=$PROD_ACCESS_TOKEN)# @initBitwarden(id=dev, accessToken=$DEV_ACCESS_TOKEN)# ---
PROD_SECRET=bitwarden(prod, "11111111-1111-1111-1111-111111111111")DEV_SECRET=bitwarden(dev, "22222222-2222-2222-2222-222222222222")Loading secrets
Section titled “Loading secrets”Once the plugin is installed and initialized, you can start adding config items that load values using the bitwarden() resolver function.
Basic usage
Section titled “Basic usage”Fetch secrets by their UUID:
# Fetch secrets by UUIDDATABASE_URL=bitwarden("12345678-1234-1234-1234-123456789abc")API_KEY=bitwarden("87654321-4321-4321-4321-cba987654321")Multiple instances
Section titled “Multiple instances”If you have multiple plugin instances, specify which instance to use:
PROD_ITEM=bitwarden(prod, "11111111-1111-1111-1111-111111111111")DEV_ITEM=bitwarden(dev, "22222222-2222-2222-2222-222222222222")Bitwarden Setup
Section titled “Bitwarden Setup”Create a machine account
Section titled “Create a machine account”Machine accounts provide programmatic access to Bitwarden Secrets Manager.
-
Log in to your Bitwarden organization web vault
-
Navigate to Secrets Manager → Machine accounts
-
Click “New machine account”
-
Provide a name (e.g., “Production App”)
-
Copy the Access token (shown only once!)
-
Grant access to specific projects or secrets
Permission Levels:
- Can read - Retrieve secrets only (recommended for most use cases)
- Can read, write - Retrieve, create, and edit secrets
Grant access to secrets
Section titled “Grant access to secrets”Via Projects (Recommended):
- Create or select a project in Secrets Manager
- Add secrets to the project
- Grant your machine account access to the project
This approach makes it easier to manage access to multiple secrets at once.
Direct Secret Access:
- Navigate to a specific secret
- Click Access
- Add your machine account with appropriate permissions
Password Manager / Vaultwarden
Section titled “Password Manager / Vaultwarden”Separate from Secrets Manager, you can also load values from your personal or team Bitwarden Password Manager vault — or a self-hosted Vaultwarden server. This path uses the Bitwarden bw CLI rather than the REST API, so it’s best suited to local development.
varlock acquires and caches a CLI session token for you: on first load it runs bw unlock (prompting for your master password), then caches the token in varlock’s encrypted cache and reuses it on subsequent loads. You no longer need to manually unlock and paste a session token into a .env file.
By default the token is cached indefinitely (sessionTtl="forever"). This is safe because the cached token is encrypted at rest with varlock’s local key — biometric-gated (Touch ID / Windows Hello) on platforms with a secure enclave — so reading it requires you. Note the bw CLI session does not auto-lock on system sleep/lock/restart (those are app-only vault-timeout settings), so by default a fresh master-password unlock is only needed when the session is explicitly invalidated (bw lock/bw logout, an external bw unlock, or an account policy). Set a shorter sessionTtl if you want varlock to force periodic master-password re-auth.
Prerequisites
Section titled “Prerequisites”-
Install the
bwCLI from a trusted source.Terminal window # macOSbrew install bitwarden-cli# Linuxsnap install bw# Windowschoco install bitwarden-cliOr download the official binary from the Bitwarden CLI docs.
-
Log in once with your account:
Terminal window bw login# For Vaultwarden / self-hosted, point the CLI at your server first:# bw config server https://vault.yourcompany.comvarlock handles unlocking from here on — you don’t need to run
bw unlockyourself.
Basic usage
Section titled “Basic usage”# 1. Load the plugin# @plugin(@varlock/bitwarden-plugin)## 2. Initialize the Password Manager instance (no token needed)# @initBwp()# ---
# 3. Load values from vault items by name or UUIDDATABASE_PASSWORD=bwp("Production DB")On the first varlock load, you’ll be prompted for your master password once; the resulting session token is cached and reused (sessionTtl, default forever). The bw CLI session does not lock on system sleep, lock, or restart — those vault-timeout settings apply to the Bitwarden apps and extension, not the CLI — so in practice you re-enter the master password only when the session is explicitly invalidated (see below) or when you set a shorter sessionTtl.
Selecting a field
Section titled “Selecting a field”By default bwp() returns the item’s password. Use the field argument to fetch a different field. Standard fields are password, username, notes, totp, and uri; any other name is matched against the item’s custom fields (case-insensitive).
DB_USER=bwp("Production DB", field=username)DB_PASSWORD=bwp("Production DB", field=password)# the stored TOTP secret/seed, not a generated 6-digit code (see note below)API_TOTP_SEED=bwp("Some Service", field=totp)# custom field on the itemSTRIPE_KEY=bwp("Stripe", field="secret-key")Caching and TTLs
Section titled “Caching and TTLs”# @initBwp(sessionTtl="1h", cacheTtl="5m")sessionTtl(defaultforever) — how long the unlocked CLI session token is cached before varlock forces a freshbw unlock.foreverkeeps the token until the CLI session is invalidated externally (bw lock/bw logout, an externalbw unlock, or account policy), at which point the next load re-unlocks automatically; set e.g."1h"to force periodic master-password re-auth.cacheTtl(optional) — additionally cache the resolved item values, avoiding abwinvocation per value within the window. See the Caching guide.
Non-interactive loads
Section titled “Non-interactive loads”Interactive unlock needs a TTY, so a non-interactive load (a dev server started by a tool that has no TTY, an editor task runner, etc.) can’t show the master-password prompt. How you fix it depends on the environment:
Local dev, using auto-unlock — if you’re letting varlock unlock the vault itself (no sessionToken / masterPassword configured on @initBwp()), run varlock load once in a regular terminal (npx varlock load, pnpm exec varlock load, etc.). That performs the interactive unlock and caches the session token in varlock’s encrypted cache, so subsequent non-interactive commands reuse it without prompting (until the session is invalidated — see sessionTtl above). This only applies to the auto-unlock flow — if you’ve configured sessionToken= or masterPassword=, no interactive varlock load is needed.
CI / truly headless — there’s no terminal to unlock from, so provide credentials explicitly instead:
# Option A: pass a pre-obtained session token (from `bw unlock --raw`)# @initBwp(sessionToken=$BWP_SESSION)
# Option B: let varlock unlock non-interactively with a master password# @initBwp(masterPassword=$BW_MASTER_PASSWORD)# ---# @type=bwSessionToken @sensitive @internalBWP_SESSION=Multiple accounts / servers
Section titled “Multiple accounts / servers”The bw CLI keeps one logged-in account per data directory, so to read from more than one account or server (e.g. personal + work, or bitwarden.com + a self-hosted Vaultwarden) you give each instance its own data dir via appDataDir. Set up each dir once with the bw CLI (bw config server … if needed, then bw login), then point an instance at it. Since a data-dir path is specific to your machine, reference it from a per-developer value rather than hardcoding it:
# @initBwp(id=personal, appDataDir=$BWP_PERSONAL_DIR)# @initBwp(id=work, appDataDir=$BWP_WORK_DIR)# ---# bw data dirs — set per-machine in .env.local (a leading ~ is expanded)BWP_PERSONAL_DIR=BWP_WORK_DIR=
PERSONAL_TOKEN=bwp(personal, "My Token")WORK_DB=bwp(work, "Work DB", field=password)varlock unlocks and caches a session token per account (keyed by appDataDir), so the two accounts never share or invalidate each other’s sessions.
Reference
Section titled “Reference”Root decorators
Section titled “Root decorators”@initBitwarden()
Section titled “@initBitwarden()”Initialize a Bitwarden Secrets Manager plugin instance for accessing secrets.
Key/value args:
accessToken(required): Machine account access token. Should be a reference to a config item of typebitwardenAccessToken.apiUrl(optional): API URL for self-hosted Bitwarden (defaults tohttps://api.bitwarden.com)identityUrl(optional): Identity service URL for self-hosted Bitwarden (defaults tohttps://identity.bitwarden.com)cacheTtl(optional): cache resolved values for the specified duration (e.g."5m","1h","1d", orforeverto cache until manually cleared). For cache mode behavior and CLI cache controls, see the Caching guide.id(optional): Instance identifier for multiple instances
# @initBitwarden(accessToken=$BITWARDEN_ACCESS_TOKEN)# ---# @type=bitwardenAccessToken @sensitive @internalBITWARDEN_ACCESS_TOKEN=@initBwp()
Section titled “@initBwp()”Initialize a Password Manager / Vaultwarden instance accessed via the bw CLI. With no arguments, varlock unlocks the vault interactively on first use and caches the session token.
Key/value args (all optional):
sessionToken: a pre-obtained CLI session token (e.g.$BWP_SESSION). When provided and non-empty, it’s used as-is instead of auto-unlocking.masterPassword: master password used to unlock the vault non-interactively (e.g. in CI). Reference a@sensitiveconfig item.sessionTtl: how long the auto-unlocked session token is cached before a freshbw unlock(defaultforever— kept until the CLI session is invalidated externally; e.g."1h"to force periodic master-password re-auth).cacheTtl: cache resolved item values for the specified duration. See the Caching guide.appDataDir: bw CLI data directory (BITWARDENCLI_APPDATA_DIR) for this instance — point separate instances at separate dirs to read from different accounts/servers. A leading~is expanded. The dir must be set up independently (bw config server+bw login).id: instance identifier for multiple instances.
# @initBwp(sessionTtl="1h")# ---DB_PASSWORD=bwp("Production DB")Data types
Section titled “Data types”bitwardenAccessToken
Section titled “bitwardenAccessToken”This type is @internal by default — varlock uses it to fetch your other secrets but does not inject it into your application. Override with @internal=false if your app uses the credential directly — for example to write secrets back or fetch additional secrets at runtime.
Represents a Bitwarden Secrets Manager machine account access token. Validation ensures the token is in the correct format (0.<client_id>.<client_secret>:<encryption_key>).
Note that the type itself is marked as @sensitive, so adding an explicit @sensitive decorator is optional.
# @type=bitwardenAccessTokenBITWARDEN_ACCESS_TOKEN=bitwardenSecretId
Section titled “bitwardenSecretId”Represents a secret UUID in Bitwarden Secrets Manager. Validation ensures the ID is a valid UUID format.
# @type=bitwardenSecretIdMY_SECRET_ID=12345678-1234-1234-1234-123456789abcbitwardenOrganizationId
Section titled “bitwardenOrganizationId”Represents an organization UUID in Bitwarden. Validation ensures the ID is a valid UUID format.
# @type=bitwardenOrganizationIdBITWARDEN_ORG_ID=87654321-4321-4321-4321-cba987654321bwSessionToken
Section titled “bwSessionToken”This type is @internal by default — varlock uses it to fetch your other secrets but does not inject it into your application. Override with @internal=false if your app uses the credential directly — for example to write secrets back or fetch additional secrets at runtime.
Represents a Bitwarden CLI session token (the output of bw unlock). Only needed when supplying a token manually for non-interactive use. Marked @sensitive.
# @type=bwSessionTokenBWP_SESSION=Resolver functions
Section titled “Resolver functions”bitwarden()
Section titled “bitwarden()”Fetch a secret from Bitwarden Secrets Manager by UUID.
Array args:
instanceId(optional): instance identifier to use when multiple plugin instances are initializedsecretId(required): secret UUID in the formatxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# Fetch by secret UUIDDATABASE_URL=bitwarden("12345678-1234-1234-1234-123456789abc")
# With instance IDPROD_SECRET=bitwarden(prod, "11111111-1111-1111-1111-111111111111")Fetch a field from a Password Manager / Vaultwarden vault item via the bw CLI.
Args:
instanceId(optional, positional): instance identifier when multiple@initBwp()instances are initializeditem(required, positional): item name or UUID to look upfield(optional, key/value): which field to return —password(default),username,notes,totp(the stored TOTP secret/seed, not a generated code),uri, or a custom field name
# Default field (password)DB_PASSWORD=bwp("Production DB")
# Specific fieldDB_USER=bwp("Production DB", field=username)
# With instance IDWORK_DB=bwp(work, "Work DB", field=password)Troubleshooting
Section titled “Troubleshooting”Secret not found
Section titled “Secret not found”- Verify the secret UUID is correct (must be valid UUID format)
- Check that the secret exists in your Bitwarden Secrets Manager
- Ensure your machine account has access to the secret or its project
Permission denied
Section titled “Permission denied”- Verify your machine account has “Can read” or “Can read, write” permissions
- Check that the machine account has access to the specific secret
- Review the access settings in Bitwarden Secrets Manager console
Authentication failed
Section titled “Authentication failed”- Verify the access token is correct
- Check if the access token has been revoked or expired
- Ensure the machine account is not disabled
- For self-hosted: verify
apiUrlandidentityUrlare correct
Invalid UUID format
Section titled “Invalid UUID format”- Secret IDs must be valid UUIDs:
12345678-1234-1234-1234-123456789abc - Check for typos or incorrect format
- UUIDs should contain 32 hexadecimal characters and 4 hyphens
Password Manager (bwp())
Section titled “Password Manager (bwp())”bwnot found — install the CLI from a trusted source and ensure it’s on yourPATH.- Not logged in — run
bw loginonce (for Vaultwarden, alsobw config server <url>first). - Cannot unlock without an interactive terminal — you’re auto-unlocking (no
sessionToken/masterPasswordconfigured) in a non-TTY context. For local dev, runvarlock loadonce in a regular terminal to unlock and cache the session token; for CI / headless, providesessionToken=$BWP_SESSIONormasterPassword=...to@initBwp(). See Non-interactive loads. - Item / field not found — verify the item name or UUID; the error lists the item’s available custom fields.