Skip to content

Instantly share code, notes, and snippets.

@kevinoid
Created July 31, 2019 16:31
Show Gist options
  • Save kevinoid/189a0168ef4ceae76ed669cd696eaa37 to your computer and use it in GitHub Desktop.
Save kevinoid/189a0168ef4ceae76ed669cd696eaa37 to your computer and use it in GitHub Desktop.
GnuPG pinentry script for terminal or graphical interface based on $PINENTRY_USER_DATA.
# ~/.bashrc: executed by bash(1) for non-login shells.
# If file exists (likely) copy fragment below into existing script:
# If stdin is a terminal
if [ -t 0 ]; then
# Set GPG_TTY so gpg-agent knows where to prompt. See gpg-agent(1)
export GPG_TTY="$(tty)"
# Set PINENTRY_USER_DATA so pinentry-auto knows to present a text UI.
export PINENTRY_USER_DATA=USE_TTY=1
fi
# gpg-agent configuration. Usually ~/.gnupg/gpg-agent.conf. See gpg-agent(1).
# Use pinentry-auto script as the pinentry program.
pinentry-program /path/to/pinentry-auto
#!/bin/sh
# Choose between pinentry-tty and pinentry-x11 based on whether
# $PINENTRY_USER_DATA contains USE_TTY=1
#
# Based on:
# https://kevinlocke.name/bits/2019/07/31/prefer-terminal-for-gpg-pinentry
#
# Note: Environment detection is difficult.
# - stdin is Assuan pipe, preventing tty checking
# - configuration info (e.g. ttyname) is passed via Assuan pipe, preventing
# parsing or fallback without implementing Assuan protocol.
# - environment is sanitized by atfork_cb in call-pinentry.c (removing $GPG_TTY)
#
# $PINENTRY_USER_DATA is preserved since 2.08 https://dev.gnupg.org/T799
#
# Format of $PINENTRY_USER_DATA not specified (that I can find), pinentry-mac
# assumes comma-separated sequence of NAME=VALUE with no escaping mechanism
# https://github.com/GPGTools/pinentry-mac/blob/v0.9.4/Source/AppDelegate.m#L78
# and recognizes USE_CURSES=1 for curses fallback
# https://github.com/GPGTools/pinentry-mac/pull/2
#
# To the extent possible under law, Kevin Locke <[email protected]> has
# waived all copyright and related or neighboring rights to this work
# under the terms of CC0: https://creativecommons.org/publicdomain/zero/1.0/
set -Ceu
# Use pinentry-tty if $PINENTRY_USER_DATA contains USE_TTY=1
case "${PINENTRY_USER_DATA-}" in
*USE_TTY=1*)
# Note: Change to pinentry-curses if a Curses UI is preferred.
exec pinentry-tty "$@"
;;
esac
# Otherwise, use any X11 UI (configured by Debian Alternatives System)
# Note: Will fall back to curses if $DISPLAY is not available.
exec pinentry-x11 "$@"
@Kabouik
Copy link

Kabouik commented Aug 6, 2025

I always read that environment variables should not be set in rc files but rahter in ~/.bash_profile or ~/.profile. Can we safely apply your method but just use one of these instead of ~/.bashrc?

I'm looking into favouring pinentry-curses whenever possible (when the request comes from a terminal), else use Emacs if the request comes from Emacs, and else, ultimately, use GUI.

@kevinoid
Copy link
Author

kevinoid commented Aug 6, 2025

@Kabouik If you set the environment variables in ~/.bash_profile or ~/.profile they will only be set for login shells (e.g. the shell run when you log in on a terminal or via ssh) and not for non-login shells (e.g. the shell in a graphical terminal emulator inside a Wayland or X session), as documented in Bash Startup Files section of the GNU Bash Manual. Note that variables in ~/.bashrc usually apply to both login and non-login shells since ~/.bash_profile (or ~/.profile) sources ~/.bashrc on many systems.

I'd recommend experimenting as a good way to learn and test. Try setting the environment variables in ~/.bash_profile then running echo $PINENTRY_USER_DATA in both login and non-login shells to see whether the variable is present everywhere you want it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment