Skip to content

macOS Keychain

The built-in keychain() resolver lets you load secrets directly from the macOS Keychain using declarative instructions in your .env files. It communicates with the Keychain through Varlock’s native Swift daemon, which enforces biometric authentication (Touch ID) and per-session access control.

  • Built-in: no plugin or extra dependency needed
  • Biometric gating: access is protected by Touch ID via Varlock’s native daemon
  • Interactive picker: use keychain(prompt) to browse and select items via a native dialog
  • Auto-write-back: prompt mode writes the resolved reference back to your config file
  • Named or positional syntax for quick or precise lookups
  • Field selection: extract specific fields from keychain items
  • Multiple keychain support: access the login, System, or custom keychains

Rather than wiring up items individually, the easiest way to get started is by using keychain(prompt). This will open a native picker dialog where you can select existing keychain items, or create new ones. After selection, Varlock will automatically write the resolved reference back into your config file for future use.

ITEM=keychain(prompt) # Opens a native picker dialog

If you already have sensitive plaintext values in a local .env file, import them into macOS Keychain and replace the plaintext with stable keychain(...) references. The file to import is the first argument:

Terminal window
varlock keychain import .env --profile jb

By default this edits the file in place: each sensitive plaintext value is replaced by its keychain(...) ref, so the secret no longer lives on disk. Comments and non-sensitive values are left untouched. To write the refs to a different file instead and leave the source as-is, pass --write-to:

Terminal window
varlock keychain import .env --profile jb --write-to .env.jb

Import requires an existing .env.schema file for the input env file so Varlock knows which input variables are secrets and which are not. It only imports variables marked @sensitive in that schema and never prints secret values. Re-running is safe: values already converted to keychain(...) refs are skipped. By default, Varlock refuses to overwrite an existing Keychain item (or, with --write-to, an existing ref in the target file); pass --force to overwrite.

The file you name must be one Varlock loads as part of your env setup. It resolves your normal env graph (from package.json varlock.loadPath, or the files in the current directory) to read sensitivity from your schema, the same way varlock encrypt --file works. An arbitrary file outside that setup can’t be imported. The value stored in Keychain is taken from the file you named specifically; an override of the same variable in another file (such as .env.local) does not change what gets imported.

Generated refs use service="varlock" and account names like <project>:<profile>:<ENV_VAR>. The project defaults to the current directory name and can be overridden:

Terminal window
varlock keychain import .env --profile jb --project my-app

To store one secret without putting the value in shell history, run set and enter the value at the masked prompt:

Terminal window
varlock keychain set API_KEY --profile jb --write-to .env.jb

This stores the item under service="varlock" with account <project>:<profile>:API_KEY, then writes the matching keychain(...) ref when --write-to is provided. If you need to paste a multi-line secret, pipe it through stdin instead of passing it as a command-line argument:

Terminal window
cat secret.txt | varlock keychain set PRIVATE_KEY --profile jb --write-to .env.jb

By default, set refuses to overwrite an existing Keychain item or env ref. Pass --force to replace both.

If VarlockEnclave cannot read an existing Keychain item, grant access without using /usr/bin/security directly:

Terminal window
varlock keychain fix-access --account "my-app:jb:API_KEY"

--service defaults to varlock, but can be overridden for legacy or manually-created Keychain items:

Terminal window
varlock keychain fix-access --service "com.company.api" --account "admin"

You can also fix every explicit keychain(...) ref in an env file:

Terminal window
varlock keychain fix-access --path .env.jb

To see which Keychain items are available, list them by service name. This shows metadata only (service, account, and keychain) and never reads secret values:

Terminal window
varlock keychain list # same as bare `varlock keychain`
varlock keychain list API_KEY # filter (matches service, account, or label)

Pass --keychain to search a specific keychain, such as System:

Terminal window
varlock keychain list --keychain System

Fetch a secret from the macOS Keychain. Communicates with the Keychain through Varlock’s native daemon, which enforces biometric (Touch ID) authentication.

Array args:

  • service (optional): Service name of the keychain item (positional shorthand)
  • prompt (optional): Enter interactive picker mode

Key/value args:

  • service (optional): Service name of the keychain item
  • account (optional): Account identifier for the keychain item
  • keychain (optional): Name of a specific keychain to search (e.g., "System")
  • field (optional): Specific field to extract from the keychain item
  • prompt (optional): If set, opens a native picker dialog for interactive selection
# Positional shorthand
DATABASE_PASSWORD=keychain("com.company.database")
# Named service param
API_KEY=keychain(service="com.company.api")
# With account
ADMIN_PW=keychain("com.company.db", account="admin")
# Targeting a specific keychain
CERT=keychain("com.company.cert", keychain="System")
# Field selection
TOKEN=keychain("com.company.auth", field="password")
# Interactive picker mode
NEW_SECRET=keychain(prompt)

”keychain() is only supported on macOS”

Section titled “”keychain() is only supported on macOS””

This resolver requires macOS. It is not available on Linux or Windows. If you need cross-platform secret management, consider using one of the plugin-based secret sources.

  • Verify the item exists in Keychain Access.app
  • Check that the service name and account match exactly
  • Try keychain(prompt) to browse available items and grant VarlockEnclave access via the native dialog

If you dismiss the native picker without selecting an item, the resolution will fail. Run varlock again to retry, or replace keychain(prompt) with an explicit keychain(service="...") reference.

The first time you access a keychain item through Varlock, macOS may prompt you to grant access to VarlockEnclave. Approve this to allow future reads. If you accidentally denied access, you can update the item’s Access Control settings in Keychain Access.app.