Using the Revaulter CLI
The Revaulter CLI (revaulter-cli) is the primary way to submit encrypt, decrypt, and sign requests. It ensures requests are End-to-End Encrypted as they are sent to the server.
Installing the CLI#
Pre-compiled binary (recommended)#
Download the latest release from GitHub Releases
. Binaries are available for Linux (amd64, arm64), macOS (amd64, arm64), and Windows.
Docker#
docker run --rm ghcr.io/italypaleale/revaulter-cli:2 <command> [flags]For example:
docker run --rm ghcr.io/italypaleale/revaulter-cli:2 encrypt \
--server https://revaulter.example.com \
--request-key AbCdEf0123456789GhIj \
--key-label my-key \
--algorithm A256GCM \
--value SGVsbG8Commands#
encrypt#
Submit an encryption request for approval.
revaulter-cli encrypt [flags]| Flag | Short | Required | Description |
|---|---|---|---|
--server | -s | Yes | Address of the Revaulter server (e.g. https://revaulter.example.com) |
--request-key | -k | Yes | Per-user request key (shown in the web UI after registration) |
--key-label | -l | Yes | Logical key label used for key derivation |
--algorithm | -a | Yes | AEAD algorithm identifier: A256GCM (alias aes-256-gcm) or C20P (alias chacha20-poly1305) |
--message | -m | One of --message, --input, or --json is required | The message to encrypt as a raw UTF-8 string. |
--input | -i | One of --message, --input, or --json is required | Path to a file whose bytes will be encrypted; use - to read from stdin |
--json | One of --message, --input, or --json is required | Path to a JSON file (or - to read from stdin) of shape {"value":"<base64url>","additionalData":"<base64url>"} (additionaldata is optional) | |
--aad | No | Additional authenticated data (base64-encoded). Not allowed with --json | |
--timeout | -t | No | Timeout for the operation (number of seconds or Go duration, e.g. 5m, 300) |
--note | -n | No | Message displayed alongside the request (up to 40 chars, alphanumeric and . / _ - only) |
--output | -o | No | Write the result to a file instead of stdout |
--format | No | Output format: json (only). Encrypt always emits the JSON envelope on stdout | |
--insecure | No | Skip TLS certificate validation | |
--no-h2c | No | Do not attempt HTTP/2 Cleartext when not using TLS | |
--verbose | -V | No | Show debug-level logs |
Example (raw string):
revaulter-cli encrypt \
--server https://revaulter.example.com \
--request-key AbCdEf0123456789GhIj \
--key-label boot-disk \
--algorithm A256GCM \
--message "Hello, world" \
--note "boot unlock"Example (read plaintext from a file):
revaulter-cli encrypt \
--server https://revaulter.example.com \
--request-key AbCdEf0123456789GhIj \
--key-label boot-disk \
--algorithm A256GCM \
--input ./secret.binExample (JSON input):
echo '{"value":"SGVsbG8"}' | revaulter-cli encrypt \
--server https://revaulter.example.com \
--request-key AbCdEf0123456789GhIj \
--key-label boot-disk \
--algorithm A256GCM \
--json -decrypt#
Submit a decryption request for approval.
revaulter-cli decrypt [flags]| Flag | Short | Required | Description |
|---|---|---|---|
--server | -s | Yes | Address of the Revaulter server (e.g. https://revaulter.example.com) |
--request-key | -k | Yes | Per-user request key (shown in the web UI after registration) |
--key-label | -l | Yes | Logical key label used for key derivation |
--algorithm | -a | Yes | AEAD algorithm identifier: A256GCM (alias aes-256-gcm) or C20P (alias chacha20-poly1305). Must match what was used at encryption time |
--value | -m | One of --value or --json is required | The ciphertext to decrypt, base64-encoded |
--tag | -g | Required when not using --json | Authentication tag, base64-encoded |
--nonce | Required when not using --json | Nonce/IV, base64-encoded | |
--aad | No | Additional authenticated data, base64-encoded (only allowed not using --json) | |
--json | -j | One of --value or --json is required | Path to a JSON file (or - to read from stdin) in the shape produced by encrypt ({"value":"<base64url>","nonce":"<base64url>","tag":"<base64url>","additionalData":"<base64url>"}). Mutually exclusive with --value, --tag, --nonce, and --aad |
--timeout | -t | No | Timeout for the operation (number of seconds or Go duration, e.g. 5m, 300) |
--note | -n | No | Message displayed alongside the request (up to 40 chars, alphanumeric and . / _ - only) |
--output | -o | No | Write the result to a file instead of stdout |
--format | No | Output format: json (default — JSON envelope) or raw (write the decrypted plaintext as raw bytes) | |
--insecure | No | Skip TLS certificate validation | |
--no-h2c | No | Do not attempt HTTP/2 Cleartext when not using TLS | |
--verbose | -V | No | Show debug-level logs |
Example:
revaulter-cli decrypt \
--server https://revaulter.example.com \
--request-key AbCdEf0123456789GhIj \
--key-label boot-disk \
--algorithm A256GCM \
--value <base64-ciphertext> \
--nonce <base64-nonce> \
--tag <base64-tag>Example (JSON input — pipes encrypt’s output back in):
revaulter-cli encrypt --message "secret" ... \
| revaulter-cli decrypt \
--server https://revaulter.example.com \
--request-key AbCdEf0123456789GhIj \
--key-label boot-disk \
--algorithm A256GCM \
--json - \
--format rawWrite result to a file (“raw” format):
revaulter-cli decrypt \
--server https://revaulter.example.com \
--request-key AbCdEf0123456789GhIj \
--key-label boot-disk \
--algorithm A256GCM \
--value <base64-ciphertext> \
--nonce <base64-nonce> \
--tag <base64-tag> \
--output /tmp/decrypted.bin \
--format rawsign#
Submit a signing request for approval. The CLI derives the bytes sent to the browser from the selected signing algorithm:
ES256: the CLI hashes the input with SHA-256 and sends the 32-byte digestEd25519: the CLI sends the raw message bytes (note the max message size of 100KB)Ed25519ph: the CLI hashes the input with SHA-512 and sends the 64-byte digest
revaulter-cli sign [flags]| Flag | Short | Required | Description |
|---|---|---|---|
--server | -s | Yes | Address of the Revaulter server |
--request-key | -k | Yes | Per-user request key |
--key-label | -l | Yes | Logical key label used for signing-key derivation |
--algorithm | -a | Yes | Signing algorithm identifier: ES256, Ed25519, or Ed25519ph |
--input | -i | One of --input or --digest is required | Path to the message file to sign; use - for stdin. With ES256 and Ed25519ph, the CLI hashes the file’s contents locally. |
--digest | -d | One of --input or --digest is required | A pre-computed digest, encoded as hex or base64url. Supported only for ES256 (32-byte SHA-256) and Ed25519ph (64-byte SHA-512). Mutually exclusive with --format jws |
--format | No | Output format: json (default — JSON envelope with base64url signature), jws (compact JWS string), or raw (the raw 64-byte signature). jws requires --input and is supported only for ES256 and Ed25519 | |
--jws-header | No | JSON fragment merged into the default protected header when building a JWS from --input. The alg field is always forced to ES256 or EdDSA, depending on --algorithm | |
--timeout | -t | No | Timeout for the operation |
--note | -n | No | Message displayed alongside the request |
--output | -o | No | Write the result to a file instead of stdout |
--insecure | No | Skip TLS certificate validation | |
--no-h2c | No | Do not attempt HTTP/2 Cleartext when not using TLS | |
--verbose | -V | No | Show debug-level logs |
Examples:
Sign a file (default output is a JSON envelope containing the base64url r || s signature):
revaulter-cli sign \
--server https://revaulter.example.com \
--request-key AbCdEf0123456789GhIj \
--key-label release-signing \
--algorithm ES256 \
--input manifest.jsonSign data piped from stdin:
echo "hello" | revaulter-cli sign \
--server https://revaulter.example.com \
--request-key AbCdEf0123456789GhIj \
--key-label release-signing \
--algorithm ES256 \
--input -Sign a pre-computed SHA-256 digest (useful when integrating with other tooling that already hashes):
revaulter-cli sign \
--server https://revaulter.example.com \
--request-key AbCdEf0123456789GhIj \
--key-label release-signing \
--algorithm ES256 \
--digest d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592Emit a compact JWS over a file, merging a custom kid into the protected header:
revaulter-cli sign \
--server https://revaulter.example.com \
--request-key AbCdEf0123456789GhIj \
--key-label release-signing \
--algorithm ES256 \
--input manifest.json \
--format jws \
--jws-header '{"kid":"release-signing-2026"}'Write just the raw 64-byte r || s signature to a file (useful for pipelines that verify with a separate tool):
revaulter-cli sign \
--server https://revaulter.example.com \
--request-key AbCdEf0123456789GhIj \
--key-label release-signing \
--algorithm ES256 \
--input manifest.json \
--format raw \
--output manifest.sigECDSA signatures are non-deterministic by design (a fresh random
kper signature, per FIPS 186-5). Signing the same input twice produces two different but equally valid signatures — this is expected.
Ed25519signatures are deterministic for a fixed key and message.
trust#
Pin a Revaulter server’s anchor public keys in the local trust store using TOFU (Trust On First Use).
revaulter-cli trust [flags]The CLI fetches the server’s hybrid anchor bundle (ES384 + ML-DSA-87), verifies its signatures, and pins the anchor fingerprint. Subsequent commands (encrypt, decrypt, sign, ssh-agent) verify the pinned anchor and refuse to proceed if it changes unexpectedly. If the anchor is already pinned and matches, the command verifies it and exits successfully.
Run this once when first connecting to a server. In interactive use the CLI displays the fingerprint and prompts for confirmation. For non-interactive use, pass --yes to automatically accept the key (you will not be able to verify the key, however)
The default trust store path is <user-config-dir>/revaulter-cli/trust.json (e.g. ~/.config/revaulter-cli/trust.json on Linux). Override it with the TRUST_STORE_PATH environment variable or the --trust-store flag.
| Flag | Short | Required | Description |
|---|---|---|---|
--server | -s | Yes | Address of the Revaulter server |
--request-key | -k | Yes | Per-user request key used to authenticate with the server |
--trust-store | No | Path to the anchor trust store file (defaults to <user-config-dir>/revaulter-cli/trust.json) | |
--yes | -y | No | Accept the anchor fingerprint without prompting (for non-interactive use) |
--insecure | No | Skip TLS certificate validation | |
--no-h2c | No | Do not attempt HTTP/2 Cleartext when not using TLS | |
--verbose | -V | No | Show debug-level logs |
Example (interactive — prompts for confirmation):
revaulter-cli trust \
--server https://revaulter.example.com \
--request-key AbCdEf0123456789GhIjExample (non-interactive — pin without prompting):
revaulter-cli trust \
--server https://revaulter.example.com \
--request-key AbCdEf0123456789GhIj \
--yesThe
encrypt,decrypt,sign, andssh-agentcommands also accept--trust-storeto point at a non-default trust store, and--no-trust-storeto skip anchor verification entirely (equivalent to SSH’sStrictHostKeyChecking=no).
ssh-agent#
Run a local SSH agent that routes signing requests through Revaulter. Available on Unix systems only (Linux, macOS, BSD).
revaulter-cli ssh-agent [flags]Starts a Unix-socket SSH agent. SSH clients with SSH_AUTH_SOCK pointed at the socket route each signing request through Revaulter, where the key holder approves it in the browser with a passkey. The private signing key is never exported: only public keys are stored on the server, and only signatures are returned to the agent. The signing public key for the chosen --key-label must already exist: create it from the web UI under Settings → Signing keys.
Note: Pin the server anchor with
revaulter-cli trustbefore running the agent, especially in non-interactive environments.
For a full setup walkthrough (including installing the public key in authorized_keys), see Authenticate to SSH servers
.
| Flag | Short | Required | Description |
|---|---|---|---|
--server | -s | Yes | Address of the Revaulter server |
--request-key | -k | Yes | Per-user request key |
--key-label | -l | Yes | Logical key label for the signing key |
--algorithm | -a | No | Signing algorithm: ES256 (default) or Ed25519. Ed25519ph is not supported by the SSH agent |
--socket | No | Path to the Unix socket (defaults to $XDG_RUNTIME_DIR/revaulter/ssh-agent-<key-label>.sock, or a private per-user directory under $TMPDIR if XDG_RUNTIME_DIR is unset). The socket is created with 0600 permissions | |
--comment | No | Comment attached to the SSH key (default: revaulter/<key-label>) | |
--timeout | -t | No | Per-sign-operation timeout (number of seconds or Go duration; defaults to 5 minutes) |
--note | -n | No | Extra text appended to the approval note shown in the browser (the note is always prefixed with SSH auth) |
--trust-store | No | Path to the anchor trust store file | |
--no-trust-store | No | Skip anchor pinning and hybrid bundle verification (equivalent to SSH’s StrictHostKeyChecking=no) | |
--insecure | No | Skip TLS certificate validation | |
--no-h2c | No | Do not attempt HTTP/2 Cleartext when not using TLS | |
--verbose | -V | No | Show debug-level logs |
Example:
revaulter-cli ssh-agent \
--server https://revaulter.example.com \
--request-key AbCdEf0123456789GhIj \
--key-label ssh-main \
--algorithm ES256On startup the agent prints an export SSH_AUTH_SOCK=... line on stderr; evaluate it in the shell where you will use ssh:
export SSH_AUTH_SOCK='/run/user/1000/revaulter/ssh-agent-ssh-main.sock'
ssh user@prod.example.comTo list the SSH public key managed by the agent (for example to copy into ~/.ssh/authorized_keys):
SSH_AUTH_SOCK=/run/user/1000/revaulter/ssh-agent-ssh-main.sock ssh-add -LThe agent stops on Ctrl-C or SIGTERM and removes its socket on exit.
check#
Verify that a Revaulter server is serving unmodified web client assets, signed by this repo’s release workflow. See Verifying the web client’s integrity for a deeper explanation of the trust model and when to run this.
revaulter-cli check --server https://revaulter.example.com| Flag | Short | Required | Description |
|---|---|---|---|
--server | -s | Yes | Address of the Revaulter server |
--timeout | -t | No | Overall timeout for the check (e.g. 60s, 2m); defaults to 60s |
--insecure | No | Skip TLS certificate validation | |
--no-h2c | No | Do not attempt HTTP/2 Cleartext when not using TLS | |
--verbose | -V | No | Show debug-level logs |
version#
Print the CLI version.
revaulter-cli versionHow it works#
When you run revaulter-cli encrypt, decrypt, or sign, the CLI:
- Fetches the user’s public encryption keys from the server
- Generates an ephemeral ECDH P-256 keypair and an ML-KEM-768 encapsulation
- Encrypts the request payload end-to-end to the user’s public keys
- Submits the encrypted request to the server
- Long-polls for the result
- Decrypts the response envelope locally using its ephemeral private key
The server never has access to the plaintext request or response data.
Output#
By default, the CLI writes a JSON envelope (--format json) to stdout after decrypting the response:
{
"value": "<base64>"
}- For
decrypt:--format rawwrites the decrypted plaintext as raw bytes — useful for piping into other commands or writing binary data to a file with--output. - For
sign:--formatalso acceptsjws(compact JWS) andraw(the 64-byter || ssignature).
Exit codes#
- 0 — the operation completed successfully
- non-zero — the request was denied, canceled, expired, or an error occurred (details are printed to stderr)