Skip to content

Security

Antiphon's security story has two halves that never overlap: the vault protects everything at rest, and OpenPGP protects what travels. Neither invents cryptography; both orchestrate audited tools you already run.

The vault

Everything sensitive, the Maildir, the notmuch index, OAuth tokens and account state, lives inside an encrypted vault. The guarantee is simple: sealed at rest, open in session, ciphertext only off the machine. When the vault is locked, nothing but ciphertext exists on disk; a backup tool copying the store copies ciphertext and never sees your mail.

The store lives at $XDG_DATA_HOME/antiphon/store and the vault mounts its decrypted view at exactly that path, so nothing else needs to know whether a vault is present. Until you set one up, that path is an ordinary directory; antiphon doctor tells you which it is.

Backends

Chosen in config.toml under [vault] backend; auto picks the right one for the platform.

Backend Platform Notes
apfs macOS Encrypted APFS sparse image via hdiutil, AES-256, no admin rights
luks2 Linux LUKS2 container via cryptsetup; open and mount run through a narrow, documented sudo allowance
gocryptfs any FUSE, rootless, the portable reserve

Unlocking and locking

The passphrase is the floor: every vault has one and it always works. The daemon unlocks headlessly through passphrase_cmd, a command that prints the passphrase, never stored in config:

[vault]
backend = "auto"
passphrase_cmd = "pass show antiphon/vault"
idle_lock_minutes = 30       # 0 = open until the daemon stops

Any secret-printing command works: pass, secret-tool lookup, or on macOS security find-generic-password -w -s antiphon-vault (antiphon setup creates exactly that Keychain item). The secret reaches the underlying tool over a private channel, never on a command line another process could read.

The vault opens when the daemon starts and stays open so mail keeps arriving while the client is closed. antiphond seals it on a clean stop, and, with idle_lock_minutes above zero, after that many minutes without a client connection, pausing sync until the next client connects and unlocks it.

Setting it up

# set backend and passphrase_cmd in [vault] first, then:
antiphon vault create          # create and mount the vault
antiphon doctor --init-store   # lay out the store inside it
antiphond &                    # unlocks on start, seals on stop
antiphon

antiphon vault create refuses to shadow an existing plain store; migrate an unencrypted store by moving it aside, creating the vault, and copying the mail back in while it is mounted.

OpenPGP

Antiphon does OpenPGP with Sequoia end to end. Verification is pure Rust inside the client; private-key operations (signing, decryption) are delegated to your running gpg-agent over its own protocol, so your keyring, smartcard and pinentry work exactly as they do everywhere else. Antiphon never reads secret key material.

Verifying

Verification trusts a directory of certificates you curate: $XDG_CONFIG_HOME/antiphon/pgp/. Drop .asc or .pgp exports there:

gpg --armor --export alice@example.com \
    > ~/.config/antiphon/pgp/alice.asc

The pager then shows Good signature from … when a message verifies against a cert in the directory, Unknown signature for signers you have not added (claiming neither good nor bad), and BAD signature when a trusted key matches and the content does not check out. Both PGP/MIME and inline cleartext signatures are handled. There is no web-of-trust or TOFU automation: a cert is trusted because you put it there, nothing else.

Signing and encrypting

Signing is off by default and enabled per identity with pgp_sign = true (optionally pinning pgp_key to a fingerprint). :sign, :nosign, :encrypt and :noencrypt override the default for the next message only, and the compose status line shows the live plan.

Sealing happens through gpg-agent after the editor closes, so pinentry or a smartcard touch appears exactly where gpg would put it. Encryption requires a cert for every To and Cc address, signs inside the encryption layer, and aborts to a draft naming the problem if anything is missing or refused: plaintext is never sent silently where protection was asked for. Received encrypted mail decrypts through the agent when opened, and signatures inside are verified against the same keyring.

antiphon doctor reports the keyring size and whether gpg-agent is reachable, with how many signing keys it holds.