KeyVault

A key manager and an encrypted store for secrets and files, with a backup that any gpg can read.

Managers and Stores

KeyVault does two different jobs behind one window, and the difference decides how careful you need to be.

SSH, GPG and Age are managed. The key material already exists — in ~/.ssh, in your GPG keyring, in the Age key files you point at under Settings. KeyVault finds it, shows it, and can generate more. It is never the only copy.

Notes, API Keys and Files are stored. Notes and API keys live in the macOS Keychain because KeyVault put them there; files live encrypted in KeyVault's own folder. For a note, or a file whose original you have deleted, nothing else holds a copy — which is the whole reason the backup section below exists.

Getting Started

Open KeyVault and the sidebar lists what it found: SSH keys from ~/.ssh, GPG keys from your keyring, Age keys from the paths in Settings, plus anything you have stored.

Add something with the + under the list, the toolbar, or ⌘N for a note, ⇧⌘N for an API key and ⌥⌘N for a file. A file can also be dropped on the Files list.

The search field in the toolbar, or ⌘F, narrows whatever list the sidebar is showing to the items whose name, service, category or notes contain what you type, ignoring case and accents. It never looks inside a secret. Locking clears it.

Unlocking

If you have set a master passphrase, KeyVault asks for it at launch, and nothing stored is read until you give it. Not even the names — those are the part of a Keychain item that is never encrypted, so a locked window that listed them would be giving away most of what someone looking over your shoulder wanted.

The prompt can be cancelled. The window then sits locked, with an Unlock… button in the middle of it, so opening KeyVault by mistake costs a keystroke instead of a passphrase. ⇧⌘L locks and unlocks from the keyboard, and locking clears what was loaded rather than merely covering it.

KeyVault also shuts itself when you leave it alone — after 15 minutes by default, changed or turned off under Settings → Lock when idle. Time spent in another app counts, since walking away is the case it is for.

It shuts at once, without waiting for the idle limit, when the Mac goes to sleep, locks its screen, or switches to another user. That happens even with Lock when idle set to Never, which is about leaving KeyVault open while you work in other apps, not about leaving the Mac.

An idle lock reopens with Touch ID, and it is the only lock that does. On a Mac without Touch ID, or with no finger enrolled, there is nothing for it to wait for, so it is a full lock that forgets the key. A fingerprint is a yes-or-no answer rather than a secret to derive a key from, so opening a vault from cold with one would mean keeping the key on disk — which is the thing the encryption is there to avoid. An idle lock only hides a vault this run of the app already had open: the key stays in memory where it already was, and nothing new is written anywhere. Lock Vault Now and ⇧⌘L forget the key outright, and after those — as at every launch — only the passphrase opens the vault.

Settings → Change Passphrase… re-encrypts every note, API key and file under a new passphrase, and asks for the current one first. Each item is written again under the new passphrase beside the original before anything is replaced, so a change that is stopped part way — a crash, a force quit, a dead battery — never leaves the vault half in one and half in the other: the next launch either finishes it or undoes it, and says which. Backups made before the change still open with the passphrase each was made with.

Notes

A note is a titled, multi-line secret that KeyVault owns outright — recovery codes, an account number, a licence key, the contents of a key file.

The editor is deliberately not masked. Notes are usually pasted in bulk, often straight from a file, and a masked single-line field mangles multi-line input while hiding the evidence that it did. You cannot proof-read what you cannot see, and for a value nothing else holds a copy of, proof-reading is the point.

Organise Notes… (⇧⌘O) files notes under categories, which group the Notes list. A category lasts as long as a note is filed under it. The menu for each category, under the list, renames it on every note at once — the fix for a misspelling — or removes it, which leaves those notes uncategorised. Nothing is written until you press Save.

To file a single note, Control-click it in the list and choose from Category: a category already in use, None, or New Category…. That takes effect straight away.

Load from File… reads a file in verbatim. Nothing is trimmed — a trailing newline is part of some key formats, and quietly stripping it produces a value that looks right and does not work.

API Keys

Same storage, different question. An API key belongs to a service and fits on one line, so it gets a service field and a masked entry. Use a note instead when the value is long, multi-line, or came out of a file.

Files

Some secrets are whole files: a password manager's CSV export, a cloud service-account key, a recovery kit, a scan. Add File… stores one, up to 25 MB, and so does dropping it on the Files list.

Files are stored only under a master passphrase. Notes and API keys fall back to plain storage until one is set, so the app keeps working; a file store that did the same would just be a folder, so KeyVault asks you to set the passphrase first.

Storing a file does not encrypt the original. It is still wherever it was — usually Downloads, if you have just exported it — and readable by anything that looks. Move the original to the Trash is on by default for that reason, and it happens only after the stored copy has been written and read back. It goes to the Trash rather than away, so a mistake costs a drag back out, but it stays readable there until the Trash is emptied.

Select a stored file and, if it is text, its contents are shown the way a note's secret is — the first 500 lines, which is enough to see that you stored the right thing. A file that is not text shows its name, kind and size. Save a Copy… writes the file back out wherever you choose, readable only by you. That copy is not encrypted, so delete it when you are done with it.

KeyVault never opens a stored file in another app. Doing that means decrypting it to a temporary file that outlives the viewing, somewhere you did not choose, for as long as that app keeps it — so the contents are shown from memory, and they leave only when you save a copy yourself.

Edit renames a file or changes its notes and leaves its contents alone. To replace a file with a newer one, store the new one and delete the old.

Reading a Secret

Select the item and the secret is there. Unlocking the vault was the deliberate act; a second click per note added friction with nothing behind it. Hide puts one away again for the moment someone is standing behind you, and everything stays hidden while the vault is locked.

Only one secret is in memory at a time — selecting anything else drops the last one.

Copy marks the clipboard as concealed, which tells clipboard managers not to archive it, and clears the clipboard after 45 seconds — but only if it still holds that secret, so it never wipes something you copied in the meantime.

Deleting

Deleting a note removes it from the Keychain immediately and permanently, and deleting a file removes it from KeyVault's folder the same way. There is no trash and no undo, which is why the confirmation says so plainly and why there is no minus button beside the list.

If you have not exported a backup, cancel and do that first.

Encrypted Export

Notes live in the macOS Keychain: encrypted at rest, tied to your login. That protects them from other people. It does not protect them from a dead Mac, and Keychain items do not leave the machine they were made on.

Backup & Restore — in the toolbar, or ⇧⌘B — writes every stored secret and file to one passphrase-encrypted archive. It is OpenPGP, AES-256, ASCII-armored.

The archive's passphrase must be at least 12 characters: once the file is somewhere other than this Mac, nothing slows down an attempt to guess it. The sheet shows when this Mac last saved one, in orange once that is more than thirty days ago or has never happened, and says so up front if GnuPG is not installed — from Homebrew (brew install gnupg) or GPG Suite.

Two properties are deliberate. It is plain text, so it survives a password manager field, an email to yourself, or a printer — none of which a binary blob does reliably. And it is readable without KeyVault:

gpg --decrypt keyvault-export.asc > vault.json

Any GnuPG, on any platform, opens it. Inside is a JSON document with each value in the clear, carrying its own decryption instructions. That is the exit: this app is not somewhere your secrets can get stuck.

Stored files are in it too, each one's contents in base64 beside the name it had, and they come back out with nothing but jq and base64. The archive's own instructions say so as well:

jq -r '.items[] | select(.fileName == "export.csv") | .secret' \
  vault.json | base64 --decode > export.csv

A file makes the archive larger by about its own size. A few text exports hardly show; a couple of scans and it is no longer something to paste into a password field.

The passphrase is stored nowhere and cannot be reset. Lose it and the archive is unreadable by you as well as by everyone else.

Restoring

Restore reads an archive and writes its contents back. It is keyed on each item's identifier, so it adds what is missing and updates what already exists rather than creating duplicates. Importing the same archive twice is harmless.

The Restore Drill

Because restore is idempotent, rehearsing it costs nothing. Do it.

Nothing should exist only in KeyVault until you have restored an archive somewhere else and read the value back. An untested backup is not a backup — it is an assumption, and the moment you discover it was wrong is the moment you needed it.

The check that matters is not that the file exists. It is that gpg --decrypt outside the app returns the value you put in, character for character — and, for a stored file, that it writes back out identical to the original.

SSH, GPG and Age

These are managed rather than stored. SSH keys load from ~/.ssh, GPG from the system keyring, and Age from the file paths listed under Settings.

They are not included in the encrypted export, because KeyVault is not their only home — they are files on disk and entries in a keyring, with their own backup story. Deleting an SSH key here deletes the file; deleting a GPG key removes it from your keyring.

How Storage Works

Stored items are kSecClassGenericPassword entries under the service io.github.sevmorris.KeyVault, and each is self-describing: the name, type and metadata live in the Keychain item's own attributes.

That matters more than it sounds. An earlier design kept the index in UserDefaults and only the payload in the Keychain, which meant losing that one file turned every secret into an anonymous blob keyed by an identifier nothing referenced any more. The data survived; the ability to find it did not. Items written under the old scheme are migrated automatically on first launch.

Files are not in the Keychain. The login keychain is one database that every app on the Mac reads and writes, made for secrets measured in bytes, and filling it with files would make them every app's problem. Each stored file is instead a single encrypted file in ~/Library/Application Support/KeyVault/Files, sealed with AES-256-GCM under the key your master passphrase derives — the same key that encrypts your notes. It is self-describing in the same way: its name, notes and original filename are sealed inside it, so there is no index to lose. Unlike a note's name, nothing about a stored file is readable without the passphrase, not even what it is called.

The key that opens that folder comes from your passphrase and a salt kept in the Keychain, so copying the folder to another Mac on its own does not move your files. The encrypted export does.

Requirements

The app sandbox is disabled, because it shells out to those tools.