varsvars
Encryption & Security

Multi-PIN

Owner-scoped PINs that give each team access to only their secrets.

Not everyone on the team needs access to every secret. vars lets you create owner PINs, which can only decrypt fields tagged with a specific owner.


How it works

Tag variables with owner metadata:

DB_PASSWORD : z.string() {
  dev  = "dev-db-pass"
  prod = "prod-db-pass"
} (owner = "backend-team")

GA_TOKEN : z.string() {
  dev = "ga-token-123"
} (owner = "frontend-team")

Then create a PIN for that owner:

vars pin create backend-team

The CLI:

  1. Derives a sub-key from the master key using HKDF
  2. Wraps that sub-key with the new owner PIN
  3. Re-encrypts all fields tagged (owner = "backend-team") with the derived sub-key

The backend team's PIN cannot decrypt the frontend team's fields, or any unowned fields. This isn't access control enforced by the CLI; it's different keys.


Encrypted tokens show ownership

Owner-encrypted values include the owner in the prefix:

# Master-encrypted (no owner)
enc:v2:aes256gcm-det:<iv>:<ciphertext>:<tag>

# Owner-encrypted
enc:v2:aes256gcm-det:owner=backend-team:<iv>:<ciphertext>:<tag>

The CLI uses this to skip fields it can't decrypt without trying.


Partial show/hide

When a backend team member runs vars show with their PIN, they get a partially decrypted file:

# Their fields — decrypted
DB_PASSWORD : z.string() {
  dev  = "dev-db-pass"
  prod = "prod-db-pass"
} (owner = "backend-team")

# Other fields — still encrypted
GA_TOKEN : z.string() {
  dev = enc:v2:aes256gcm-det:owner=frontend-team:...
} (owner = "frontend-team")

# Unowned fields — still encrypted
API_KEY = enc:v2:aes256gcm-det:...

vars hide with an owner PIN only re-encrypts that owner's fields, leaving everything else untouched.


Sharing access

To give the backend team access, copy .varskey and delete the lines they shouldn't have:

pin:v1:aes256gcm:owner=backend-team:<salt>:<iv>:<ct>:<tag>

Give them this key file and the PIN. No CLI command needed.


Unowned fields

Fields without (owner = "...") metadata are encrypted with the master key. Only the master PIN can decrypt them. Owner PINs skip them entirely.


Master PIN still decrypts everything

The master PIN holds the master key, which can derive any owner sub-key. It always has full access. Owner PINs can only decrypt their own fields.


Key rotation with owner PINs

vars rotate with owner PINs present will warn you that all owner PINs will be invalidated, then ask for confirmation. After rotation, re-run vars pin create for each owner to issue new PINs derived from the new master key.