Local encryption
Varlock includes a built-in varlock() function that lets you secure local untracked secrets (typically in git-ignored env files like .env.local).
This allows you to keep everything out of plaintext - even temporary local overrides, or a “secret-zero” which is needed by some plugins to load the rest of your sensitive data.
Sensitive values will be stored encrypted, with the key linked to your local device, and requiring no extra configuration. The encryption mechanism varies per platform, but as it is tied to your device, these values are not meant to be shared or committed to git.
PLAINTEXT=shh-im-secret # 🚨 dangerSECURED=varlock(local:abc123...) # ✅ secured at restQuick start - existing secrets
Section titled “Quick start - existing secrets”You likely already have some plaintext secrets in a .env.local file. If not you can create one, and add some. Ensure those items are marked as @sensitive in your schema. Then you can use varlock encrypt to encrypt them in-place:
- Run
varlock encrypt --file .env.localto encrypt them in-place - Sensitive plaintext values are replaced with
varlock("local:<***encrypted***>") - Decryption happens automatically during
varlock load/varlock run
Only plaintext values of @sensitive items are encrypted, so you may run it multiple times.
Using varlock(prompt) resolver
Section titled “Using varlock(prompt) resolver”When you need to edit a value or add a new sensitive item, just set the value to varlock(prompt) and run varlock load. You will be prompted for the new value in a secure input prompt, and the encrypted value will be written back to the file automatically.
EXISTING_ITEM=varlock(local:abc123...)NEW_ITEM=varlock(prompt) # will prompt you for new valueUsing varlock encrypt CLI
Section titled “Using varlock encrypt CLI”You can also call the varlock encrypt CLI command to get a secure prompt to encrypt a single value. It will spit out an item you can copy/paste into your file:
◇ Enter the value you want to encrypt│ ▪▪▪▪▪▪▪▪
Copy this into your .env.local file and rename the key appropriately:
SOME_SENSITIVE_KEY=varlock("local:ABC123...")--file option
Section titled “--file option”As outlined above, you can also run varlock encrypt --file .env.local to encrypt all sensitive plaintext values in a file in-place. This is a great way to quickly encrypt many secrets at once.
Core commands
Section titled “Core commands”Use varlock encrypt to create encrypted payloads:
# Interactive: encrypt a single valuevarlock encrypt# Batch: encrypt all sensitive plaintext values in .env.localvarlock encrypt --file .env.localUse varlock reveal to inspect decrypted values safely:
varlock reveal # interactive - select and revealvarlock reveal API_KEY # securely reveal specific itemvarlock reveal API_KEY --copy # copy to clipboardUse varlock lock to invalidate biometric session cache when stepping away:
varlock lockBackend selection overview
Section titled “Backend selection overview”Varlock chooses the best available backend automatically:
| Platform | Backend | Key Storage | Biometric |
|---|---|---|---|
| macOS | Secure Enclave | Hardware Secure Enclave | Touch ID |
| Windows | NCrypt TPM + Windows Hello | TPM (when available), else DPAPI | Windows Hello (face/fingerprint/PIN) |
| Linux | TPM2 / Secret Service | TPM2 and/or system key store | Yes (when configured via polkit/PAM) |
| All platforms | File-based fallback | ~/.varlock/ directory | No |
If native capabilities are unavailable, varlock falls back to file-based local encryption.
With npm installs, the native helper arrives through per-platform optional dependencies (@varlock/native-helper-*), so only your platform’s binary is downloaded. The one exception: Linux installs also receive the Windows helper, since WSL uses it for Windows Hello and TPM support. If your install skips optional dependencies (for example --no-optional / --omit=optional), the native helper is not installed and varlock uses the file-based fallback, with a warning printed when local encryption is used.
Verify encryption backend (macOS/Linux)
varlock-local-encrypt statusVerify encryption backend (Windows/WSL)
varlock-local-encrypt.exe statusUnattended decryption (headless servers & CI)
Section titled “Unattended decryption (headless servers & CI)”Local encryption isn’t just for laptops. On a headless host with a TPM (a home server, a CI runner, a container host), varlock seals the key to the machine’s TPM and decrypts automatically at load with no prompt. That makes it a good way to store a “secret-zero” (like a 1Password service account token) that bootstraps the rest of your secrets:
# @type=opServiceAccountToken @sensitive @internalOP_TOKEN=varlock("local:...")On Linux this requires tpm2-tools and a TPM (see Linux setup below). The sealed value survives reboots and is bound to that specific machine. It can’t be copied to another host.
This unattended path applies to backends whose key is unsealed to the host: Linux (TPM2) and Windows (NCrypt/TPM). macOS is different: Secure Enclave keys never leave the enclave, and every decrypt requires user presence (Touch ID or password), so there is no unattended decrypt on macOS. A headless Mac or CI runner falls back to file-based encryption instead.
What this protects (and what it doesn’t)
Section titled “What this protects (and what it doesn’t)”Hardware-backed encryption protects your secrets at rest: a stolen disk, a leaked backup, or an env file committed by mistake can’t be decrypted off the machine.
It does not protect against an attacker who is already running code as your user on that machine. They can ask the TPM to unseal the value exactly like varlock does. This is the same trade-off as tools like systemd-creds, and it’s inherent to unattended decryption: if no human is present to approve, anything running as you can decrypt.
If you want a presence gate (fingerprint / password / YubiKey via polkit + PAM) on every decrypt, register the policy:
sudo varlock-local-encrypt setup --linux-biometricsOnce configured, varlock’s normal decrypt flow requires user presence, so everyday loads are no longer unattended. Enable this only on interactive machines, not headless servers.
Treat this gate as a consent prompt, not an at-rest boundary. On Linux the TPM unseal isn’t bound to polkit, so (as noted above) other code already running as your user can still unseal directly. It means “a human approved this load,” not “only a human can ever decrypt.”
Platform details & setup
Section titled “Platform details & setup”- Native Swift helper (Secure Enclave integration)
- Uses system-native secure input / auth prompts
- Includes a menu bar applet flow for native interactions
- Hardware-backed key protection via Secure Enclave with biometric auth where supported
✅ No additional install/setup steps required
Windows
Section titled “Windows”- Native helper with TPM-sealed key protection when a TPM 2.0 chip is available (via NCrypt / Platform Crypto Provider)
- Falls back to DPAPI (user-session-scoped) when TPM sealing is unavailable
- Windows Hello gates interactive decrypts (fingerprint/face/PIN), separate from at-rest protection, same as before
- Windows native and WSL workflows are both supported (WSL uses the Windows
varlock-local-encrypt.exevia--via-daemon; Hello + TPM behavior is identical) - Automated daemon startup/installation behavior is built in for biometric session flows
- WSL decrypt flows use a native bridge to the Windows daemon path
✅ No additional install/setup steps required
Upgrading existing keys to TPM
Section titled “Upgrading existing keys to TPM”New keys automatically use TPM sealing when available. Existing DPAPI keys are auto-upgraded to TPM sealing on the next successful decrypt (public key unchanged, no re-encryption of .env values). To upgrade without decrypting, use varlock-local-encrypt rewrap-key --key-id varlock-default.
- Native Linux helper when available
- User-presence verification via polkit/PAM (can support fingerprint/face/password depending system setup)
Common packages/tools:
tpm2-tools(plus distro TPM2 libs such astpm2-tss)polkitfor user-presence authorization flowsxcliporxselforvarlock reveal --copy
Example installs:
# Debian/Ubuntusudo apt-get updatesudo apt-get install -y tpm2-tools tpm2-tss policykit-1 xclip# Fedora/RHEL variantssudo dnf install -y tpm2-tools tpm2-tss polkit xclipIf biometric/user-presence prompts are unavailable on Linux, complete policy setup (native helper command):
sudo varlock-local-encrypt setup --linux-biometricsAntivirus false positives
Section titled “Antivirus false positives”varlock-local-encrypt is Varlock’s official local-encryption helper, installed with the npm package and the standalone CLI. Microsoft Defender sometimes flags it as Trojan:Win32/Wacatac.C!ml, Trojan:Script/Wacatac.C!ml, or similar. These are generic machine-learning detections that fire on small, new, unsigned executables. The binary is safe when obtained from official Varlock releases.
The npm package installs the helper through per-platform optional dependencies (@varlock/native-helper-darwin, -linux-x64, -linux-arm64, -win32-x64), so you only download the binary for your own platform. One exception: the Windows helper also installs on Linux, because WSL uses it for Windows Hello and TPM support. So a scanner on Linux may still see a Windows .exe, but a detection against one platform’s binary can no longer block installs on the others.
Verify the download. Each GitHub release includes SHA256SUMS.txt with hashes for all native helpers. In npm installs the helper lives under node_modules/@varlock/native-helper-<platform>/; compare it against the matching native-bins/<platform>/... line for your release version:
# Windows PowerShellGet-FileHash varlock-local-encrypt.exe -Algorithm SHA256# macOS / Linuxshasum -a 256 varlock-local-encryptIf a file was quarantined. On Windows, restore it from Windows Security → Protection history, or add an exclusion for your project directory. On macOS and Linux, restore it through your endpoint security console. Reinstalling from npm or the official release also works once the exclusion is in place.
Reporting. If you hit a detection on a current release, file an issue with the file path and the detection name. You can also submit the file to Microsoft as a false positive, which is what gets the definition corrected for everyone.
What we do to prevent this. Windows binaries are Authenticode-signed via Azure Artifact Signing, macOS binaries are Developer ID signed and notarized, and no binary is compressed with an executable packer (packers such as UPX are a well-known trigger for these detections).