Identities

Keys

A key is one line of text. Keep it in your OS keychain, your password manager, or your platform's secret store, never in the repository.

msecrets identity new
msecrets identity use op://Private/msecrets/identity
msecrets whoami

Key types

Private key (identity) Public key (in msecrets.json) Can
AGE-SECRET-KEY-1… msecrets1… read and write
AGE-SECRET-KEY-PQ-1… (post-quantum, --pq) msecrets1… read and write
any of the above age1… / age1pq1… read only (machines)
OpenSSH ssh-ed25519, plain or passphrase-protected ssh-ed25519 AAAA… read and write

Identities are standard age keys, so keys made with age-keygen work, and values can be decrypted with age itself. An msecrets1… public key carries the age encryption key and an Ed25519 key that verifies the values its owner writes; the signing key is derived from the identity.

Post-quantum keys (ML-KEM-768 + X25519, the age mlkem768x25519 recipient type) protect values against future quantum computers, which matters for data that stays in git history forever. Everyone who reads them needs Node.js 24.7 or newer. An environment mixing post-quantum and classical recipients is only as strong as the classical ones, and msecrets check warns about it.

For developers

msecrets init, join, and identity new create a key and store it in the OS keychain: the macOS Keychain, the Windows Credential Manager, or the Linux Secret Service (secret-tool). Where none is available they fall back to a private file in your config directory (--passphrase encrypts it). They never replace a key that is already stored there: it may be the only copy that decrypts your secrets. Use --account NAME for another key, or msecrets identity use keychain:default to use the existing one.

To use a password manager instead, put the key there and point msecrets at it:

Source Reads the key with
op://vault/item/field 1Password CLI: op read
bw:ITEM Bitwarden CLI: bw get password ITEM
pass:NAME pass show NAME
keychain:ACCOUNT the OS keychain
file:PATH or a path a key file (age format; SSH keys; passphrase-encrypted age files)
env:NAME an environment variable
cmd:COMMAND ARGS… any command that prints the key (gopass, Vault, KeePassXC, …)
msecrets identity new --store print     # prints a new key once; paste it into your password manager
msecrets identity use op://Private/msecrets/identity
msecrets whoami                          # shows which sources were used and what you can access

The sources are saved in config.json in your config directory ($XDG_CONFIG_HOME/msecrets or ~/.config/msecrets; %APPDATA%\msecrets on Windows; MSECRETS_CONFIG_DIR overrides it). They never come from the repository, because a repository must not be able to run commands on your machine.

For machines

msecrets recipient add ci --generate --env staging --env production

This prints a new identity once. Store it in your CI or runtime secret store and provide it as MSECRETS_IDENTITY, MSECRETS_IDENTITY_FILE, /run/secrets/msecrets_identity, or the systemd credential msecrets_identity. The SDK and msecrets run remove MSECRETS_IDENTITY from the environment before your code runs, and run hides all MSECRETS_IDENTITY* variables and MSECRETS_PASSPHRASE from the child process.

Rotating keys

msecrets recipient rotate ci --generate         # new machine key; values are re-encrypted
msecrets identity new --account new             # your own new key, loaded next to the old one
msecrets recipient rotate alice msecrets1…      # then switch your recipient to it

Rotation needs someone who can read every affected environment. Old values remain readable with old keys in git history, so rotate the secrets themselves when a key was exposed.