org.nvim

Encryption (org-crypt)

Encrypt the text of an entry with GnuPG, like Emacs' org-crypt. The gpg command (crypt.gpg_program) must be installed and set up. The headline, planning line, property drawer, clock lines, LOGBOOK drawer and the blank lines after them stay in clear; the rest of the entry, subtree included, is replaced by an ASCII-armored message (trailing blank lines stay outside):

* Totally secret                                              :crypt:
:PROPERTIES:
:CRYPTKEY: 0x0123456789012345678901234567890123456789
:END:
-----BEGIN PGP MESSAGE-----

jA0ECQMIkNjbrRrbvfn80mABVGV15nu5Iag/HgOzdDr3dtq1HTkNqHu2MN2nc6ow
...
-----END PGP MESSAGE-----

The format is the one Emacs writes: files encrypted in either editor decrypt in the other. A gpg call that doesn't finish within a minute (e.g. one waiting for a passphrase prompt it can't show) is stopped and reported instead of freezing Neovim.

:Org crypt_encrypt_entry     Encrypt the entry at the cursor
                             (org-encrypt-entry). Encrypted entries are
                             left alone.
:Org crypt_decrypt_entry     Decrypt the entry at the cursor
                             (org-decrypt-entry).
:Org crypt_encrypt_entries   Encrypt every entry matching
                             crypt.tag_matcher (org-encrypt-entries).
:Org crypt_decrypt_entries   Decrypt every matching entry
                             (org-decrypt-entries).
There are no default keys, as in Emacs; bind the actions in mappings.org.
:Org reveal (<C-c><C-r>) decrypts the entry at the cursor first, as
org-crypt does through org-fold-reveal-start-hook. *_entries skip
archived and commented trees, like org-scan-tags.

Keys: crypt.key = false encrypts symmetrically, always (Emacs org-crypt-key nil). Otherwise the CRYPTKEY property (inherited only through use_property_inheritance; the value nil means no key), or else crypt.key, is looked up in the public keyring and every key it matches becomes a recipient. When nothing matches ("", the default, never does) the entry is encrypted symmetrically. Unchanged decrypted text gets its old message back when encrypted again, so saving doesn't rewrite it.

Passphrases are read with inputsecret() (twice when encrypting) and passed to gpg on stdin with --pinentry-mode loopback; for secret keys they are asked only when gpg-agent has none cached. During one *_entries run or save the symmetric passphrase is asked once.

Encrypting on save: with crypt.encrypt_on_save = true, matching entries are encrypted before the buffer is written, by :w or by org itself (refile, archive, capture, agenda edits, mobile push, tangle, ...; Emacs (org-crypt-use-before-save-magic)). As in Emacs the buffer stays encrypted after writing; decrypt again to keep working. When an entry can't be encrypted the write is aborted.

Emacs' auto-save files leak decrypted text to disk; in Neovim the swap file ('swapfile') and a persistent undo file ('undofile') do. Before decrypting in a buffer that has either, crypt.disable_auto_save decides (org-crypt-disable-auto-save): "ask" (default) asks whether to turn both off for the buffer, true turns them off, false keeps them with a warning. "encrypt" (re-encrypt before auto-saving) is not possible, as Neovim has no hook before writing the swap file: it acts like true. Text you yank goes to registers, which 'shada' may save; for files you always keep encrypted consider an autocmd such as:


vim.api.nvim_create_autocmd("BufReadPre", {
  pattern = "*/secret/*.org",
  command = "setlocal noswapfile noundofile shada=",
})

Tag inheritance: children inherit crypt, so crypt_encrypt_entries and crypt_decrypt_entries also visit them (harmless: they are encrypted with their parent). Emacs recommends tags_exclude_from_inheritance = { "crypt" } to keep the tag on the entries that carry it; like Emacs it is not the default.

Config (crypt): tag_matcher "crypt" (a org-match-syntax expression), key "" (string or false), encrypt_on_save false, disable_auto_save "ask", gpg_program "gpg".