Encrypting local files with revaulter-edit
revaulter-edit is a small utility for keeping local text files encrypted: notes, .env files, recovery codes, TLS keys, anything you want on disk but not in cleartext. Opening a file asks Revaulter to unwrap its encryption key, which you approve in the browser with your passkey, exactly like any other Revaulter operation.
Installing#
revaulter-edit ships in the same release archives as revaulter-cli, for Linux, macOS, Windows, and FreeBSD.
There’s also a container image: ghcr.io/italypaleale/revaulter-edit:2. See Using Docker below.
How it works#
Each file is encrypted locally with its own random 256-bit key and saved in JWE format.
That key is never stored on disk in cleartext. Revaulter wraps it with the same encrypt operation the CLI uses, and the JWE-protected header stores the wrapped key along with the key label and algorithm needed to unwrap it. Opening the file sends a decrypt request to Revaulter, and once you approve it in the browser, the file’s contents are decrypted locally.
Only the wrapped key is sent to Revaulter. The file’s contents never leave your machine, so revaulter-edit works with larger files too.
Each encrypted file is encoded as JWE and includes this info in the protected header:
{ "alg": "dir", "enc": "A256GCM", "revaulter": { "v": 1, "server": "https://revaulter.example.com", "keyLabel": "notes", "algorithm": "A256GCM", "wrappedKey": "<base64url>", "wrappedKeyNonce": "<base64url>", "wrappedKeyTag": "<base64url>" } }
Commands#
revaulter-edit has three subcommands, and they all take the path of a local file.
edit is the default: running revaulter-edit FILE is the same as revaulter-edit edit FILE.
edit#
Decrypts the file and opens it in your editor. Every time the editor saves, the contents are encrypted again and written back.
revaulter-edit [edit] [flags] FILEIf the file does not exist, a new random key is generated and wrapped with Revaulter, and the file is created the first time the editor saves.
The decrypted contents are written only to a temporary file that only your user can read, which is overwritten and deleted when the editor exits.
The editor is taken from --editor, then from the $VISUAL and $EDITOR environment variables.
Editors that open the file in an existing window and return right away, such as code (Visual Studio Code) and subl (Sublime Text), are launched with the --wait flag automatically, so the session lasts as long as the file is open. Pass --no-auto-wait to omit it.
cat#
Decrypts the file and writes its contents to stdout (logs go to stderr):
revaulter-edit cat [flags] FILEwrite#
Reads the new contents of the file from stdin, encrypts them, and writes them back:
revaulter-edit write [flags] FILEThis is the counterpart of cat, for writing a file without opening an editor. Use it to encrypt a file that already exists in cleartext:
revaulter-edit write secrets.txt.enc < secrets.txtTo update one from a script:
echo "my-secret-pw" | revaulter-edit write password.encOr to edit one with an editor that revaulter-edit can’t launch itself, for example from inside a container:
revaulter-edit cat secrets.txt > /tmp/secrets
"$EDITOR" /tmp/secrets
revaulter-edit write secrets.txt < /tmp/secretsIf the file already exists, its key is unwrapped and reused, so only the contents change. If it doesn’t, a new random key is generated and wrapped, as with edit.
Reading and writing are two separate operations, so the round trip above needs two approvals.
write refuses to replace the contents of a file with nothing unless you pass --allow-empty, so a pipe that produced no output doesn’t silently empty the file.
Flags#
All subcommands accept these flags, except where noted.
| Flag | Short | Required | Description |
|---|---|---|---|
--server | -s | Yes | Address of the Revaulter server (e.g. https://revaulter.example.com). Can also be passed as env var REVAULTER_SERVER |
--request-key | -k | One of --request-key or --request-key-file is required | Per-user request key (shown in the web UI after registration), or a JWT from a trusted OIDC issuer. Can also be passed as env var REVAULTER_REQUEST_KEY. Mutually exclusive with --request-key-file |
--request-key-file | One of --request-key or --request-key-file is required | Path to a file containing the per-user request key, or a JWT from a trusted OIDC issuer. A JWT is read from the file again for every request, so it can be a token that’s refreshed in place, such as a Kubernetes service account token, even while you’re editing. Can also be passed as env var REVAULTER_REQUEST_KEY_FILE. Mutually exclusive with --request-key | |
--user-id | Only with OIDC tokens | ID of the user the request is for, as shown in the web UI. Required when the request key is a JWT from a trusted OIDC issuer. Can also be passed as env var REVAULTER_USER_ID | |
--key-label | -l | For new files | Logical key label used to wrap the file’s encryption key. Can also be passed as env var REVAULTER_KEY_LABEL. Files that already exist carry their own label, which always wins |
--algorithm | -a | No | Algorithm used to wrap the file’s encryption key: A256GCM (default) or C20P |
--editor | No | For edit only: editor to launch. Defaults to the VISUAL or EDITOR env var | |
--no-auto-wait | No | For edit only: do not add the --wait flag to editors that need it, such as code and subl | |
--allow-empty | No | For write only: allow replacing the contents of the file with nothing | |
--timeout | -t | No | Timeout for the approval request, as a Go duration (e.g. 5m) |
--note | -n | No | Message displayed alongside the request. Defaults to the name of the file |
--insecure | No | Skip TLS certificate validation | |
--no-h2c | No | Do not attempt connecting with HTTP/2 Cleartext when not using TLS | |
--trust-store | No | Path to the anchor trust store, shared with revaulter-cli | |
--no-trust-store | No | Skip anchor pinning and hybrid bundle verification | |
--verbose | -V | No | Show debug-level logs |
Anchor pinning works exactly as it does for revaulter-cli, and uses the same trust store. A server pinned with revaulter-cli trust is already trusted by revaulter-edit.
Examples#
Set the connection details once, then keep the commands short:
export REVAULTER_SERVER="https://revaulter.example.com"
export REVAULTER_REQUEST_KEY="AbCdEf0123456789GhIj"
export REVAULTER_KEY_LABEL="notes"Create a new encrypted file, or edit an existing one:
revaulter-edit ~/notes/recovery-codes.txtRead a file without opening an editor:
revaulter-edit cat ~/notes/recovery-codes.txtLoad an encrypted .env file into the environment of a single command:
set -a
eval "$(revaulter-edit cat ~/projects/app/.env.enc)"
set +aEdit with VS Code, which is launched with --wait automatically:
revaulter-edit --editor code ~/notes/recovery-codes.txtUsing Docker#
The container image has no editor, so it’s built around cat and write, which cover reading a file, encrypting one, and updating one from a script.
To edit inside the container, supply an editor with --editor or EDITOR. It has to be statically linked, since the image has no shared libraries and no terminfo database. A busybox-static from the host works:
-v /bin/busybox:/bin/busybox:ro --editor "/bin/busybox vi"Two volumes matter:
- The directory holding the files to work on, so the container can read and write them.
- A volume for the trust store, at
/data. Anchor pins are saved there. Without it, every run is a first contact: each command asks to pin the anchor again, and fails when there’s no terminal to ask on.
Pin the server’s anchor once, into a volume that later runs reuse:
docker run --rm -it \
-v revaulter-trust:/data \
ghcr.io/italypaleale/revaulter-cli:2 trust \
--server https://revaulter.example.com \
--request-key "$REQUEST_KEY"Then read and write files. These commands need no terminal, so they work in scripts:
docker run --rm \
-v revaulter-trust:/data -v "$PWD:/work" \
-e REVAULTER_SERVER -e REVAULTER_REQUEST_KEY \
ghcr.io/italypaleale/revaulter-edit:2 cat /work/secrets.txt
docker run --rm -i \
-v revaulter-trust:/data -v "$PWD:/work" \
-e REVAULTER_SERVER -e REVAULTER_REQUEST_KEY -e REVAULTER_KEY_LABEL \
ghcr.io/italypaleale/revaulter-edit:2 write /work/secrets.txt < secrets.txtTo edit a file on the host, use the same two commands. Only your own editor writes the contents to the host in cleartext:
docker run --rm -v revaulter-trust:/data -v "$PWD:/work" \
-e REVAULTER_SERVER -e REVAULTER_REQUEST_KEY \
ghcr.io/italypaleale/revaulter-edit:2 cat /work/secrets.txt > /tmp/secrets
"$EDITOR" /tmp/secrets
docker run --rm -i -v revaulter-trust:/data -v "$PWD:/work" \
-e REVAULTER_SERVER -e REVAULTER_REQUEST_KEY \
ghcr.io/italypaleale/revaulter-edit:2 write /work/secrets.txt < /tmp/secretsThe container runs as a non-root user (UID 65532), so that user must be able to read and write the mounted files and the trust store volume.
Notes and limitations#
- Each save re-encrypts the whole file with a fresh nonce, but reuses the same content encryption key. Only opening the file requires approval, so a long editing session costs a single approval.
catfails if the file does not exist: onlyeditandwritecreate files.- The file records the server it was encrypted with. If
--serverpoints at a different server,revaulter-editwarns and then fails to unwrap the key, because only the original user’s keys can unwrap it.