# Rook

![logo](https://hg.sr.ht/~ser/rook/raw/assets/rook.png?rev=tip)

[![Build status](https://builds.sr.ht/~ser/rook/.build.yml.svg)](https://builds.sr.ht/~ser/rook/.build.yml?) ★ [Releases](https://downloads.ser1.net/software/rook/) ★ [Changelog](https://hg.sr.ht/~ser/rook/browse/CHANGELOG.md?rev=tip) ★ [Issues](https://todo.sr.ht/~ser/rook) ★ [![Go Report Card](https://goreportcard.com/badge/ser1.net/rook)](https://goreportcard.com/report/ser1.net/rook)

A lightweight, stand-alone, headless secret service tool backed by a Keepass v2 database.

-   [Usage](#usage)
-   [Install](#install)
-   [Security](#security)
-   [Roadmap](#roadmap)
-   [Credits & Related](#credits-related)
-   [Other solutions](#other-solutions)

Rook allows you to use a KeePass v2 database as storage for secrets. It provides client and server modes; the server unlocks the database and stays in memory; the client communicates over a socket with the server and fetches data. This allows:

- Using your KeePass database, instead of having to have all your secrets _also_ stored in a second system
- Allows you to unlock the database once, instead of every time the system needs a secret
- Allows you to use your secrets in scripts and tools such as [mbsync/isync](https://github.com/gburd/isync), [vdirsyncer](https://github.com/pimutils/vdirsyncer), [offlineimap](https://github.com/OfflineIMAP/offlineimap), [msmtp](https://marlam.de/msmtp), encrypted file system mounters, encrypted and/or offsite backup programs, IMAP email clients, and so on.
- Allows you to run a _headless_ secret server
- Is easily auditable, and as a single binary is more easily protectable

There are other ways of accomplishing each of these, but none that accomplish all of them. passhole and kpmenu are close alternatives. passhole is Python with a lot of dependencies to audit. Recent versions of kpmenu also have large dependency trees, and the mode of operation makes it harder to use for non-GUI scripting.

Rook is not compatible with any other secret service tools; it does not communicate over, or rely on, DBus, or anything systemd. It should work as well on Darwin, *BSD, or any other POSIX-ish OS as it does on Linux, although this has not been tested. I don't know what it'll do on Windows.

### Status & Features

Rook is mostly complete. Some features are missing; see the Roadmap section below. It might crash, although it's been a while since I've seen one. It should be currently able to replace most secret-service type calls you might make from scripts; you can easily fetch passwords by account with the client, which is what you really need.

- Read kdbx v2 Keeass database files
- List entries from the DB
- Search entries, by title, URL, or tag
- Show entries
- Show individual fields, such as username & password
- Reload a (presumably changed) database; open a different database
- Lock / unlock a database
- Pin-controlled client calls for additional security (see Secure section, below)
- Headless
- One-time pin soft locking & unlocking
- Minimal dependencies and an auditable amount of sourcecode -- you can check to make sure it's not doing anything fishy, even if you aren't a Go expert. This repo contains around 1k LOC in 5 files. Assuming you trust the core Go team, you only then have to audit two other external libraries: a [flags lib](https://hg.sr.ht/~ser/claptrap) written by me that is a single Go file with no external dependencies and which has no networking code; and [the library](https://github.com/tobischo/gokeepasslib) that provides access to the KeepassDB (gokeepasslib has itself one external dependency, a crypto library written by the same author) - neither of which have networking code.
- Supports either password, or password + keyfile credentials

# Usage

`rook help` prints a list of the supported commands. Start a rook server process with `rook serve`, and then access it by calling `rook` again with one of the other commands. There is no configuration, or configuration file.

Exit the rook server with `rook exit` or `^c`. If, for some reason, it leaves behind the socket file, the next time you start rook it'll complain about one already running. You can override that warning with the `-f` argument.

Rook will watch the DB file and auto-refresh the memory cache if the file changes.

## Serving a database

Start a rook server with:

```bash
rook -f serve -P /path/to/your.kdbx
```

`-f` here forces rook to ignore and overwrite any pre-existing socket file; you probably shouldn't use it unless you are certain rook isn't already running. `-P` tells rook to prompt for the database password. `-P` can be used either when starting a server, or when calling `open` from a client. Alternatively (and, probably, more normally), start the rook server with no password, and unlock it with a client call when you can prompt for a password:

```bash
$ rook serve /path/to/kdbx &
$ rook open -P
```

If you use a key file for a second security factor, you can specify it with the `-k` argument to either `serve` or `open`.

Or, the server can be called with no database, and the client can tell it which database to open by passing it a path; in this case, it must be a path the server can find the file through, usually a fully qualified path. However, if the rook server was run in the same directory as one or more databases, the client can provide a simple relative path:

```bash
$ rook open -P my.kdbx
```

A database supplied by `open` replaces whatever the server was started with.

As an alternative to all of password prompting, rook will also take a password from the `ROOK_PASSWORD` environment variable. The prompt takes precedence. Remember that passwords are plain text, and environment variables are -- while reasonably secure -- still more persistent and accessible than other mechanisms.  The password may also be provided to the `serve` command via stdin pipe with the `--stdin` argument; this allows starting a Rook server with a password vault without exposing the password through environment variables and without requiring user interaction. This means you could also do this in an interactive bash, as an alternative to `-P`:

```
$ rook serve --stdin database.kdbx
supersecretpassword
^d
```

It's possible to tell whether the database is unlocked by making most any other request, e.g. `info`, as the server will return a `NO OPEN DATABASE` error message. If no database is open, the `lock` and `commands` requests will not throw errors, and the only other request that will work is `open`.

Databases can be live-reloaded with the `reload` command; this assumes the credentials have not changed,and it simply replaces the in-memory database from the current version on disk. There is not yet -- and may never be -- fancy DB merging such as KeepassXC does; it's a straight-up replacement.

If you want to move the socket location, e.g. to `/var/run/user/$(id -u)/rook.sock` then use the `--socket` argument. If you do this, you **must** set it for **both** the server and every client call. Easiest would be to create an alias in your shell's rc file, but you can also just specify it everywhere you call Rook.

## Searching & showing

If the entry Title is unique, then `show` only needs that; otherwise, it'll show all the matching entities -- which may not be what you want when using rook for script authentication purposes. In the case of duplicates, provide the path - separated by `/` - to the entity -- or part of it. For example, if you Keepass database looks like:

```
Root
  Internet
    Accounts
      me
  Server
    Accounts
      me
  Identities
    me
    myself
```

then `rook show me` will show of all three `me` entries; `rook show Accounts me` will show two of them; `rook show myself` will show only the one entry; and `rook show Server/Accounts me` or `rook show Identities me` will show only the one entry. A fully qualified path would be `rook show Root/Internet/Accounts me`, but rook requires only enough to uniquely identify entries.

Rook can also display your database as a tree by using the `--tree` argument:

```
$ rook ls -t
┌─ /
├─┬─ Subgroup  1
│ ├─┬─ Subsubgroup 2
│ │ └── Subsub item
│ └── Subgroup 1 item 1
├── First entry
├── Second test
├── Third entry
├── fourth entry
└── Something wicked
```

Empty groups are not shown; leaves in the tree are _always_ entries.

## Locking

Databases can be hard-locked with the `lock` command, e.g. `rook lock`. With a hard lock, rook forgets everything it knows about the database, except for the path on the filesystem. To unlock the database, a client call of `rook open -P` is required —- ths will prompt for the DB password.

Databases can also be soft-locked by calling `lock --pin`; in this case, the rook server will generate a 4-digit one-time pin and return it to the client. This pin can be used one time to unlock the database with `open --pin <digits>`. If `open --pin` is called incorrectly 3 times, the database is hard-locked.  Any other calls to rook while the database is in this state are soft-failures, in that they return an error, but do not count as failed unlock attempts. This prevents cron-jobbed scripts looking up paswords from hard-locking the database, and since none of the requests can be used to brute-force rook, they're considered mostly harmless. Similarly, attempts to call `lock --pin` while the database is locked also soft-fail.

If a database is soft-locked, a client call to `open -P` (providing the full password) will also unlock the database.

Rook does not have any built-in support for timing-out and locking databases. This can be accomplished with cron, or systemd timer units. Since the intended purpose of rook is to provide secrets to cron jobs, such as email and calendar syncing, there is no "timeout after disuse period" functionality. However, in some cases a more ideal behavior can be tied into the screen locker; an example is provided in (Examples)[#Examples], below.

## Caveats

`rook list` shows all entries in the DB, with an index number and a path. While the index can be used, it's not a fixed thing and could change as the database changes, so it's best to not rely on it in scripts.

## Examples

The alias in the following example isn't necessary; it's just to keep rook running everything in `${PWD}`.

![](https://cloud.ser1.net/api/public/dl/HMme5iHN?inline=true)

Here are some code snippets from various config files:

**mbsync** (`~/.config/isyncrc`)
```
PassCmd "rook show -p 'Email account'"
```

**aerc** (`~/.config/aerc/accounts.conf`)
```
source-cred-cmd = rook show -p Accounts 'Email account' 
```

**vdirsyncer** (`~/.config/vdirsyncer/config`)
```
password.fetch = ["command", "rook","show","-p","CardDAV account"] 
```

**msmtp** (`~/.msmtprc`)
```
passwordeval   rook show -p Identities "SMTP server"
```

**restic**
You could store all of your restic backup information in Keepass:

```
RESTIC_PASSWORD=$(rook show -p Accounts/Backups glamdring) \
  RESTIC_REPOSITORY=$(rook show -f restic_repository Accounts/Backups glamdring) \
  EXCLUDES=($(rook show -f excludes Accounts/Backups glamdring)) \
  /usr/bin/restic backup ${EXCLUDES[@]/#/-e } --one-file-system \
  $(rook show -f files Accounts/Backups glamdring)
```

Searches look in several places for matches: titles, URLs, and tags. However, `show` only matches against titles. Both `search` and `show` also take an optional path as the first argument, which filters the entities before matching; e.g.:

```bash
$ rook ls
1    Accounts   Entry 1
2    Accounts   Entry 2
3    Internet   Entry 1
4    Internet   Entry 2
$ rook search 'Entry 2'
Index: 2
Path: Accounts
Title: Entry 2

Index: 4
Path: Internet
Title: Entry 2
$ rook search Internet 'Entry 2'
Index: 4
Path: Internet
Title: Entry 2
```

### Locking and unlocking with a screensaver

If you're looking at rook, you may have a similar system, as I describe here, all of these functions are usually provided by the DE; if you're replacing one, you've probably replaced all of them.

On my system, I use [xss-lock](https://bitbucket.org/raymonad/xss-lock) as an auto-locker, although [xautolock](https://ibiblio.org/pub/Linux/X11/screensavers/) works just as well. In both cases, the locker called is a shell script that wraps [i3lock](https://github.com/Raymo111/i3lock-color); I do this because there are things I want to happen when the screen locks, such as disabling dunst messaging. In this script, you can call `rook lock --pin`, get the pin and save it, and then use the pin to unlock rook when the screen is unlocked. 

A simplified version of what I run is:

```
$ xss-lock ~/.bin/superlock
$ cat ~/.bin/superlock
#!/usr/bin/zsh

dunstctl set-paused true

lock() {
  i3lock-fancy-rapid 16 3 -enkS 1
  rook open --pin $1
}
lock $(rook lock --pin)

dunstctl set-paused false
```

or,

```
rook lock --pin | (
  i3lock-fancy-rapid 16 3 -enkS 1
  read pin && rook open --pin $pin
)
```

Just be careful about using long-term environment variables to store the pin, because environment variables can be read from `/proc` and it increases the attack surface. For example, I'd avoid:

```
PIN=$(rook lock --pin)
i3lock
rook open --pin $PIN
```

## Autotype

There are shell scripts in the `utils/` directory: `autotype.sh` and `getAttr.sh`. `autotype.sh` can provide form-filling utility; it works by grabbing the focused window's title and uses `rook match` to find the best match in the DB -- rook uses the Keepass Autotype rules for this, if they exist -- and the best match is returned with the first line being the defined autotype sequence for the entry. The script then parses out the key/value pairs and uses `xdotool` to type the sequence. `getAttr.sh` uses rofi to allow the user to select an entry from the DB, and then an attribute from the entry, and then it performs a user-requested action with the data.

`autotype.sh` and `getAttr.sh` are shell scripts; if you use them, you should also read through them. They're not exactly trivial scripts, but at ~100 lines long each they're auditable even if you're not a shell expert. Again, **I** trust me implicitly, but you shouldn't.

`autotype.sh` is straightforward -- it just does its thing: tries to find the best entry and type the autotype sequence. If it gets multiple matches, it shows them in a rofi dialog, you choose one, and off it goes.

`getAttr.sh` has a `-h` argument for help, and more complex options. It can use [fzf](https://github.com/junegunn/fzf) instead of `rofi`, and be used entirely from the command line. It can copy the selected value to the system clipboard, print the value out, or type the value out with `xdotool`.

### autotype & getAttr Dependencies

These are dependencies **only** for the utility scripts -- the rook binary itself does not use any of these.

- [zsh](https://www.zsh.org/), because I just couldn't be arsed to do the extra work to make some things in `autotype.sh` work the harder way in bash.
- [ripgrep](https://github.com/BurntSushi/ripgrep), because -- again -- it makes some things just so much easier.
- [xdotool](https://www.semicomplete.com/projects/xdotool/), for teh tappy-tap-tap.
- [xprop](https://gitlab.freedesktop.org/xorg/app/xprop), because Luakit needs hacks to work properly. If you don't use Luakit,
  you can (should?) delete the hacks -- it'll probably make things more robust.
- [yad](https://github.com/v1cont/yad), for providing info dialogs.
- [xsel](https://vergenet.net/~conrad/software/xsel/), used in `autoType.sh` to put values in the clipboard.

KeepassXC autotype sequences have a lot of keywords; the script understands a small number of these -- ENTER, DELAY, TOTP, USERNAME, PASSWORD.  It should be easy to add to this list by modifying the case switch at the end of the file -- improvement patches are welcome.

To use the script, put it in your path and bind it to a hotkey, e.g.

```bash
herbstluftwm keybind Mod4+t spawn ${HOME}/bin/autotype.sh
### OR, if you installed with a package manager:
herbstluftwm keybind Mod4+t spawn /usr/bin/rook-autotype
```

If you installed rook with one of the binary distribution packages, and you have the necessary required programs listed above (rofi, ripgrep, yad, etc.), then the utility scripts will be installed in `/usr/bin/` as `rook-autotype` and `rook-getattr`.

## Select Entry Attribute

There's a shell script in `utils/` called `getAttr.sh` which shows a list of all entries, and when one is selected shows a list of all attributes, one of which can be selected. If called with the `-t` argument, the selected value will be typed using `xdotool`. If called with the `-c` arg, the value will be copied into the clipboard using `xsel`. If no argument is given, the script prints the value to STDOUT.

`getAttr.sh` can prompt either with `rofi` or `fzf`; the `-f` argument selects `fzf`, and `rofi` is used by default. Using `fzf` allows `getAttr` to be used without a GUI.

## Testing

You can see how `autotype.sh` will work by faking an input dialog with `yad`. To get as close to real life, you'll want to run the application that will ask for the login -- if it's Paypal, then go to Paypal.com in an incognito window and click "Logn". If it's some GTK application, run the application and start the login process. Then use `xprop` to get the window title -- this is what `autotype.sh`, and incidentally, KeepassXC, uses for entry matching. Then run `yad` with that window title as the `--title` parameter -- something like this:

```bash
yad  --form --field "Username" --field "Password:H" --field "TOTP:NUM" --title "Log in to Sourcehut"
```

Give that Username field focus and trigger your autotype -- once you hit OK, the values will be printed on the command line.


# Install

## From your distribution

rook packages are available in Apline testing, and in Arch AUR. For Alpine, you'll have to have the testing repo enabled:

```shell
echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing/" >> /etc/apk/repositories
apk add --no-cache rook
```

For Arch, use your AUR enabled tool:

```shell
yay -Sy rook
```


## From pre-built binaries

Download the latest release from the [releases page](https://downloads.ser1.net/software/rook/). Optionally, also download the binary signature and validate the binary. Decompress the binary with `gzip`, copy it somewhere in your path, and off you go.

I provide builds for Linux arm64 and amd64, and the _latest_ version as RPM, deb, apk, and Arch zst packages for each of these.

You can get my public key either from [Sourcehut](https://meta.sr.ht/~ser.pgp), or from one of the public key servers. You'll want to grab the one that says:

`Sean E. Russell (Signing key for Sourcehut builds) <ser@ser1.net>`, which is key `5E0D7ABD6668FDD1`. To get it from Sourcehut, where the builds are made:

```bash
curl https://meta.sr.ht/~ser.pgp | gpg --import
###      OR    ###
gpg --keyserver keys.openpgp.org --recv-keys 6668FDD1
###  AND THEN  ###
gpg --verify rook_arm64_latest.gz.sig
```

If you install from one of the pre-built packages, the script `autotype.sh` will be installed as `/usr/bin/rook-autotype`, and `getAttr.sh` as `/usr/bin/rook-getattr`.

## Build from source

If you want to try building and running on something other than Linux -- say, one of the BSDs, or Darwin -- then this or the [next section](#Install the Go way) is what you'll need to use.

You'll need [Mercurial](https://www.mercurial-scm.org/) and [Go](https://golang.org). For the manpages, you'll need Go 1.24 or greater.

```bash
hg clone https://hg.sr.ht/~ser/rook
go build .
# OR, to embed the correct version s.t. `rook version` works,
go build -ldflags "-X main.Version=$(hg log -r tip --template '{bookmarks}')" .
```

If you have Go 1.24 installed, you can generate the man pages with:

```
go generate
```

They'll be put in a directory called `man1`; you can then copy them into some place in your `man --path`.

## Install the Go way

```bash
go install ser1.net/rook@latest
# OR, if you know the version you want
go install -ldflags '-X main.Version=v0.0.9' ser1.net/rook@v0.0.9
```

## nfpm

The repo contains an [`nfpm.yaml`](https://nfpm.goreleaser.com/) file, from which can be built packages for Arch, rpm, apk, and deb systems. You could use this to build old versions, or versions for other architectures such as arm5/6/7. If you want to do this, you're best off reading the nfpm usage docs, since repeating them here would be silly. But, basically, you'll need to make sure some environment variables are set, and run (to get an apk; replacable with `rpm`, `deb`, or `archlinux`):

```bash
VERSION=$(hg log -r tip --template '{bookmarks}') \
  GOOS=$(go env GOOS) \
  GOARCH=$(go env GOARCH) && \
  nfpm pkg -p apk
```

# Security

Rook's threat model is simple: it doesn't expose anything to any network connection, but _does_ trust that the local system is secure. Don't use Rook if you don't trust the root user, or worry that someone could masquerade as your user. If your user or root accounts are compromised, then an attacker can talk to the Rook server over the socket and get at your secrets. Since the socket is created with user-only RW permissions, and is created in the user's home directory, it should still be safe on multi-user systems -- with the aforementioned caveat that root has access to everything. See the note below about [Secure mode](#Secure_mode) about how to tighten security.

Rook attempts to keep external dependencies to an absolute minimum. Rook itself is not a large program, and is only multiple files for semantic encapsulation. Aside from the Rook code itself, dependencies are:

- [github.com/tobischo/gokeepasslib/v3](https://github.com/tobischo/gokeepasslib/v3), which is the library used to process the KeePass libraries. It should have *no* network access, and is easy enough to grep for `net` imports. It, itself, depends on
- [github.com/tobischo/argon2](https://github.com/tobischo/argon2), which is a crypto library. Again, no network and easy enough to grep for `net`
- [golang.org/x/crypto](https://golang.org/x/crypto), which I haven't audited, but I guess I trust the Go core team enough
- [golang.org/x/sys](https://golang.org/x/sys), same as above
- [ser1.net/claptrap/v4](https://hg.sr.ht/~ser/claptrap), my bespoke flags library. It has no net access, is entirely a single 4k-line file. **I** trust it implicitly, but again, it's easy enough to check to make sure it's not doing anything shady. Again, the same author who wrote rook also wrote claptrap (me).

So, in all of this, Rook should be the only thing using `net`, and would be the only thing (if I weren't me) I'd be concerned about. **rook** needs `net` to create the local socket over which the client/server communicate, and there are only two places **rook** opens a connection, both of which are to a local socket.

Regardless, as with _any_ secret services, if you don't trust your own system, don't run these. Don't run KeePassXC at all, because with enough effort, root can still get memory dumps and extract secrets from your open database.

## Secure mode (`--secure`)

Security can be improved by running rook in secure mode, by starting the server with `--secure`. In this mode, the server will print out a 6-digit pin that the client will always have to provide for every interaction. Locking and unlocking the database will generate a new pin.

```
$ rook serve --secure -P secret.kdbx
Password:
123456
$ rook ls
FAIL
BAD COMMAND
exit status 1
$ echo 123456 | rook --pin ls
<all your secrets>
```

There are some subtlelies to note:

- There is a difference between a rook PIN and a rook OTP. On OTP is for one-time, and one time only. A PIN is for every action (with two exceptions below).
- The `open -P` command does _not_ require the PIN: in all of rook, if you can prove that you have the DB master password, all other security features are irrelevant. `open -P` will print the current PIN.
- If you `--secure` a database, you can still OTP lock and unlock the DB; in this scenario, you will _double_ lock the DB. To unlock the first lock, you'll need to call `open --pin` with the OTP, after which you're back to simply needing to supply the PIN for every command.
- You may OTP lock a PIN authenticated database. This will, effectively, double-lock the database, requiring **both** first a `open --pin`, and then the PIN auth for all following commands. As usual, providing the master password with `open -P` completely voids these locks. 

If you hard `lock` the database, the PIN will be cleared. When you `open -P` it again, a _new_ PIN will be generated. This bears on cases where you're using it with the Linux kernel keychain, described below.

### Linux kernel keychain

This PIN feature can be used with the Linux kernel keychain to further strengthen security by preventing other user sessions, and even root, from accessing secrets. This is the most secure way to run rook.

You will need `keyutils` installed, for the `keyctl` command. To use this, run the rook server like this:

```
$ export ROOK_KEY=$(rook serve --secure --detach ~/secrets.kdbx | keyctl padd user rookpin @s)
$ rook open -P
Password:
$ keyctl pipe $ROOK_KEY | rook --pin ls
<all your secrets>
```

Explaining they kernel keychain and its use is beyond the scope of this document, but in short:

- `keyctl` is the tool for interacting with the secure kernel keychain. The `@s` restricts access to secrets to programs running in the same session. This prevents root from peeking at the PIN, but also prevents a hacker from logging in as you and accessing the PIN. In `--secure` mode, without the PIN, the secrets can't be accessed.
- The value of `$ROOK_KEY` is the kernel's generated key name; it is not itself a secret, but is needed to get at the PIN; the value can only be retrieved by a process in the same session.
- The secrets (the PIN) is stored _in_ the kernel. All of your passwords are still in your KeePass DB; it's only the temporary PIN that's saved in the kernel, and that's cleared on reboot. You can also manually clear the value with `keyctl`, but it's probably just to `lock` the DB, which invalidates the PIN.

If you use this, you'll want to do two things:

- Propagate the keychain key -- what I've been storing in `$ROOK_KEY`; there's nothing magical about the variable name -- to child processes. In herbstluftwm, you can do this with the `herbstclient setenv` command, which sets environment variables that get inherited by any process spawned by Herbstluftwm. Other window and desktop managers will have their own mechanisms. 
- Create an alias or shell function for the rook command that does the `keyctl pipe $ROOK_KEY | rook --pin` command.

If you're absolutely secure about your desktop -- it's firewalled off in a LAN and all that -- PIN locking and using the kernel keychain might be overkill. But maybe not. It adds a little complexity to using rook, but adds a lot of security.


## (Auto)Locking

If you want the DB to be locked periodically, run an external cron job that calls `lock`. Rook _might_ add a lock-after-inactivity function, since this can't really be done externally, but (a) it'd be useless to me since the _purpose_ of rook is as a secret-service for other cron jobs like mail syncing, and so it'd _never_ timeout and lock; (b) I'm really trying to limit the amount of code in rook, making it more auditable, and that means leaving out features; and (c) a general lock-after-time regardless of use is arguably more secure, and a more common feature in these sorts of tools -- and that *can* be done externally.

## Master password plaintext considerations

Rook itself does not keep the master password in memory in clear text, and neither does gokeepasslib. Variables that hold the password during credentialing are cleared, or are scoped out in brief functions.

Don't use the `--password` argument. It's there to simplify testing, nothing more. If you use it, it could easily be stored in a shell history file, or even worse, be seen by any user in the sysetem through `ps`.

Use of `ROOK_PASSWORD` for the server is discouraged. While any account that can inspect the rook process' environment (via `/proc/<pid>/environ`) can also probably dump memory and get at your secrets, by using the environment variable your master password is stored in plain text and this is less secure. It's less of a concern for client calls, because client calls are ephemeral.

The best way to minimize the master password's existence in memory in plain text is to run the rook server with at most the database path, and then use the `open` command with the client, e.g.:

```
$ rook serve database.kdbx
$ rook open -P
```

# Roadmap

Most of the roadmap is kept in the sourcecode. A good way of looking at it is to get [legume](https://sr.ht/~ser/legume) and run leg in the source directory. However, there is a general thrust to most of what's coming:

- Bug fixes
- DB modification commands. I'm bearish about adding these: I think they're best done through tools like KeepassXC, which provide nice UIs, and which handle conflicts and merging well; they also add a **lot** of complexity that would make auditing harder, and for questionable value. Rook is not intended to fully replace KeepassXC tools. That said: small changes, like changing a password or adding TOTP to an entry, might be handy to do on the CLI. We shall see.

# Credits & Related

There are countless giants that should be listed, that we all take so much for granted that they are unknown to most of us. However, I'd like to call out a couple of individuals whose code I've leveraged heavily, or sometimes (basically) stolen. Those I've copied/pasted from I've also done my best to audit in the process.

- [tobischo](https://github.com/tobischo), for whom I'm very grateful for allowing me to avoid reading the entire Keepass v2 spec and implement it by hand. Although I probably should have, in following with my own attempt to minimize the attack surface of rook.
- [jlinoff](https://github.com/jlinoff), whose [getPassword](https://gist.github.com/jlinoff/e8e26b4ffa38d379c7f1891fd174a6d0) (MIT) code filled a tedious hole. I very much appreciate that jlinoff not only provided a solution, but provided _3_ solutions with different dependencies ranging from stdlib only, stdlib but with some of the more esoteric packages, and a final one that pulls in a `golang.org/x` package.
- The rook logo was derived from SVG art titled ['Raven With Key' by Karen Arnold](https://openclipart.org/detail/252186/raven-with-key). The original was licensed CC0, and so, therefore, is this.

rook is developed using a set of decentralized, CLI-based tools. Web tools are fine, and they're sometimes necessary, but they centralize data, are hard to script, and just generally get in my way. When there are many people from different domains collaborating on a thing, this extra impediment is necessary; when there aren't, it's not. I use code comments for issue tracking and [legume](https://hg.sr.ht/~ser/legume) to make that easier to view; I use [changelog](https://hg.sr.ht/~ser/changelog) to generate the [CHANGELOG.md](https://hg.sr.ht/~ser/rook/browse/CHANGELOG.md?rev=tip).

rook was inspired by [kpmenu](https://github.com/AlessioDP/kpmenu), which approaches security differently, requiring interaction from the user on every request. This makes it harder to use as a secret service for automating scripts, and since kpmenu has a very specific threat model it's been resistant to [patches](https://git.sr.ht/~ser/kpmenu) that would weaken their solution, and even with those patches [additional tools](https://hg.sr.ht/~ser/quasiauto) are needed to support autotype. As mentioned in [Security](#Security), Rook assumes the user environment is secure and can therefore take a more relaxed approach; these are the same assumptions and approaches used by any secret service software, such as Gnome's [secret-tool](https://gnome.pages.gitlab.gnome.org/libsecret/), [keyring](https://github.com/jaraco/keyring), or [pass](https://www.passwordstore.org/).

# Other solutions

Linux users have many options for storing passwords. I can't list all of them here, but I can list the ones I've used, and why I believe **rook** was necessary. In every case where the tool has a bespoke data store, it means the user having to _manually_ syncing passwords between Keepass and whatever the tool is using, and is for this reason is IMO unsuitable.

- [KeepassXC](https://keepassxc.or). This is an outstanding program that you should use. It does include a _proper_ secret service option, in that it is compatible with keyring and Gnome's secret-tool. It is, however, a GUI tool, and the CLI program that comes with it requires a password every time it is called, making it unsuitable for background scripts.
- [kpmenu](https://github.com/AlessioDP/kpmenu). As mentioned above, kpmenu's architecture does not lend itself to running as a secret service that can be used by background jobs needing credentials.
- [secret-service](https://github.com/yousefvand/secret-service) is another keyring-compatible solution. It uses its own backend DB store, and I want to use a Keepass DB. It also has a fairly large number of external, non-stdlib dependencies (19), making it much harder to fully audit.
- [secret-tool](https://gnome.pages.gitlab.gnome.org/libsecret/). This is what Gnome uses for password stores; it has a bespoke password database (not Keepass), and communicates over DBus -- using secret-tool outside of Gnome pulls in several extra services and software dependencies, which is relatively heavy if a secret service is all you're using it for.
- [keyring](https://github.com/jaraco/keyring). This is (I believe) the CLI tool for KDE and KWallet; it is compatible with Gnome's secret service, again communicating over DBus. It requires the KWallet service to be running in the background, and IIRC that kicks off several related KDE services. Like secret-tool, if you're not running the KDE desktop, you're getting a chunk of the load of it by running `keyring`.
- [pass](https://www.passwordstore.org/). git for passwords, encrypted with GPG! It's a fantastic idea, and by using `gpg-agent` it's not adding much to your service load. It has two downsides: first, it's _still_ a bespoke backend password database, meaning having to manually sync any passwords or entries with Keepass by hand; and second, it exposes a huge amount of metadata about the secrets. Since the entire DB is a filesystem, where the entry titles are directory names and attributes are files in the directory, the entire structure of the database is exposed, unencrypted, and readable. Attackers may not know what your Pornhub password is, but they know you have an account. This is fine for `pass`, which is designed more for secret sharing across teams, and so the account names are not considered confidential information. It's pretty horrible for anyone else, and especially anyone considered a dissident for whom that metadata could be used in persecution. 
- [gopass](https://github.com/gopasspw/gopass) is a Go-based client for `pass` databases. If you use pass, it's a pretty nice tool. `pass` itself is a collection of shell scripts; `gopass` is a single self-contained binary. It doesn't have any connection with Keepass, though.
