CLI tool
drduh/Purse avatar
drduh/Purse

Purse: a Bash secret manager that opens GnuPG files with a YubiKey

Manage secrets with Bash and GnuPG asymmetric encryption (YubiKey)

615 stars43 forksShellMIT

At a glance

What is it?
Purse is a single Bash script that keeps secrets in GnuPG encrypted text files and replaces the passphrase with a YubiKey touch. It stores revisions by epoch timestamp, keeps an index of secrets, and hands passwords to the clipboard or the screen depending on environment variables you set.
Who is it for?
Purse suits a single operator on a Unix workstation who already has a GnuPG identity on a YubiKey and wants a password store that is a script rather than a daemon, especially if you like the idea of revisioned secrets and offline archives. It does not suit shared or team use, since the README documents one identity and one safe directory per user, and it does not suit anyone without a YubiKey, because the passphrase-free flow is the point of the project.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One script, one GnuPG identity, one YubiKey touch

Purse is a Bash shell script and nothing else. The repository root holds LICENSE.md, README.md and purse.sh, so there is no daemon, no database and no service to install. Its stated purpose is managing secrets with Bash and GnuPG asymmetric encryption, and it is built on the earlier pwd.sh project by the same author, with the difference drawn explicitly in the README: pwd.sh uses symmetric, passphrase based authentication, while Purse uses asymmetric public key authentication. The practical effect is that you do not type a passphrase to read a secret. You plug in the YubiKey, enter its PIN and touch it, and the secret opens. The important prerequisite is stated in an important callout in the README: a GnuPG identity is required, and the author points to a separate YubiKey guide for setting one up. Everything else in the tool assumes that identity exists, so the setup cost of a hardware token is part of the cost of this project rather than an optional extra.

Five commands cover the whole workflow

The script is run interactively with ./purse.sh, or symlinked into a directory on PATH so it can be called from anywhere. The README lists the single letter commands available in that interactive mode: w to create a secret, r to access a secret, l to list all secrets, b to create a backup archive, and h to print the help text. The same options can be passed on the command line, and the documented examples show that difference. Creating a 20 character password for a user named userName is:

bash
./purse.sh w userName 20

and reading it back is:

bash
./purse.sh r userName

The two forms matter if you plan to script around the tool, because the interactive mode is what the keybindings are for. The letter shortcuts are a terminal habit borrowed from password managers with a TUI, and they are what makes the script pleasant to use several times a day rather than something you invoke by remembering subcommand names.

Secrets are versioned by epoch timestamp

Passwords are not overwritten in place. Each stored password carries an epoch timestamp, which gives the secret a revision history and makes older versions addressable. The most recent version is copied to the clipboard when you read it, and to reach an older one you append the timestamp to the name, using the at sign as the separator. Listing everything is a single call:

bash
./purse.sh l

and a specific historical revision looks like this:

bash
./purse.sh r userName@1574723600

That design has an obvious operational consequence. If a credential is rotated because it leaked, the old value is still present in the store and still retrievable by anyone who knows the timestamp, so the revision history is a feature for legitimate recovery and a liability if the leak is the reason for rotating. The README does not describe any deletion or purge command, and the only command that removes data is the manual removal of files, so treat the store as append only and plan how you would ever get rid of a compromised revision.

Environment variables decide length, clipboard and backup behaviour

The configuration surface is a table of environment variables, and it is wide enough to change how the tool behaves in ways that matter for security. PURSE_LEN sets the default generated password length and is 14 by default. PURSE_CHARS sets the character set, with a default of [:alnum:]!?@#$%^&*();:+=. PURSE_DEST chooses where a password goes, clipboard by default or screen, which sets it to screen without a clipboard. PURSE_TIME sets the seconds before the password is cleared from the clipboard or screen, 10 by default. PURSE_CLIP selects the clipboard command, xclip by default, with pbcopy for macOS, and PURSE_CLIP_ARGS passes extra arguments such as using the primary selection. PURSE_COPY copies the password to the clipboard before writing, PURSE_DAILY creates a daily backup archive on write, PURSE_ECHO sets the character echoed during input, and PURSE_SAFE and PURSE_INDEX name the safe directory and the index file. The README suggests setting them in a shell rc file, which is the practical way to use them.

The index is metadata, and PURSE_ENCIX closes it

Purse keeps an index of the secrets it holds, and by default that index is unencrypted, which leaks the names of your accounts to anyone who can read the file. Two settings control that. PURSE_COMMENT adds an unencrypted comment to the index and the safe files, so anything you put there is visible in clear text. PURSE_ENCIX encrypts the index for additional privacy, and the cost is stated in the README: two YubiKey touches are required, one to work with the safe and another to decrypt the index for separate operations. That is the trade-off in one line. Without it, listing and reading are single touch operations against an index anyone can read. With it, the index stops being readable metadata but every session costs an extra touch. Separately, the README notes a privacy measure that is on by default: the recipient key ID is not included in metadata, using the GnuPG throw-keyids option.

Backups are tar archives you restore by hand

Backups are created with a single command:

bash
./purse.sh b

The archive name follows the pattern purse.$hostname.$today.tar by default, which PURSE_BACKUP can override, and the daily variant is opt-in through PURSE_DAILY, so a backup is written on every write when you enable it. Restoring is deliberately a plain tar operation:

bash
tar xvf purse*tar

There is no restore command inside the tool, which is worth noting for two reasons. The archive is a normal tarball, so it can be inspected with ordinary tools and moved to other storage, and restoring it overwrites whatever is in the current directory, so it needs to be unpacked somewhere deliberate rather than in your home directory. The GnuPG options recommended for the tool are published separately in the referenced config/gpg.conf, and the project documentation site is at drduh.github.io/Purse.

A 2024 release, an October 2026 push, and an install line worth reading twice

The project is MIT licensed and hosted on GitHub with a documentation site. Release tags are sparse: Version 3 was published on 2024-07-05, preceded by Version 3 Beta on 2024-03-10 and Version 2 Beta 1 in 2020, so the tagged history is old even though the last push was on 2026-10-20. That gap tells you the script is being worked on without a steady tagging habit, so if you depend on it, pin to a commit rather than assume a release exists for what you want. The install instructions mention two routes, the Releases page and a direct download from GitHub, and the direct line is worth reading carefully:

bash
wget https://github.com/drduh/Purse/blob/master/purse.sh

That address points at the blob view on GitHub rather than a raw file endpoint, so a careful reader will want to check what that actually saves before running it, and prefer the release artefact or a raw download.

Editorial conclusion

Purse suits a single operator on a Unix workstation who already has a GnuPG identity on a YubiKey and wants a password store that is a script rather than a daemon, especially if you like the idea of revisioned secrets and offline archives. It does not suit shared or team use, since the README documents one identity and one safe directory per user, and it does not suit anyone without a YubiKey, because the passphrase-free flow is the point of the project. Verify first that GnuPG is configured with a YubiKey backed identity, read the recommended options in the referenced gpg.conf, and decide about PURSE_ENCIX before you store anything, because by default the index file is readable metadata about which secrets exist.

Frequently asked questions

What is Purse and how does it differ from pwd.sh?

Purse is a Bash script that manages secrets in GnuPG encrypted text files using asymmetric public key authentication with a YubiKey, while pwd.sh, which it is based on, uses symmetric passphrase based authentication. The README says Purse removes the need for a passphrase.

What do I need before I can use Purse?

A GnuPG identity is required, and the README points to the YubiKey guide by the same author for setting one up. The recommended GnuPG options are published separately in the referenced gpg.conf.

How do I create and read a password with Purse?

Run ./purse.sh w userName 20 to generate a 20 character password for that entry, and ./purse.sh r userName to read it back, in which case the most recent version is copied to the clipboard. In interactive mode the shortcuts are w, r, l, b and h.

Can Purse keep older versions of a password?

Yes. Passwords are stored with an epoch timestamp for revision control, and a specific older revision is read by appending the timestamp to the name, for example ./purse.sh r userName@1574723600. The README does not describe a command that deletes a revision.

How does Purse handle the index file and backups?

The index is unencrypted by default and can be encrypted with PURSE_ENCIX, which requires two YubiKey touches. Backups are tar archives named purse.$hostname.$today.tar by default, created with ./purse.sh b or daily when PURSE_DAILY is enabled, and restored with tar xvf purse*tar.

Official sources

  1. drduh/Purse on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/drduh-purse.svg)](https://hysenlabs.com/projects/drduh-purse)

Was this useful?