EP003 · The pieces

EnvFolio: The Pieces

What folio init set up, one piece at a time, so a passphrase prompt never takes you by surprise.

In EP002 you made your store with folio init, and put in a text and a secret. That one command set up several things, and most of the time you never think about them. But the first time gpg pops up and asks for a passphrase, it helps to know who is asking, and for what. Here is the whole picture:

~/.gnupg · your GPG keyring🗝️ the store keypublic halfencrypts secrets, checks textssecret half 🔒decrypts secrets, signs texts~/.folio · your folio storea pass store, with a git history.gpg-idwhich key: the store key's fingerprintnames it🔑 gh/token.gpga secret: encrypted to the key📄 gh/user.txt + .siga text: plain, and signed by the key.git/every change, committed🧑 youtype thepassphrasepinentrygpg's prompt, ina terminal or a windowgpg-agentremembers itfor a whileunlocksfolio runs pass and gpg for you
▶
Rather watch? The same pieces, one at a time, are a short video: flip through the slides.

The store key, in your GPG keyring

The key lives in your GPG keyring (~/.gnupg), with any other keys you have. Like every GPG key, it has two halves. The public half encrypts, and checks signatures: anyone may have it. The secret half decrypts, and signs, and it is locked by the passphrase you chose. This is the sandbox's key, as gpg lists it (a post-quantum one: its encryption subkey is ML-KEM, ky768):

EnvFolio
gpg --list-secret-keys --keyid-format none
sec brainpoolP384r1 2026-10-28 [SC] [expires: 2029-10-27]
C9406AB7E48726D265D45E4AD5DE5723219725DF
uid [ultimate] Demo User <demo@example.com>
ssb ky768_bp256 2026-10-28 [E]

The store, and its .gpg-id

The store is a folder, ~/.folio by default. It is a pass store, kept apart from your own ~/.password-store. folio init makes it private (700): even encrypted, the names of your items and the plain texts would show to other users. A fresh store holds only this:

EnvFolio
ls -A ~/.folio
.git .gitattributes .gpg-id
cat ~/.folio/.gpg-id
C9406AB7E48726D265D45E4AD5DE5723219725DF

.gpg-id is how the store knows its key: one line, the key's fingerprint, written by pass init. Everything in the store is encrypted to, or signed by, the key it names.

Secrets and texts

A secret is a <name>.gpg file, encrypted to the store key: exactly a pass entry. A text is a plain <name>.txt file, signed with the store key in <name>.txt.sig, so a text changed outside EnvFolio, or signed by another key, is refused rather than shown. Both live in the same folders, and a text and a secret can share a name.

Which half of the key a command needs decides whether you are asked for the passphrase. Writing a secret only uses the public half; reading one, or writing a text, needs the secret half:

You…The key doesPassphrase?
add a secretencrypt (public half)no
read a secret, or run a command with onedecrypt (secret half)yes
add or edit a textsign (secret half)yes
read a textcheck the signature (public half)no
back up the storeexport the secret halfyes

The passphrase: pinentry and gpg-agent

EnvFolio never asks for a passphrase itself. gpg does, through its agent: gpg-agent runs a small program, pinentry, that asks you, in the terminal or in a window. The agent then remembers the passphrase for its cache time, so the next folio, pass or gpg needs none. That is why you are asked once, then not again for a while.

  • A window instead of the terminal: install a GUI pinentry (pinentry-gnome3, pinentry-qt, or pinentry-mac on macOS), name it in ~/.gnupg/gpg-agent.conf, and restart the agent:
    echo "pinentry-program $(command -v pinentry-gnome3)" >> ~/.gnupg/gpg-agent.conf
    gpgconf --kill gpg-agent
  • With no display (over SSH, in a container or a booth), keep a terminal pinentry (pinentry-curses or pinentry-tty).
  • How long it remembers is default-cache-ttl and max-cache-ttl (seconds), in the same file.
  • On a machine you trust less (a shared server, a booth in the cloud), --fresh-passphrase on any command, or FOLIO_FRESH_PASSPHRASE=1, has the agent forget the store key's passphrase when the command starts and when it ends.

The history

The store is a git repository, and every change is committed, by pass or by EnvFolio. A removed item is still in the history. This is the sandbox's, after EP002's steps and a few more commands:

EnvFolio
git -C ~/.folio log --oneline
13f452c (HEAD -> master) Remove gh/token-old from store.
25b183b Copy gh/token to gh/token-old.
5de2373 Rename gh/user to gh/login.
…
f696965 Add given password for gh/token to store.
cfa04a2 Add given text for gh/user to store.
d939fe6 Configure git repository for gpg file diff.
60859f8 Add current contents of password store.

And because it is a plain pass store, pass itself still opens every secret in it. If you ever stop using EnvFolio, nothing is locked in:

EnvFolio
PASSWORD_STORE_DIR=~/.folio pass show gh/token
ghp_DEMO0000fake0000token

Bottom line

Under your store is one GPG key. Its public half writes your secrets and checks your texts; its secret half, locked by your passphrase, reads the secrets and signs the texts. The store names that key in .gpg-id, and keeps every change in git. When gpg asks for the passphrase, it is pinentry asking for gpg-agent, which then remembers it for a while. folio runs pass, gpg and git for you, and it is all still a plain pass store. Keep the passphrase, and a backup, safe.

Keep your secrets out of plain sight.

Links