Key is a macOS-native, CLI-first secret manager. It keeps encrypted vault files in a folder you control—local or synchronized through the file-sync provider you choose—uses Touch ID, Apple Watch, or your Mac password for local authentication, and exposes a small command set that composes naturally with the shell. Behind that small interface is a signed, on-demand agent and a device-enrolled security model designed to keep protected key material and trust decisions away from the sync provider.
After installing Key, add a secret from a secure prompt:
key add github/personalOr pipe a value without placing it in the command-line arguments:
openssl rand -base64 32 | key add github/personalRead or copy it, list the vault, and explicitly clear the helper session:
key get github/personal
key copy github/personal
key list
key lockkey unlock authenticates in advance. Otherwise, the first operation that
needs key material prompts through macOS. Key Agent keeps an unlocked session
copy in memory for a short idle window so separate CLI invocations can reuse
it.
TOTP and shell composition are part of the ordinary workflow. Store a bare
Base32 TOTP seed with --totp, or combine hierarchical entry names with tools
such as fzf:
key add --totp github/mfa
key copy github/mfa
key copy "$(key list | fzf)"Full otpauth:// URLs are not accepted yet; provide only their secret
value. Key intentionally has no built-in password generator, so any generator
that writes to stdout can feed key add or key edit.
The command-line client never accesses protected vault-key material directly. It talks over authenticated XPC to the signed, on-demand Key Agent, while macOS gates key use with Touch ID, Apple Watch, or your Mac password. Secrets use AES-256-GCM authenticated encryption, and unlocked key material is reused only inside the agent's short-lived memory session.
The device-enrolled vault strengthens that model for multiple Macs. Each Mac holds non-exportable Secure Enclave identity keys, the vault key is wrapped separately to every approved device using HPKE, and Key trusts membership changes and vault history only after cryptographic authentication.
Key stores its vault in an ordinary folder, so you choose whether it remains local or moves between Macs through iCloud Drive or another ordinary folder-sync provider. The provider transports encrypted files; it is not trusted to decide vault access, device membership, trusted history, or conflict resolution.
File synchronization can arrive late, out of order, or incompletely. A device-enrolled vault authenticates immutable, content-addressed history instead of trusting timestamps or a provider's idea of “latest.” Key automatically merges independent edits, preserves genuine conflicts for explicit resolution, blocks writes when required objects are missing, and fails closed on corruption, rollback, or competing authority. When delivery is incomplete, a narrow stale read can use only the last complete state already trusted on that Mac.
Install Stable for ordinary use. Key Stable and Key Preview are separate installed products, not names for the two vault models. They can live side by side with different apps, CLIs, helpers, configuration, default vaults, and Keychain namespaces.
| Channel | Current release | App and CLI | Homebrew cask | Purpose |
|---|---|---|---|---|
| Stable | 0.2.0 |
Key.app, key |
key |
Ordinary use |
| Preview | 0.2.0-beta.1 |
Key Preview.app, key-preview |
key@beta |
Isolated prerelease testing |
The published Preview beta predates Stable 0.2.0; install it only when you
specifically need to reproduce that prerelease. Preview does not read or
modify Stable configuration, vault selection, or Keychain state.
Important
Keep at least two Macs enrolled in a device-enrolled vault. If every enrolled
Mac and its Secure Enclave identity is lost, the vault is permanently
unrecoverable in 0.2.0. Provider files alone are not a backup, and there is
no password, cloud escrow, support override, or hidden recovery path.
brew tap tvanreenen/tap
brew install --cask key
open -a KeyExisting Homebrew users can update in place:
brew update
brew upgrade --cask key
open -a KeyOpen Key.app once after installation or upgrade so macOS can register Key
Agent. If macOS asks, allow the background item. Then confirm that the CLI and
helper are available:
key version
key statusbrew install --cask tvanreenen/tap/key@beta
open -a "Key Preview"
key-preview version
key-preview statusThe current Preview is older than Stable 0.2.0. Do not point it at the live
Stable vault; use its isolated default vault or a disposable copy. In the
examples below, replace key with key-preview when reproducing Preview
behavior.
Release channel and vault protection are separate choices. Stable 0.2.0
supports both protection models. New Stable vaults begin Keychain-backed, and
upgrades preserve the existing Keychain-backed selection; installing the
release never migrates a vault automatically. Moving to the device-enrolled
model is a separate, explicit operation.
The on-disk names are format v2 and format v3, respectively. The rest of this README uses the descriptive model names because they express the security property that matters to a user:
- A Keychain-backed vault keeps its raw vault key in a local or synchronizable Keychain item.
- A device-enrolled vault wraps the vault key separately to every approved Mac's Secure Enclave identity and keeps plaintext key material only in Key Agent's short-lived session.
| Keychain-backed (format v2) | Device-enrolled (format v3) | |
|---|---|---|
| Access model | Any Mac that receives the selected Keychain item | Explicitly enrolled Macs with equal authority |
| Vault key | Persistent local or synchronizable Keychain item | Wrapped separately to each active Mac's Secure Enclave identity |
| Unlocked key | Reused briefly by Key Agent | Exists in plaintext only in Key Agent's short-lived memory session |
| Provider history | Individually encrypted named files | Authenticated, immutable, content-addressed history |
| Concurrency | Provider filesystem behavior | Automatic independent merges; explicit genuine-conflict resolution |
| Device loss | Depends on the configured Keychain mode | Recoverable only while at least one enrolled Mac survives |
A device-enrolled vault stores encrypted entries, authenticated manifests, public device metadata, and per-device vault-key wrappers in the selected folder. The provider can delay or omit files and deny service, but it cannot silently grant access or choose which history Key trusts.
Migration is explicit and local. It never begins merely because a newer binary was installed.
First run the read-only preflight:
key migrate --checkReview the report, then create and select a verified device-enrolled snapshot:
key migrate --applyMigration converts format v2 to format v3. It retains the Keychain-backed
source files unchanged and selects the device-enrolled vault only after the new
snapshot, local device identity, wrapper, and checkpoint are usable. The
retained source provides a controlled rollback boundary while migration is
being validated, but 0.2.0 has no ordinary rollback command. It does not
receive later device-enrolled changes and is neither a current fallback nor a
recovery key for the new vault.
Other Macs remain on their existing Keychain-backed state. Their later edits are not imported into the migrated snapshot. Enroll each additional Mac into the device-enrolled vault instead of migrating independent copies of the same vault.
Preview can migrate only a Keychain-backed vault and key that already belong to Preview's isolated namespace. It cannot use Stable's protected Keychain state.
Enrollment uses a 10-minute invitation and a comparison code. Both Macs must show the exact same device pair and code before approval.
On an active Mac, inspect the roster and create an invitation using this Mac's exact recorded name:
key share devices
key share invite --name "<this Mac's recorded name>"On the joining Mac, discover or enter that invitation and create an answer:
key share invitations
key share join <invitation-id> --name "Laptop"Use the IDs printed by those commands to compare on both Macs:
key share requests <invitation-id>
key share compare <vault-id> <invitation-id> [join-request-id]Only after the device pair and comparison code match, approve on the existing Mac and accept on the joining Mac:
key share approve <vault-id> <invitation-id> <comparison-code>
key share accept <vault-id> <invitation-id> <comparison-code>Each command prints the exact safe next command for its side of the ceremony. If an invitation expires, begin a fresh ceremony. A prepared exact approval may finish after provider delay, but expiry never authorizes a different request.
Inspect the authenticated roster at any time:
key share deviceskey share revoke <device-id>Revocation requires local authentication and explicit review. It rotates the vault key, re-encrypts the current snapshot, and omits the revoked Mac from new wrappers. It cannot erase old plaintext, screenshots, exports, or key material that device already possessed.
A lost or revoked Mac can rejoin only through a fresh invitation from a
surviving active Mac. Running the ordinary share join command on the revoked
Mac presents a replacement review and requires the literal REJOIN before
removing only that Mac's unusable local enrollment state.
The device-enrolled vault is directly validated on local APFS and iCloud Drive.
Other ordinary folder-backed providers may work if they preserve the required
containment, atomicity, hydration, type, and naming semantics, but they have
not been directly validated and are not covered by the 0.2.0 compatibility
guarantee.
Tip
If the vault is stored in iCloud Drive, Control-click its folder in Finder and choose Keep Downloaded on every enrolled Mac. This keeps the vault locally available and helps reduce incomplete states caused by on-demand file hydration. It does not make iCloud Drive a backup or bypass Key's missing-object checks.
Key does not trust provider timestamps, ordering, mutable metadata, or a
claimed “latest” file. Missing synchronized objects produce an incomplete
state and block writes. Malformed, substituted, rolled-back, or competing
authority objects fail closed. Independent content edits merge automatically;
genuinely incompatible edits remain available through the conflict commands
until you choose the complete resolution.
--allow-stale is intentionally narrow. It permits a read only from the last
complete version already trusted on that Mac when newer provider delivery is
incomplete. It does not bypass corruption, rollback, or an authority conflict.
To move a vault, move its complete directory and update the configured path:
mv ~/.key ~/Secrets/key-vault
key config set vault-dir ~/Secrets/key-vault
key config get vault-dirDo not repoint one product at another product's live vault. If the default directory already contains unrelated files, Key refuses to adopt it.
key status [--json] [--verbose] Explain vault health and the next safe action
key unlock Warm the helper session
key lock Clear the session and stop the helper
key get <name> [--allow-stale] Print a secret or current TOTP code
key copy <name> [--allow-stale] Copy a secret or current TOTP code
key add [--totp] <name> Add a secret from stdin or a secure prompt
key edit [--totp] <name> Update a secret
key duplicate <src> <dst> [--force] Duplicate an entry
key rename <src> <dst> [--force] Rename an entry
key remove <name> [--force] Remove an entry
key list List entry names
key config get <config-name> Print one configuration value
key config set <config-name> <value> Update one configuration value
key config list List known configuration values
key migrate --check Check migration readiness without changing the vault
key migrate --apply Create and select a verified device-enrolled copy
key conflict list [--json] List unresolved device-enrolled conflicts
key conflict show <id> [--json] Inspect authenticated conflict metadata
key conflict get <id> <version> Print one conflicted value
key conflict copy <id> <version> Copy one conflicted value
key conflict resolve <id>=<version>… Resolve the complete listed conflict set
key share devices [--json] List authenticated enrolled devices
key share revoke <device-id> Review and revoke a device
key share invitations List available invitations
key share invite --name <name> Create an invitation from this Mac
key share join <invite> --name <name> Answer an invitation on the joining Mac
key share requests <invite> List answers to an invitation
key share compare <vault> <invite> [request]
Show the device pair and comparison code
key share approve <vault> <invite> <code>
Approve the compared joining Mac
key share accept <vault> <invite> <code>
Trust and select the approved vault here
key version [--json] Print the CLI version
key help Show the complete command reference
Key is intentionally macOS-specific and requires macOS 14 or later for the device-enrolled Secure Enclave and CryptoKit HPKE profile. The installed product has three signed components:
Key.appregisters the helper and shows installation diagnostics.- The
keyCLI handles command parsing, terminal I/O, and clipboard writes. - Key Agent owns Keychain and Secure Enclave access, encryption, authenticated storage operations, and the short-lived in-memory session.
The CLI talks to the on-demand helper over an authenticated XPC Mach service.
launchd starts it when needed, and the helper exits after its idle window.
The CLI does not directly access protected vault-key material.
Key uses AES-256-GCM for entries, HKDF-SHA256 and HMAC-SHA256 for derived manifest authentication, P-256 signatures for device-authorized transitions, and RFC 9180 HPKE with P-256, HKDF-SHA256, and AES-256-GCM for per-device vault key wrapping.
The 0.2.0 assurance boundary includes extensive focused internal review,
automated fault and security coverage, notarized installed-product checks, and
two-device physical qualification on local APFS and iCloud Drive. It has not
received an independent third-party security audit. A third physical device
and additional storage providers were not direct release gates.
For the complete promises and limitations, read:
- Security, continuity, and recovery
- Version 3 device-wrapped key architecture
- Version 3 implementation and qualification tracker
- Release process
The Swift package and release-script checks run with:
just testThe Xcode project builds the signed host apps and helpers. Signing, notarization, Preview isolation, and release publication are documented in the release process and Apple setup guide.
Key is available under the MIT License.
