A Password Manager Using age for Encryption
It is inspired by pass. seniorpw's features are
- Multiple stores
- OTP support
- Clipboard support for Linux (Wayland and X11), Termux, WSL, Darwin (macOS)
- Select and automatically copy or type a password via
senior menu - git support
- Completions for bash and zsh
- Passphrase protected identities
- Passphrases only need to be entered once per session and then get cached by
senior agent - A store can be shared among a group (encryption for multiple recipients)
- Search (grep) inside the passwords
- No config files
- Symlinks between stores are supported
To do:
- Android app
- Browser Add-On
- More import scripts
| Name | Backend | Encrypted Identities | Agent | Agent Timeout | git | Key-Value Pairs | TOTP | Configless | Language |
|---|---|---|---|---|---|---|---|---|---|
| pasejo | age | ❌ | - | - | ✅ | ❌ | ✅ | ❌ | Rust |
| psswd | age | ❌ scrypt | ❌ | - | ❌ | ❌ | ❌ | ✅ | Rust |
| Pa-rs E | - | - | - | - | - | ❌ | ❌ | ❌ | Rust |
| privage | age | yubikey | - | - | ✅ | ❌ | ❌ | ✅ | Go |
| neopass | age | ✅ | ❌ | - | - | ❌ | ❌ | ❌ | Go |
| pa | age | ✅ | ❌ | - | ✅ | ❌ | ❌ | ✅ | POSIX Shell |
| passage | age | ✅ | ❌ | - | ✅ | ❌ | ✅ | ✅ | Bash |
| kbs2 | age | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | Rust |
| pass | gpg | ✅ | ✅ gpg-agent | ✅ gpg-agent | ✅ | ❌ | ✅ | ✅ | Bash |
| pago | age | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | Go |
| seniorpw | age | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Rust |
senior init
# optionally initialise for git use:
senior git init
senior git add '*'
senior git commit -m "init"The default store name is main. You can use senior -s <NAME> <command> to use another name.
senior clone git@gitlab.com:exampleuser/mystore.gitWithout specifying another store name (using -s), the default name will be mystore in this example.
Someone who already has access to the store can then add you to the recipients via
senior add-recipient <PUBLIC KEY> <ALIAS>Both senior init and senior clone support the optional flag -i <FILE> or --identity <FILE>
to use an existing identity instead of generating a new one.
Supported are
- Cleartext age identity
- Passphrase encrypted age identity
- ssh key of type ed25519 or rsa
senior edit example.com
senior show example.com
senior mv example.com example2.com
senior rm example2.comsenior show has the option -k or --key to only print the value of a key: value pair.
The special key otp creates the one-time password from the otpauth-string.
$ senior show example.com
mysecretpassword
user: myusername
otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example
$ # use `-c` or `--clip` to also add it to the clipboard
$ senior show -c -k user example.com
myusername
$ senior show -k otp example.com
118250With senior git you can run git commands in the senior print-dir directory.
If you have initialised your store for git use then
any senior edit creates a git-commit.
To sync it with remote, run
senior git pull
senior git pushYou can use multiple stores by setting -s or --store
$ ls "$(senior print-dir)"/..
friends main work
# the default store is `main`
$ senior show
/home/bob/.local/share/senior/main
├── gitlab.com
├── friends -> ../friends
│ ├── amazon.com
│ ├── example.com
│ └── netflix.com
└── gitlab.com
$ senior -s friends show
/home/bob/.local/share/senior/friends
├── amazon.com
├── example.com
└── netflix.com
$ senior -s work show
/home/bob/.local/share/senior/work
├── server1
└── workstationNotice the symlink main/friends -> ../friends. This makes the two commands
senior -s friends show example.com
senior show friends/example.comequivalent.
seniorpw recognises that main/friends/example.com is actually at friends/example.com and therefore uses
friends/.identity.age to decrypt.
The same goes for senior edit and using friends/.recipients/* to encrypt.
This is very practical for senior menu.
If only one store exists then this is the default store. Otherwise, main is the default store.
You can use the store argument multiple times or use globbing to run the same command for multiple stores.
senior -s '*' git pull
senior -s main -s work add-recipient ...senior menu type-content <KEY1> type-text <TEXT1> sleep <MILLISECONDS> type-content <KEY2> type-text <TEXT2> ...
senior menu clip <KEY>
senior menu uses a dmenu-like program (can be set with --menu-program <PROGRAM>) to let you select a password for the clipboard or for typing.
The typing program can be changed via --typing-program <PROGRAM>.
Set up some keybindings in your window manager to quickly clip/type passwords. An example for sway/i3 is
bindsym $mod+u exec senior menu type-content password type-text "\n"
bindsym $mod+y exec senior menu type-content otp type-text "\n"
bindsym $mod+t exec senior menu type-content user type-text "\t" type-content password type-text "\n"
If you have set a passphrase to protect your identity file, then running
age -d -i .identity.age example.com.age
would require you to enter the passphrase each time.
Because this is very cumbersome, seniorpw provides an agent.
Upon receiving your passphrase once,
senior starts senior agent to cache your identity.
This way you only have to enter your passphrase once per session.
There are two possibilities: senior grep and senior cat.
senior grep <REGEX PATTERN>This searches the password contents of an entire store for a regex pattern. Alternatively, use a custom pattern matching program for more sophisticated searches.
# using the system's grep
senior grep grep --color=always -i -n <REGEX PATTERN>
# using fzf
senior grep fzf --filter=<REGEX PATTERN> --no-sort
# using ripgrep
senior grep rg --color=always -i -n <REGEX PATTERN>Use senior cat to print the contents of the entire store or a subdirectory.
Pipe the output to your favourite pattern matching program.
senior cat | less
senior cat [dirname] | fzf
senior cat [dirname] | grep -C 5 -i -n <REGEX PATTERN>Simply use the provided PKGBUILD.
# Download the PKGBUILD into an empty directory
curl -LO "https://gitlab.com/retirement-home/senior/-/raw/main/PKGBUILD"
# Install the package with all its dependencies
makepkg -sic# build
make
# install
sudo make install
# uninstall
sudo make uninstallOn Termux you should omit the sudo.
Make sure you have the dependencies installed (look at depends and makedepends in the PKGBUILD).
Use the script pass2seniorpw.py to import your passwords.
./pass2seniorpw.py ~/.password-store "$(senior print-dir)"
# set a passphrase
senior change-passphraseFirst export your database as a CSV file, then use the script keepass2seniorpw.py to import your passwords.
./keepass2seniorpw.py exported-passwords.csv "$(senior print-dir)"
# set a passphrase
senior change-passphraseYour store is just a directory, usually ~/.local/share/senior/main/. Run senior print-dir to find out.
Let us look at the directory tree.
$ tree -a "$(senior print-dir)"
/home/bob/.local/share/senior/main
├── example.com.age
├── .gitignore
├── gitlab.com.age
├── .identity.age
└── .recipients
└── main.txtApart from .gitignore there are two special entries: .identity.age and .recipients/.
-
.identity.ageis your age identity that is used to decrypt the passwords. -
.recipients/main.txtcontains the public keys used for encrypting the passwords.
The passwords are age-encrypted text files. Let us look at a password:
$ senior show gitlab.com
mysupersafepassword
user: myusernameThe show command is equivalent to
$ age -d -i .identity.age gitlab.com.age
mysupersafepassword
user: myusernameWith senior edit ..., after editing the decrypted text file, it gets encrypted via
$ age -e -R .recipients/main.txt -o gitlab.com.age /tmp/gitlab.com.txt