pashage

Yet Another Opinionated Re-engineering of the Unix Password Store
git clone https://git.instinctive.eu/pashage.git
Log | Files | Refs | README | LICENSE

README.md (24785B)


      1 [![Casual Maintenance Intended](https://casuallymaintained.tech/badge.svg)](https://casuallymaintained.tech/)
      2 
      3 # pashage
      4 
      5 Yet Another Opinionated Re-engineering of the Unix Password Store
      6 
      7 Core objectives:
      8 
      9 - same interface and similar feature set
     10   as [pass](https://www.passwordstore.org/)
     11 - simplicity, understandability, and hackability, from using POSIX shell,
     12   like [pash](https://github.com/dylanaraps/pash)
     13 - [age](https://age-encryption.org) as encryption backend,
     14   like [passage](https://github.com/FiloSottile/passage)
     15 - validation using [shellcheck](https://www.shellcheck.net/)
     16   and [shellspec tests](https://shellspec.info/)
     17 
     18 Portability is not a core objective, but a nice side-effect of using
     19 basic POSIX shell, and it is embraced when possible.
     20 
     21 Security is not branded as a core objective, because the author does not
     22 have the clout to declare anything secure, and you should probably not
     23 trust random READMEs anyway.
     24 However the simplicity should help you assess whether this password store
     25 is a worthwhile trade-off for _your_ threat model.
     26 
     27 For the reference, the author has views [similar to those of Filippo
     28 Valsorda](https://words.filippo.io/dispatches/passage/) and considers
     29 the password store shell script to be about as critical as the rest
     30 of her computer, and relies mostly on age to provide secure encryption
     31 at rest and on a [YubiKey](https://www.yubico.com/) to gatekeep decryption.
     32 
     33 ## Licencing
     34 
     35 This project was written from scratch, and every character of the script
     36 was typed with my fingers.
     37 However I looked deeply into pass, passage, and pash code bases.
     38 I don't know whether that's enough to make it a derivative work covered
     39 by the GPL, so to be on the safe side I'm using GPL v2+ too.
     40 
     41 ## Differences with `pass`
     42 
     43 ### Behavior Differences
     44 
     45 - Not using a terminal does not imply `--force`, instead `pashage` asks for
     46 a confirming `y` on a standard input line.
     47 
     48 - When copying a secret to the clipboard, the script keeps running while
     49 waiting for the automatic clearing. This provides a user-facing cue that
     50 the secret may still be the clipboard and allows to clear the clipboard
     51 earlier.
     52 
     53 - The commands `copy`, `edit`, `insert`, `list`, `move`, and `show`
     54 accept multiple arguments to operate on many secrets at once.
     55 
     56 - The commands `copy` and `move` also operate on unencrypted files in the
     57 password store.
     58 
     59 - The `edit` command does not warn a about using `/tmp` rather than
     60 `/dev/shm`, because the warning does not seem actionable and quickly
     61 becomes ignored noise.
     62 
     63 - The `edit` command uses `$VISUAL` rather than `$EDITOR` when it set and
     64 the terminal is not dumb.
     65 
     66 - The `find` command search-pattern is a regular expression rather than
     67 a glob.
     68 
     69 - The `find` command matches against the secret names without
     70 extension (`.age` or `.gpg`).
     71 
     72 - The `init` command is redesigned to accommodate `age` backend.
     73 I didn't really understand the original `init` command, so I'm not sure
     74 how different it is; but now it installs `.age-recipients` and re-encrypts.
     75 
     76 - The `insert` command makes the user try again when entering mismatching
     77 passwords.
     78 
     79 ### New Features and Extensions
     80 
     81 - The commands `copy` and `move` have new flags to control re-encryption
     82 (always, never, ask for each file).
     83 
     84 - The `find` and `list` commands have a new flag to output a
     85 machine-readable list of entries (e.g. for completion or wrappers).
     86 
     87 - The `generate` command has a new command-line argument to specify
     88 explicitly the character set.
     89 
     90 - The `generate` command optionally asks for confirmation before storing
     91 the generated secret (e.g. for iterative attempts against stupid password
     92 rules).
     93 
     94 - The `generate` command optionally asks for extra lines to append after
     95 the generated secret (e.g. for username, login page, or others comments).
     96 
     97 - The `init` command has new flags to control re-encryption (never or
     98 ask for each file).
     99 
    100 - The new `gitconfig` command configures an existing store repository to
    101 decrypt before `diff`.
    102 
    103 - The new `random` command leverages password generation without touching
    104 the password store.
    105 
    106 - The new `reencrypt` command re-encrypts secrets in-place.
    107 
    108 ## Roadmap
    109 
    110 The following features are currently under consideration:
    111 
    112 - v1.1.0:
    113     + partial display of secrets on standard output
    114     + successive clipboard copy of several lines from a single decryption
    115 (e.g. username then password)
    116 - maybe/later:
    117     + rewriting of git history to purge old cyphertexts
    118     + OTP support
    119     + extension support
    120 
    121 ## Dependencies
    122 
    123 Required dependencies:
    124 
    125 - POSIX-compliant shell, including POSIX-compliant utilities `[`, `basename`,
    126 `cat`, `cp`, `dd`, `diff`, `dirname`, `echo`, `find`, `grep`, `head`, `mkdir`,
    127 `mktemp`, `mv`, `printf`, `rm`, `stty`, `tail`, `tr`, and `true`;
    128 - [age](https://age-encryption.org/) encryption tool.
    129 
    130 Optional dependencies:
    131 
    132 - [feh](https://feh.finalrewind.org/)
    133 to display QR-codes graphically
    134 - [git](https://git-scm.com/)
    135 to track storage revision and replicate it
    136 - [GnuPG](https://gnupg.org/)
    137 to display GnuPG-encrypted password-store secrets
    138 - [GraphicsMagick](http://www.graphicsmagick.org/)
    139 to display QR-codes graphically
    140 - [ImageMagick](https://imagemagick.org/)
    141 to display QR-codes graphically
    142 - [openssl](https://www.openssl.org/)
    143 to encode and decode base-64 when interacting with clipboard
    144 - [qrencode](https://github.com/fukuchi/libqrencode)
    145 to generate QR-codes
    146 - [wl-clipboard](https://github.com/bugaevc/wl-clipboard)
    147 to copy and paste on Wayland
    148 - [xclip](https://github.com/astrand/xclip)
    149 to copy and paste on X11
    150 
    151 ## Manual
    152 
    153 **pashage** is a _password manager_, which means it manages a database of
    154 encrypted secrets, including encrypting externally-provided new secrets,
    155 generating and encrypting random strings, and decrypting and displaying
    156 stored secrets.
    157 
    158 It aims to be simple and composable, but its reliance on Unix philosophy
    159 and customs might make steep learning curve for users outside of this
    160 culture.
    161 
    162 It is used through a shell command, denoted as `pashage` in this document,
    163 immediately followed by a subcommand and its arguments. When no subcommand
    164 is specified, _list_ or _show_ is implicitly assumed.
    165 
    166 The database is optionally versioned using [git](https://git-scm.com/)
    167 to help with history audit and synchronization. It should be noted that
    168 this prevents re-encryption from erasing old cyphertext, leaving the secret
    169 vulnerable to compromised encryption keys.
    170 
    171 The cryptography is done by [age](https://age-encryption.org/) external
    172 command. It decrypts using the _identity_ file given in the environment,
    173 and crypts using a list of _recipients_ per subfolder, defaulting to the
    174 parent _recipient_ list or the _identity_.
    175 
    176 ## Command Reference
    177 
    178 Here is an alphabetical list of all subcommands and aliases:
    179 
    180 - `--help`: alias for _help_
    181 - `--version`: alias for _version_
    182 - `-h`: alias for _help_
    183 - `copy`
    184 - `cp`: alias for _copy_
    185 - `delete`
    186 - `edit`
    187 - `find`
    188 - `gen`: alias for _generate_
    189 - `generate`
    190 - `git`
    191 - `gitconfig`
    192 - `grep`
    193 - `help`
    194 - `init`
    195 - `insert`
    196 - `list`
    197 - `ls`: alias for _list_
    198 - `move`
    199 - `mv`: alias for _move_
    200 - `random`
    201 - `re-encrypt`: alias for _reencrypt_
    202 - `reencrypt`
    203 - `remove`: alias for _delete_
    204 - `rm`: alias for _delete_
    205 - `show`
    206 - `version`
    207 
    208 ### copy
    209 
    210 Syntax:
    211 
    212 ```
    213 pashage copy [--reencrypt,-e | --interactive,-i | --keep,-k ]
    214              [--force,-f] old-path ... new-path
    215 ```
    216 
    217 This subcommand copies secrets and recursively copies subfolders,
    218 using the same positional argument scheme as `cp(1)`.
    219 By default it asks before overwriting an existing secret and it re-encrypts
    220 the secret when the destination has a different _recipient_ list.
    221 
    222 Flags:
    223 - `-e` or `--reencrypt`: always re-encrypt secrets
    224 - `-f` or `--force`: overwrite existing secrets without asking
    225 - `-i` or `--interactive`: asks whether to re-encrypt or not for each secret
    226 - `-k` or `--keep`: never re-encrypt secrets
    227 
    228 Environment:
    229 - `PASHAGE_AGE`: external command to use instead of `age`
    230 - `PASHAGE_DIR`: database directory to use instead of `~/.passage/store`
    231 - `PASHAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    232   `~/.passage/identities`
    233 - `PASSAGE_AGE`: external command to use instead of `age` when
    234   `PASHAGE_AGE` is unset
    235 - `PASSAGE_DIR`: database directory to use instead of `~/.passage/store`
    236   when `PASHAGE_DIR` is unset
    237 - `PASSAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    238   `~/.passage/identities` when `PASHAGE_IDENTITIES_FILE` is unset
    239 - `PASSWORD_STORE_DIR`: database directory to use instead of
    240   `~/.passage/store` when both `PASHAGE_DIR` and `PASSAGE_DIR` are unset
    241 
    242 ### delete
    243 
    244 Syntax:
    245 
    246 ```
    247 pashage delete [--recursive,-r] [--force,-f] pass-name ...
    248 ```
    249 
    250 This subcommand deletes secrets from the database. By default it skips
    251 subfolders and asks for confirmation for each secret.
    252 
    253 Flags:
    254 - `-f` or `--force`: delete without asking for confirmation
    255 - `-r` or `--recursive`: recursively delete all secrets in given subfolders
    256 
    257 Environment:
    258 - `PASHAGE_DIR`: database directory to use instead of `~/.passage/store`
    259 - `PASSAGE_DIR`: database directory to use instead of `~/.passage/store`
    260   when `PASHAGE_DIR` is unset
    261 - `PASSWORD_STORE_DIR`: database directory to use instead of
    262   `~/.passage/store` when both `PASHAGE_DIR` and `PASSAGE_DIR` are unset
    263 
    264 ### edit
    265 
    266 Syntax:
    267 
    268 ```
    269 pashage edit pass-name ...
    270 ```
    271 
    272 This subcommand starts an interactive editor to update the secrets.
    273 
    274 Environment:
    275 - `EDITOR`: editor command to use instead of `vi` when `VISUAL` is not set
    276 - `PASHAGE_AGE`: external command to use instead of `age`
    277 - `PASHAGE_DIR`: database directory to use instead of `~/.passage/store`
    278 - `PASHAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    279   `~/.passage/identities`
    280 - `PASSAGE_AGE`: external command to use instead of `age` when
    281   `PASHAGE_AGE` is unset
    282 - `PASSAGE_DIR`: database directory to use instead of `~/.passage/store`
    283   when `PASHAGE_DIR` is unset
    284 - `PASSAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    285   `~/.passage/identities` when `PASHAGE_IDENTITIES_FILE` is unset
    286 - `PASSWORD_STORE_DIR`: database directory to use instead of
    287   `~/.passage/store` when both `PASHAGE_DIR` and `PASSAGE_DIR` are unset
    288 - `TMPDIR`: temporary directory for the decrypted file to use instead of
    289   `/tmp` when `/dev/shm` is not available
    290 - `VISUAL`: editor command to use instead of `vi`
    291 
    292 ### find
    293 
    294 Syntax:
    295 
    296 ```
    297 pashage find [--raw,-r] [GREP_OPTIONS] regex
    298 ```
    299 
    300 This subcommand lists as a tree the secrets whose name match the given
    301 regular expression, using the corresponding `grep(1)` options.
    302 
    303 Flags:
    304 - `-r` or `--raw`: display the results as a raw list of secrets,
    305   rather than a tree
    306 
    307 Environment:
    308 - `CLICOLOR_FORCE`: when set to a non-empty value, and `NO_COLOR` is unset
    309   or empty, use ANSI escape sequences to color the output even when not
    310   writing to a terminal
    311 - `LC_CTYPE`: when it contains `UTF`, the tree is displayed using Unicode
    312   graphic characters instead of ASCII
    313 - `NO_COLOR`: when set to a non-empty value, no ANSI escape sequence is output
    314 - `PASHAGE_DIR`: database directory to use instead of `~/.passage/store`
    315 - `PASSAGE_DIR`: database directory to use instead of `~/.passage/store`
    316   when `PASHAGE_DIR` is unset
    317 - `PASSWORD_STORE_DIR`: database directory to use instead of
    318   `~/.passage/store` when both `PASHAGE_DIR` and `PASSAGE_DIR` are unset
    319 
    320 ### generate
    321 
    322 Syntax:
    323 
    324 ```
    325 pashage generate [--no-symbols,-n] [--clip,-c | --qrcode,-q]
    326                  [--in-place,-i | --force,-f] [--multiline,-m]
    327                  [--try,-t] pass-name [pass-length [character-set]]
    328 ```
    329 
    330 This subcommand generates a new secret from `/dev/urandom`, stores it in
    331 the database, and by default displays it on the standard output and asks
    332 for confirmation before overwriting an existing secret.
    333 
    334 Flags:
    335 - `-c` or `--clip`: paste the secret into the clipboard instead of using
    336   the standard output
    337 - `-f` or `--force`: replace existing secrets without asking
    338 - `-i` or `--in-place`: when the secret already exists, replace only its
    339   first line and re-use the following lines
    340 - `-m` or `--multiline`: read lines from standard input to append after the
    341   generated data into the secret file
    342 - `-n` or `--no-symbols`: generate a secret using only alphanumeric
    343   characters
    344 - `-q` or `--qrcode`: display the secret as a QR-code instead of using the
    345   standard output
    346 - `-t` or `--try`: display the secret and ask for confirmation before
    347   storing it into the database
    348 
    349 Environment:
    350 - `CLICOLOR_FORCE`: when set to a non-empty value, and `NO_COLOR` is unset
    351   or empty, use ANSI escape sequences to color the output even when not
    352   writing to a terminal
    353 - `NO_COLOR`: when set to a non-empty value, no ANSI escape sequence is output
    354 - `PASHAGE_AGE`: external command to use instead of `age`
    355 - `PASHAGE_DIR`: database directory to use instead of `~/.passage/store`
    356 - `PASHAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    357   `~/.passage/identities`
    358 - `PASSAGE_AGE`: external command to use instead of `age` when
    359   `PASHAGE_AGE` is unset
    360 - `PASSAGE_DIR`: database directory to use instead of `~/.passage/store`
    361   when `PASHAGE_DIR` is unset
    362 - `PASSAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    363   `~/.passage/identities` when `PASHAGE_IDENTITIES_FILE` is unset
    364 - `PASSWORD_STORE_CHARACTER_SET_NO_SYMBOLS`: default character set to use
    365   with `tr(1)` when `-n` is specified, instead of `[:alnum:]`
    366 - `PASSWORD_STORE_CHARACTER_SET`: default character set to use with `tr(1)`
    367   when `-n` is not specified, instead of `[:punct:][:alnum:]`
    368 - `PASSWORD_STORE_CLIP_TIME`: number of second before clearing the
    369   clipboard when `-c` is used, instead of 45
    370 - `PASSWORD_STORE_DIR`: database directory to use instead of
    371   `~/.passage/store` when both `PASHAGE_DIR` and `PASSAGE_DIR` are unset
    372 - `PASSWORD_STORE_GENERATED_LENGTH`: number of characters in the generated
    373   secret when not explicitly given, instead of 25
    374 - `PASSWORD_STORE_X_SELECTION`: selection to use when `-c` and `xclip` are
    375   used, instead of `clipboard`
    376 
    377 ### git
    378 
    379 Syntax:
    380 
    381 ```
    382 pashage git git-command-args ...
    383 ```
    384 
    385 This subcommand invokes `git` in the database repository.
    386 Only `git init` and `git clone` are accepted when there is no underlying
    387 repository.
    388 
    389 Environment:
    390 - `PASHAGE_DIR`: database directory to use instead of `~/.passage/store`
    391 - `PASSAGE_DIR`: database directory to use instead of `~/.passage/store`
    392   when `PASHAGE_DIR` is unset
    393 - `PASSWORD_STORE_DIR`: database directory to use instead of
    394   `~/.passage/store` when both `PASHAGE_DIR` and `PASSAGE_DIR` are unset
    395 
    396 ### gitconfig
    397 
    398 Syntax:
    399 
    400 ```
    401 pashage gitconfig
    402 ```
    403 
    404 This subcommand configures the underlying repository to automatically
    405 decrypt secrets to display differences.
    406 
    407 Environment:
    408 - `PASHAGE_DIR`: database directory to use instead of `~/.passage/store`
    409 - `PASSAGE_DIR`: database directory to use instead of `~/.passage/store`
    410   when `PASHAGE_DIR` is unset
    411 - `PASSWORD_STORE_DIR`: database directory to use instead of
    412   `~/.passage/store` when both `PASHAGE_DIR` and `PASSAGE_DIR` are unset
    413 
    414 ### grep
    415 
    416 Syntax:
    417 
    418 ```
    419 pashage grep [GREP_OPTIONS] search-regex
    420 ```
    421 
    422 This subcommand successively decrypts all the secrets in the store and
    423 filter them through `grep(1)` using the given options, and outputs all the
    424 matching lines and the corresponding secret.
    425 
    426 Environment:
    427 - `CLICOLOR_FORCE`: when set to a non-empty value, and `NO_COLOR` is unset
    428   or empty, use ANSI escape sequences to color the output even when not
    429   writing to a terminal
    430 - `NO_COLOR`: when set to a non-empty value, no ANSI escape sequence is output
    431 - `PASHAGE_AGE`: external command to use instead of `age`
    432 - `PASHAGE_DIR`: database directory to use instead of `~/.passage/store`
    433 - `PASHAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    434   `~/.passage/identities`
    435 - `PASSAGE_AGE`: external command to use instead of `age` when
    436   `PASHAGE_AGE` is unset
    437 - `PASSAGE_DIR`: database directory to use instead of `~/.passage/store`
    438   when `PASHAGE_DIR` is unset
    439 - `PASSAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    440   `~/.passage/identities` when `PASHAGE_IDENTITIES_FILE` is unset
    441 - `PASSWORD_STORE_DIR`: database directory to use instead of
    442   `~/.passage/store` when both `PASHAGE_DIR` and `PASSAGE_DIR` are unset
    443 
    444 ### help
    445 
    446 Syntax:
    447 
    448 ```
    449 pashage help [subcommand ...]
    450 ```
    451 
    452 This subcommand displays on the standard output the help text for the
    453 given subcommands.
    454 
    455 Without arguments, this subcommand displays on the standard output the
    456 version and help text, including all subcommands and flags and a brief
    457 description.
    458 
    459 This subcommand is not affected by the environment.
    460 
    461 ### init
    462 
    463 Syntax:
    464 
    465 ```
    466 pashage init [--interactive,-i | --keep,-k ]
    467              [--path=subfolder,-p subfolder] age-recipient ...
    468 ```
    469 
    470 This subcommand initializes an age _recipient_ list, by default of the root
    471 of the password store, and re-encrypts all the affected secrets.
    472 When the _recipient_ list is a single empty string, the _recipient_ list is
    473 instead removed, falling back to a parent _recipient_ list or ultimately to
    474 the age _identity_.
    475 
    476 Flags:
    477 - `-i` or `--interactive`: ask for each secret whether to re-encrypt it
    478   or not
    479 - `-k` or `--keep`: do not re-encrypt any secret
    480 - `-p` or `--path`: operate on the _recipient_ list in the given subfolder
    481   instead of the root of the password store
    482 
    483 Environment:
    484 - `PASHAGE_AGE`: external command to use instead of `age`
    485 - `PASHAGE_DIR`: database directory to use instead of `~/.passage/store`
    486 - `PASHAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    487   `~/.passage/identities`
    488 - `PASSAGE_AGE`: external command to use instead of `age` when
    489   `PASHAGE_AGE` is unset
    490 - `PASSAGE_DIR`: database directory to use instead of `~/.passage/store`
    491   when `PASHAGE_DIR` is unset
    492 - `PASSAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    493   `~/.passage/identities` when `PASHAGE_IDENTITIES_FILE` is unset
    494 - `PASSWORD_STORE_DIR`: database directory to use instead of
    495   `~/.passage/store` when both `PASHAGE_DIR` and `PASSAGE_DIR` are unset
    496 
    497 ### insert
    498 
    499 Syntax:
    500 
    501 ```
    502 pashage insert [--echo,-e | --multiline,-m] [--force,-f] pass-name ...
    503 ```
    504 
    505 This subcommand adds new secrets in the database, using the provided data
    506 from the standard input. By default asks before overwriting an existing
    507 secret, and it reads a single secret line after turning off the console
    508 echo, and reads it a second time for confirmation.
    509 
    510 Flags:
    511 - `-e` or `--echo`: read a single line once without manipulating the
    512   standard input
    513 - `-m` or `--multiline`: read an arbitrary amount of lines from the standard
    514   input, without trying to manipulate the console, until the end of input
    515   or a blank line is entered
    516 - `-f` or `--force`: overwrite an existing secret without asking
    517 
    518 Environment:
    519 - `PASHAGE_AGE`: external command to use instead of `age`
    520 - `PASHAGE_DIR`: database directory to use instead of `~/.passage/store`
    521 - `PASHAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    522   `~/.passage/identities`
    523 - `PASSAGE_AGE`: external command to use instead of `age` when
    524   `PASHAGE_AGE` is unset
    525 - `PASSAGE_DIR`: database directory to use instead of `~/.passage/store`
    526   when `PASHAGE_DIR` is unset
    527 - `PASSAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    528   `~/.passage/identities` when `PASHAGE_IDENTITIES_FILE` is unset
    529 - `PASSWORD_STORE_DIR`: database directory to use instead of
    530   `~/.passage/store` when both `PASHAGE_DIR` and `PASSAGE_DIR` are unset
    531 
    532 ### list
    533 
    534 Syntax:
    535 
    536 ```
    537 pashage [list] [--raw,-r] [subfolder ...]
    538 ```
    539 
    540 This subcommand displays the given subfolders as a tree or a raw list,
    541 or the whole store when no subfolder is specified.
    542 
    543 Note that when a secret is given instead of a subfolder, the _show_ command
    544 will be used instead, without any warning or error.
    545 
    546 Flags:
    547 - `-r` or `--raw`: display the results as a raw list of secrets
    548 
    549 Environment:
    550 - `CLICOLOR_FORCE`: when set to a non-empty value, and `NO_COLOR` is unset
    551   or empty, use ANSI escape sequences to color the output even when not
    552   writing to a terminal
    553 - `LC_CTYPE`: when it contains `UTF`, the tree is displayed using Unicode
    554   graphic characters instead of ASCII
    555 - `NO_COLOR`: when set to a non-empty value, no ANSI escape sequence is output
    556 - `PASHAGE_DIR`: database directory to use instead of `~/.passage/store`
    557 - `PASSAGE_DIR`: database directory to use instead of `~/.passage/store`
    558   when `PASHAGE_DIR` is unset
    559 - `PASSWORD_STORE_DIR`: database directory to use instead of
    560   `~/.passage/store` when both `PASHAGE_DIR` and `PASSAGE_DIR` are unset
    561 
    562 ### move
    563 
    564 Syntax:
    565 
    566 ```
    567 pashage move [--reencrypt,-e | --interactive,-i | --keep,-k ]
    568              [--force,-f] old-path ... new-path
    569 ```
    570 
    571 This subcommand moves or renames secrets and subfolders recursively,
    572 using the same positional argument scheme as `mv(1)`.
    573 By default it asks before overwriting an existing secret and it re-encrypts
    574 the secret when the destination has a different _recipient_ list.
    575 
    576 Flags:
    577 - `-e` or `--reencrypt`: always re-encrypt secrets
    578 - `-f` or `--force`: overwrite existing secrets without asking
    579 - `-i` or `--interactive`: asks whether to re-encrypt or not for each secret
    580 - `-k` or `--keep`: never re-encrypt secrets
    581 
    582 Environment:
    583 - `PASHAGE_AGE`: external command to use instead of `age`
    584 - `PASHAGE_DIR`: database directory to use instead of `~/.passage/store`
    585 - `PASHAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    586   `~/.passage/identities`
    587 - `PASSAGE_AGE`: external command to use instead of `age` when
    588   `PASHAGE_AGE` is unset
    589 - `PASSAGE_DIR`: database directory to use instead of `~/.passage/store`
    590   when `PASHAGE_DIR` is unset
    591 - `PASSAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    592   `~/.passage/identities` when `PASHAGE_IDENTITIES_FILE` is unset
    593 - `PASSWORD_STORE_DIR`: database directory to use instead of
    594   `~/.passage/store` when both `PASHAGE_DIR` and `PASSAGE_DIR` are unset
    595 
    596 ### random
    597 
    598 Syntax:
    599 
    600 ```
    601 pashage random [pass-length [character-set]]
    602 ```
    603 
    604 This subcommand generates a new secret, like the _generate_ subcommand,
    605 then directly displays on the standard output without storing it.
    606 
    607 Environment:
    608 - `PASSWORD_STORE_CHARACTER_SET`: character set to use with `tr(1)` when
    609   `character-set` is not specified, instead of `[:punct:][:alnum:]`
    610 - `PASSWORD_STORE_GENERATED_LENGTH`: number of characters in the generated
    611   secret when not explicitly given, instead of 25
    612 
    613 ### reencrypt
    614 
    615 Syntax:
    616 
    617 ```
    618 pashage reencrypt [--deep,-d] [--interactive,-i] pass-name|subfolder ...
    619 ```
    620 
    621 This subcommand re-encrypts in place the given secrets, and all the secrets
    622 recursively in the given subfolders.
    623 
    624 Flags:
    625 - `-d` or `--deep`: re-encrypt subfolders with their own recipient list
    626 - `-i` or `--interactive`: asks whether to re-encrypt or not for each secret
    627 
    628 Environment:
    629 - `PASHAGE_AGE`: external command to use instead of `age`
    630 - `PASHAGE_DIR`: database directory to use instead of `~/.passage/store`
    631 - `PASHAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    632   `~/.passage/identities`
    633 - `PASSAGE_AGE`: external command to use instead of `age` when
    634   `PASHAGE_AGE` is unset
    635 - `PASSAGE_DIR`: database directory to use instead of `~/.passage/store`
    636   when `PASHAGE_DIR` is unset
    637 - `PASSAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    638   `~/.passage/identities` when `PASHAGE_IDENTITIES_FILE` is unset
    639 - `PASSWORD_STORE_DIR`: database directory to use instead of
    640   `~/.passage/store` when both `PASHAGE_DIR` and `PASSAGE_DIR` are unset
    641 
    642 ### show
    643 
    644 Syntax:
    645 
    646 ```
    647 pashage [show] [--clip[=line-number],-c[line-number] |
    648                 --qrcode[=line-number],-q[line-number]] pass-name ...
    649 ```
    650 
    651 This subcommand decrypts the given secrets and by default displays the
    652 whole text on the standard output.
    653 
    654 Note that when a subfolder is given instead of a secret, the _list_ command
    655 will be used instead, without any warning or error.
    656 
    657 Flags:
    658 - `-c` or `--clip`: paste the given line (by default the first line) of the
    659   secret into the clipboard instead of using the standard output
    660 - `-q` or `--qrcode`: display the given line (by default the first line) of
    661   the secret as a QR-code instead of using the standard output
    662 
    663 Environment:
    664 - `PASHAGE_AGE`: external command to use instead of `age`
    665 - `PASHAGE_DIR`: database directory to use instead of `~/.passage/store`
    666 - `PASHAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    667   `~/.passage/identities`
    668 - `PASSAGE_AGE`: external command to use instead of `age` when
    669   `PASHAGE_AGE` is unset
    670 - `PASSAGE_DIR`: database directory to use instead of `~/.passage/store`
    671   when `PASHAGE_DIR` is unset
    672 - `PASSAGE_IDENTITIES_FILE`: _identity_ file to use instead of
    673   `~/.passage/identities` when `PASHAGE_IDENTITIES_FILE` is unset
    674 - `PASSWORD_STORE_DIR`: database directory to use instead of
    675   `~/.passage/store` when both `PASHAGE_DIR` and `PASSAGE_DIR` are unset
    676 
    677 ### version
    678 
    679 Syntax:
    680 
    681 ```
    682 pashage version
    683 ```
    684 
    685 This subcommand displays on the standard output the version and author
    686 list.
    687 
    688 This subcommand is not affected by the environment.