pashage

Documentation
Login

Casual Maintenance Intended

pashage

Yet Another Opinionated Re-engineering of the Unix Password Store

Core objectives:

Portability is not a core objective, but a nice side-effect of using basic POSIX shell, and it is embraced when possible.

Security is not branded as a core objective, because the author does not have the clout to declare anything secure, and you should probably not trust random READMEs anyway. However the simplicity should help you assess whether this password store is a worthwhile trade-off for your threat model.

For the reference, the author has views similar to those of Filippo Valsorda and considers the password store shell script to be about as critical as the rest of her computer, and relies mostly on age to provide secure encryption at rest and on a YubiKey to gatekeep decryption.

Licencing

This project was written from scratch, and every character of the script was typed with my fingers. However I looked deeply into pass, passage, and pash code bases. I don't know whether that's enough to make it a derivative work covered by the GPL, so to be on the safe side I'm using GPL v2+ too.

Differences with pass

Behavior Differences

New Features and Extensions

Roadmap

The following features are currently under consideration:

Manual

pashage is a password manager, which means it manages a database of encrypted secrets, including encrypting externally-provided new secrets, generating and encrypting random strings, and decrypting and displaying stored secrets.

It aims to be simple and composable, but its reliance on Unix philosophy and customs might make steep learning curve for users outside of this culture.

It is used through a shell command, denoted as pashage in this document, immediately followed by a subcommand and its arguments. When no subcommand is specified, list or show is implicitly assumed.

The database is optionally versioned using git to help with history audit and synchronization. It should be noted that this prevents re-encryption from erasing old cyphertext, leaving the secret vulnerable to compromised encryption keys.

The cryptography is done by age external command. It decrypts using the identity file given in the environment, and crypts using a list of recipients per subfolder, defaulting to the parent recipient list or the identity.

Command Reference

Here is an alphabetical list of all subcommands and aliases:

copy

Syntax:

pashage copy [--reencrypt,-e | --interactive,-i | --keep,-k ]
             [--force,-f] old-path ... new-path

This subcommand copies secrets and recursively copies subfolders, using the same positional argument scheme as cp(1). By default it asks before overwriting an existing secret and it re-encrypts the secret when the destination has a different recipient list.

Flags: - -e or --reencrypt: always re-encrypt secrets - -f or --force: overwrite existing secrets without asking - -i or --interactive: asks whether to re-encrypt or not for each secret - -k or --keep: never re-encrypt secrets

Environment: - PASHAGE_AGE: external command to use instead of age - PASHAGE_DIR: database directory to use instead of ~/.passage/store - PASHAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities - PASSAGE_AGE: external command to use instead of age when PASHAGE_AGE is unset - PASSAGE_DIR: database directory to use instead of ~/.passage/store when PASHAGE_DIR is unset - PASSAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities when PASHAGE_IDENTITIES_FILE is unset - PASSWORD_STORE_DIR: database directory to use instead of ~/.passage/store when both PASHAGE_DIR and PASSAGE_DIR are unset

delete

Syntax:

pashage delete [--recursive,-r] [--force,-f] pass-name ...

This subcommand deletes secrets from the database. By default it skips subfolders and asks for confirmation for each secret.

Flags: - -f or --force: delete without asking for confirmation - -r or --recursive: recursively delete all secrets in given subfolders

Environment: - PASHAGE_DIR: database directory to use instead of ~/.passage/store - PASSAGE_DIR: database directory to use instead of ~/.passage/store when PASHAGE_DIR is unset - PASSWORD_STORE_DIR: database directory to use instead of ~/.passage/store when both PASHAGE_DIR and PASSAGE_DIR are unset

edit

Syntax:

pashage edit pass-name ...

This subcommand starts an interactive editor to update the secrets.

Environment: - EDITOR: editor command to use instead of vi when VISUAL is not set - PASHAGE_AGE: external command to use instead of age - PASHAGE_DIR: database directory to use instead of ~/.passage/store - PASHAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities - PASSAGE_AGE: external command to use instead of age when PASHAGE_AGE is unset - PASSAGE_DIR: database directory to use instead of ~/.passage/store when PASHAGE_DIR is unset - PASSAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities when PASHAGE_IDENTITIES_FILE is unset - PASSWORD_STORE_DIR: database directory to use instead of ~/.passage/store when both PASHAGE_DIR and PASSAGE_DIR are unset - TMPDIR: temporary directory for the decrypted file to use instead of /tmp when /dev/shm is not available - VISUAL: editor command to use instead of vi

find

Syntax:

pashage find [GREP_OPTIONS] regex

This subcommand lists as a tree the secrets whose name match the given regular expression, using the corresponding grep(1) options.

Environment: - CLICOLOR: when set to a non-empty value, use ANSI escape sequences to color the output - LC_CTYPE: when it contains UTF, the tree is displayed using Unicode graphic characters instead of ASCII - PASHAGE_DIR: database directory to use instead of ~/.passage/store - PASSAGE_DIR: database directory to use instead of ~/.passage/store when PASHAGE_DIR is unset - PASSWORD_STORE_DIR: database directory to use instead of ~/.passage/store when both PASHAGE_DIR and PASSAGE_DIR are unset

generate

pashage generate [--no-symbols,-n] [--clip,-c | --qrcode,-q]
                 [--in-place,-i | --force,-f] [--multiline,-m]
                 [--try,-t] pass-name [pass-length [character-set]]

This subcommand generates a new secret from /dev/urandom, stores it in the database, and by default displays it on the standard output and asks for confirmation before overwriting an existing secret.

Flags: - -c or --clip: paste the secret into the clipboard instead of using the standard output - -f or --force: replace existing secrets without asking - -i or --in-place: when the secret already exists, replace only its first line and re-use the following lines - -m or --multiline: read lines from standard input append after the generated data into the secret file - -n or --no-symbols: generate a secret using only alphanumeric characters - -q or --qrcode: display the secret as a QR-code instead of using the standard output - -t or --try: display the secret and ask for confirmation before storing it into the database

Environment: - CLICOLOR: when set to a non-empty value, use ANSI escape sequences to color the output - PASHAGE_AGE: external command to use instead of age - PASHAGE_DIR: database directory to use instead of ~/.passage/store - PASHAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities - PASSAGE_AGE: external command to use instead of age when PASHAGE_AGE is unset - PASSAGE_DIR: database directory to use instead of ~/.passage/store when PASHAGE_DIR is unset - PASSAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities when PASHAGE_IDENTITIES_FILE is unset - PASSWORD_STORE_CHARACTER_SET_NO_SYMBOLS: default character set to use with tr(1) when -n is specified, instead of [:alnum:] - PASSWORD_STORE_CHARACTER_SET: default character set to use with tr(1) when -n is not specified, instead of [:punct:][:alnum:] - PASSWORD_STORE_CLIP_TIME: number of second before clearing the clipboard when -c is used, instead of 45 - PASSWORD_STORE_DIR: database directory to use instead of ~/.passage/store when both PASHAGE_DIR and PASSAGE_DIR are unset - PASSWORD_STORE_GENERATED_LENGTH: number of characters in the generated secret when not explicitly given, instead of 25 - PASSWORD_STORE_X_SELECTION: selection to use when -c and xclip are used, instead of clipboard

git

Syntax:

pashage git git-command-args ...

This subcommand invokes git in the database repository. Only git init and git clone are accepted when there is no underlying repository.

Environment: - PASHAGE_DIR: database directory to use instead of ~/.passage/store - PASSAGE_DIR: database directory to use instead of ~/.passage/store when PASHAGE_DIR is unset - PASSWORD_STORE_DIR: database directory to use instead of ~/.passage/store when both PASHAGE_DIR and PASSAGE_DIR are unset

gitconfig

Syntax:

pashage gitconfig

This subcommand configures the underlying repository to automatically decrypt secrets to display differences.

Environment: - PASHAGE_DIR: database directory to use instead of ~/.passage/store - PASSAGE_DIR: database directory to use instead of ~/.passage/store when PASHAGE_DIR is unset - PASSWORD_STORE_DIR: database directory to use instead of ~/.passage/store when both PASHAGE_DIR and PASSAGE_DIR are unset

grep

Syntax:

pashage grep [GREP_OPTIONS] search-regex

This subcommand successively decrypts all the secrets in the store and filter them through grep(1) using the given options, and outputs all the matching lines and the corresponding secret.

Environment: - CLICOLOR: when set to a non-empty value, use ANSI escape sequences to color the output - PASHAGE_AGE: external command to use instead of age - PASHAGE_DIR: database directory to use instead of ~/.passage/store - PASHAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities - PASSAGE_AGE: external command to use instead of age when PASHAGE_AGE is unset - PASSAGE_DIR: database directory to use instead of ~/.passage/store when PASHAGE_DIR is unset - PASSAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities when PASHAGE_IDENTITIES_FILE is unset - PASSWORD_STORE_DIR: database directory to use instead of ~/.passage/store when both PASHAGE_DIR and PASSAGE_DIR are unset

help

Syntax:

pashage help

This subcommand displays on the standard output the version and help text, including all subcommands and flags and a brief description.

This subcommand is not affected by the environment.

init

Syntax:

pashage init [--interactive,-i | --keep,-k ]
             [--path=subfolder,-p subfolder] age-recipient ...

This subcommand initializes an age recipient list, by default of the root of the password store, and re-encrypts all the affected secrets. When the recipient list is a single empty string, the recipient list is instead removed, falling back to a parent recipient list or ultimately to the age identity.

Flags: - -i or --interactive: ask for each secret whether to re-encrypt it or not - -k or --keep: do not re-encrypt any secret - -p or --path: operate on the recipient list in the given subfolder instead of the root of the password store

Environment: - PASHAGE_AGE: external command to use instead of age - PASHAGE_DIR: database directory to use instead of ~/.passage/store - PASHAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities - PASSAGE_AGE: external command to use instead of age when PASHAGE_AGE is unset - PASSAGE_DIR: database directory to use instead of ~/.passage/store when PASHAGE_DIR is unset - PASSAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities when PASHAGE_IDENTITIES_FILE is unset - PASSWORD_STORE_DIR: database directory to use instead of ~/.passage/store when both PASHAGE_DIR and PASSAGE_DIR are unset

insert

Syntax:

pashage insert [--echo,-e | --multiline,-m] [--force,-f] pass-name ...

This subcommand adds new secrets in the database, using the provided data from the standard input. By default asks before overwriting an existing secret, and it reads a single secret line after turning off the console echo, and reads it a second time for confirmation.

Flags: - -e or --echo: read a single line once without manipulating the standard input - -m or --multiline: an arbitrary amount of lines from the standard input, without trying to manipulate the console, until the end of input or a blank line is entered - -f or --force: overwrite an existing secret without asking

Environment: - PASHAGE_AGE: external command to use instead of age - PASHAGE_DIR: database directory to use instead of ~/.passage/store - PASHAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities - PASSAGE_AGE: external command to use instead of age when PASHAGE_AGE is unset - PASSAGE_DIR: database directory to use instead of ~/.passage/store when PASHAGE_DIR is unset - PASSAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities when PASHAGE_IDENTITIES_FILE is unset - PASSWORD_STORE_DIR: database directory to use instead of ~/.passage/store when both PASHAGE_DIR and PASSAGE_DIR are unset

list

Syntax:

pashage [list] [subfolder ...]

This subcommand displays the given subfolders as a tree, or the whole store when no subfolder is specified.

Note that when a secret is given instead of a subfolder, the show command will be used instead, without any warning or error.

Environment: - CLICOLOR: when set to a non-empty value, use ANSI escape sequences to color the output - LC_CTYPE: when it contains UTF, the tree is displayed using Unicode graphic characters instead of ASCII - PASHAGE_DIR: database directory to use instead of ~/.passage/store - PASSAGE_DIR: database directory to use instead of ~/.passage/store when PASHAGE_DIR is unset - PASSWORD_STORE_DIR: database directory to use instead of ~/.passage/store when both PASHAGE_DIR and PASSAGE_DIR are unset

move

Syntax:

pashage move [--reencrypt,-e | --interactive,-i | --keep,-k ]
             [--force,-f] old-path ... new-path

This subcommand moves or renames secrets and subfolders recursively, using the same positional argument scheme as mv(1). By default it asks before overwriting an existing secret and it re-encrypts the secret when the destination has a different recipient list.

Flags: - -e or --reencrypt: always re-encrypt secrets - -f or --force: overwrite existing secrets without asking - -i or --interactive: asks whether to re-encrypt or not for each secret - -k or --keep: never re-encrypt secrets

Environment: - PASHAGE_AGE: external command to use instead of age - PASHAGE_DIR: database directory to use instead of ~/.passage/store - PASHAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities - PASSAGE_AGE: external command to use instead of age when PASHAGE_AGE is unset - PASSAGE_DIR: database directory to use instead of ~/.passage/store when PASHAGE_DIR is unset - PASSAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities when PASHAGE_IDENTITIES_FILE is unset - PASSWORD_STORE_DIR: database directory to use instead of ~/.passage/store when both PASHAGE_DIR and PASSAGE_DIR are unset

random

Syntax:

pashage random [pass-length [character-set]]

This subcommand generates a new secret, like the generate subcommand, then directly displays on the standard output without storing it.

Environment: - PASSWORD_STORE_CHARACTER_SET: character set to use with tr(1) when character-set is not specified, instead of [:punct:][:alnum:] - PASSWORD_STORE_GENERATED_LENGTH: number of characters in the generated secret when not explicitly given, instead of 25

reencrypt

Syntax:

pashage reencrypt [--interactive,-i] pass-name|subfolder ...

This subcommand re-encrypts in place the given secrets, and all the secrets recursively in the given subfolders.

Flags: - -i or --interactive: asks whether to re-encrypt or not for each secret

Environment: - PASHAGE_AGE: external command to use instead of age - PASHAGE_DIR: database directory to use instead of ~/.passage/store - PASHAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities - PASSAGE_AGE: external command to use instead of age when PASHAGE_AGE is unset - PASSAGE_DIR: database directory to use instead of ~/.passage/store when PASHAGE_DIR is unset - PASSAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities when PASHAGE_IDENTITIES_FILE is unset - PASSWORD_STORE_DIR: database directory to use instead of ~/.passage/store when both PASHAGE_DIR and PASSAGE_DIR are unset

show

Syntax:

pashage [show] [--clip[=line-number],-c[line-number] |
                --qrcode[=line-number],-q[line-number]] pass-name ...

This subcommand decrypts the given secrets and by default displays the whole text on the standard output.

Note that when a subfolder is given instead of a secret, the list command will be used instead, without any warning or error.

Flags: - -c or --clip: paste the given line (by default the first line) of the secret into the clipboard instead of using the standard output - -q or --qrcode: display the given line (by default the first line) of the secret as a QR-code instead of using the standard output

Environment: - PASHAGE_AGE: external command to use instead of age - PASHAGE_DIR: database directory to use instead of ~/.passage/store - PASHAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities - PASSAGE_AGE: external command to use instead of age when PASHAGE_AGE is unset - PASSAGE_DIR: database directory to use instead of ~/.passage/store when PASHAGE_DIR is unset - PASSAGE_IDENTITIES_FILE: identity file to use instead of ~/.passage/identities when PASHAGE_IDENTITIES_FILE is unset - PASSWORD_STORE_DIR: database directory to use instead of ~/.passage/store when both PASHAGE_DIR and PASSAGE_DIR are unset

version

Syntax:

pashage version

This subcommand displays on the standard output the version and author list.

This subcommand is not affected by the environment.