Revaulter
GitHub

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#

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 SGVsbG8

Commands#

encrypt#

Submit an encryption request for approval.

revaulter-cli encrypt [flags]
FlagShortRequiredDescription
--server-sYesAddress of the Revaulter server (e.g. https://revaulter.example.com)
--request-key-kYesPer-user request key (shown in the web UI after registration)
--key-label-lYesLogical key label used for key derivation
--algorithm-aYesAEAD algorithm identifier: A256GCM (alias aes-256-gcm) or C20P (alias chacha20-poly1305)
--message-mOne of --message, --input, or --json is requiredThe message to encrypt as a raw UTF-8 string.
--input-iOne of --message, --input, or --json is requiredPath to a file whose bytes will be encrypted; use - to read from stdin
--jsonOne of --message, --input, or --json is requiredPath to a JSON file (or - to read from stdin) of shape {"value":"<base64url>","additionalData":"<base64url>"} (additionaldata is optional)
--aadNoAdditional authenticated data (base64-encoded). Not allowed with --json
--timeout-tNoTimeout for the operation (number of seconds or Go duration, e.g. 5m, 300)
--note-nNoMessage displayed alongside the request (up to 40 chars, alphanumeric and . / _ - only)
--output-oNoWrite the result to a file instead of stdout
--formatNoOutput format: json (only). Encrypt always emits the JSON envelope on stdout
--insecureNoSkip TLS certificate validation
--no-h2cNoDo not attempt HTTP/2 Cleartext when not using TLS
--verbose-VNoShow 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.bin

Example (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]
FlagShortRequiredDescription
--server-sYesAddress of the Revaulter server (e.g. https://revaulter.example.com)
--request-key-kYesPer-user request key (shown in the web UI after registration)
--key-label-lYesLogical key label used for key derivation
--algorithm-aYesAEAD algorithm identifier: A256GCM (alias aes-256-gcm) or C20P (alias chacha20-poly1305). Must match what was used at encryption time
--value-mOne of --value or --json is requiredThe ciphertext to decrypt, base64-encoded
--tag-gRequired when not using --jsonAuthentication tag, base64-encoded
--nonceRequired when not using --jsonNonce/IV, base64-encoded
--aadNoAdditional authenticated data, base64-encoded (only allowed not using --json)
--json-jOne of --value or --json is requiredPath 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-tNoTimeout for the operation (number of seconds or Go duration, e.g. 5m, 300)
--note-nNoMessage displayed alongside the request (up to 40 chars, alphanumeric and . / _ - only)
--output-oNoWrite the result to a file instead of stdout
--formatNoOutput format: json (default — JSON envelope) or raw (write the decrypted plaintext as raw bytes)
--insecureNoSkip TLS certificate validation
--no-h2cNoDo not attempt HTTP/2 Cleartext when not using TLS
--verbose-VNoShow 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 raw

Write 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 raw

sign#

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 digest
  • Ed25519: 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]
FlagShortRequiredDescription
--server-sYesAddress of the Revaulter server
--request-key-kYesPer-user request key
--key-label-lYesLogical key label used for signing-key derivation
--algorithm-aYesSigning algorithm identifier: ES256, Ed25519, or Ed25519ph
--input-iOne of --input or --digest is requiredPath to the message file to sign; use - for stdin. With ES256 and Ed25519ph, the CLI hashes the file’s contents locally.
--digest-dOne of --input or --digest is requiredA 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
--formatNoOutput 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-headerNoJSON 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-tNoTimeout for the operation
--note-nNoMessage displayed alongside the request
--output-oNoWrite the result to a file instead of stdout
--insecureNoSkip TLS certificate validation
--no-h2cNoDo not attempt HTTP/2 Cleartext when not using TLS
--verbose-VNoShow 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.json

Sign 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 d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592

Emit 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.sig

ECDSA signatures are non-deterministic by design (a fresh random k per signature, per FIPS 186-5). Signing the same input twice produces two different but equally valid signatures — this is expected.

Ed25519 signatures 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.

FlagShortRequiredDescription
--server-sYesAddress of the Revaulter server
--request-key-kYesPer-user request key used to authenticate with the server
--trust-storeNoPath to the anchor trust store file (defaults to <user-config-dir>/revaulter-cli/trust.json)
--yes-yNoAccept the anchor fingerprint without prompting (for non-interactive use)
--insecureNoSkip TLS certificate validation
--no-h2cNoDo not attempt HTTP/2 Cleartext when not using TLS
--verbose-VNoShow debug-level logs

Example (interactive — prompts for confirmation):

revaulter-cli trust \
  --server https://revaulter.example.com \
  --request-key AbCdEf0123456789GhIj

Example (non-interactive — pin without prompting):

revaulter-cli trust \
  --server https://revaulter.example.com \
  --request-key AbCdEf0123456789GhIj \
  --yes

The encrypt, decrypt, sign, and ssh-agent commands also accept --trust-store to point at a non-default trust store, and --no-trust-store to skip anchor verification entirely (equivalent to SSH’s StrictHostKeyChecking=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 trust before 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 .

FlagShortRequiredDescription
--server-sYesAddress of the Revaulter server
--request-key-kYesPer-user request key
--key-label-lYesLogical key label for the signing key
--algorithm-aNoSigning algorithm: ES256 (default) or Ed25519. Ed25519ph is not supported by the SSH agent
--socketNoPath 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
--commentNoComment attached to the SSH key (default: revaulter/<key-label>)
--timeout-tNoPer-sign-operation timeout (number of seconds or Go duration; defaults to 5 minutes)
--note-nNoExtra text appended to the approval note shown in the browser (the note is always prefixed with SSH auth)
--trust-storeNoPath to the anchor trust store file
--no-trust-storeNoSkip anchor pinning and hybrid bundle verification (equivalent to SSH’s StrictHostKeyChecking=no)
--insecureNoSkip TLS certificate validation
--no-h2cNoDo not attempt HTTP/2 Cleartext when not using TLS
--verbose-VNoShow debug-level logs

Example:

revaulter-cli ssh-agent \
  --server https://revaulter.example.com \
  --request-key AbCdEf0123456789GhIj \
  --key-label ssh-main \
  --algorithm ES256

On 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.com

To 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 -L

The 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
FlagShortRequiredDescription
--server-sYesAddress of the Revaulter server
--timeout-tNoOverall timeout for the check (e.g. 60s, 2m); defaults to 60s
--insecureNoSkip TLS certificate validation
--no-h2cNoDo not attempt HTTP/2 Cleartext when not using TLS
--verbose-VNoShow debug-level logs

version#

Print the CLI version.

revaulter-cli version

How it works#

When you run revaulter-cli encrypt, decrypt, or sign, the CLI:

  1. Fetches the user’s public encryption keys from the server
  2. Generates an ephemeral ECDH P-256 keypair and an ML-KEM-768 encapsulation
  3. Encrypts the request payload end-to-end to the user’s public keys
  4. Submits the encrypted request to the server
  5. Long-polls for the result
  6. 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 raw writes the decrypted plaintext as raw bytes — useful for piping into other commands or writing binary data to a file with --output.
  • For sign: --format also accepts jws (compact JWS) and raw (the 64-byte r || s signature).

Exit codes#

  • 0 — the operation completed successfully
  • non-zero — the request was denied, canceled, expired, or an error occurred (details are printed to stderr)
Edit this page on GitHub