rypt.sh 0.1.1
Encrypt files from a shell.
The rypt command line: one POSIX shell script that needs curl and base64, runs on Linux and macOS, and keeps your API key off the command line.
Install
One line, no sudo
curl -fsSL https://sh.rypt.dev/install | sh
The installer puts rypt in ~/.local/bin, then asks for your API key, with the input hidden, and saves it where only you can read it. Get an API key at dashboard.rypt.dev first, or press Enter to skip and add it later. Run the same line again to update.
The installer checks the script against its published SHA-256 before installing it, and never takes the API key as an argument, so the key stays out of your shell history. Read it first at sh.rypt.dev/install.
In CI
Pass the key from your CI's secret store in RYPT_API_KEY. The installer saves it and asks nothing. If the secret is missing, so the variable is empty, the install step fails rather than a later one.
curl -fsSL https://sh.rypt.dev/install | RYPT_API_KEY="$RYPT_API_KEY" sh
By hand
Download the script, read it, check it, and put it on your PATH yourself.
curl -fsSLO https://sh.rypt.dev/rypt.sh curl -fsSLO https://sh.rypt.dev/rypt.sh.sha256 sha256sum -c rypt.sh.sha256 # Linux shasum -a 256 -c rypt.sh.sha256 # macOS mkdir -p ~/.local/bin && install -m 755 rypt.sh ~/.local/bin/rypt
The checksum catches a corrupted or truncated download. It comes from the same server as the script, so it is not a signature. The script itself is at sh.rypt.dev/rypt.sh.
API key
Where your API key lives
The installer saves your API key to ~/.config/rypt/api-key, readable only by you. To add or change it by hand, paste it this way, which keeps it off the screen and out of your shell history (it works from bash, zsh and fish alike):
mkdir -p ~/.config/rypt
sh -c 'umask 077; trap "stty echo; echo" EXIT; trap "exit 130" INT; stty -echo; head -n 1 > ~/.config/rypt/api-key'
# paste the API key and press Enter; it is not shown
The script also reads RYPT_API_KEY from the environment, and RYPT_API_KEY_FILE names a different key file. An API key can create, rotate and delete every key in your account, so treat it like a password.
Use
Files in, files out, or a pipe
# list the keys in your account rypt keys # encrypt a file: writes customers.csv.enc rypt encrypt --key orders customers.csv # decrypt it: writes customers.csv rypt decrypt --key orders customers.csv.enc # or use stdin and stdout printf 'hello world' | rypt encrypt --key orders > hello.enc rypt decrypt --key orders < hello.enc
--key takes a key's name or its id. New files are readable only by you, and an existing file is never replaced unless you pass --force. A .enc file holds the ciphertext exactly as the API returned it: one line of base64.
Reference
Commands, settings and exit codes
| Command | What it does |
|---|---|
rypt encrypt [FILE] | Encrypts FILE, or stdin, with the key you name. Writes FILE.enc, or stdout. |
rypt decrypt [FILE.enc] | Decrypts FILE.enc, or stdin. Writes FILE without the .enc, or stdout. |
rypt keys | Lists your account's keys: id, tier and name. |
rypt help, rypt version | Prints usage, or the version. |
| Option | Meaning |
|---|---|
-k, --key KEY | The key's name or id. Defaults to RYPT_KEY. |
--aad TEXT | Binds the ciphertext to a context, such as a row id. Decrypt needs the same text. |
-o, --output OUT | Writes to OUT instead of the default. -o - means stdout. |
-f, --force | Replaces an existing output file. |
| Variable | Meaning |
|---|---|
RYPT_API_KEY | The API key. Used before the key file. |
RYPT_API_KEY_FILE | A file holding the API key. Default ~/.config/rypt/api-key. |
RYPT_KEY | The default for --key. |
RYPT_API_URL | The API to call. Default https://api.rypt.dev. Plain http is refused except on localhost. |
| Exit code | Meaning |
|---|---|
| 0 | Done. |
| 1 | The API refused the request. The message names the error code and the request_id. |
| 2 | Usage: a missing key, a bad option, a missing API key. |
| 3 | Local: a file, the size limit, the network. |
encrypt takes at most 65,536 bytes, or 8,192 including --aad on a Hardware key, and --aad at most 65,536. The API takes 128 KiB per request, so a large input with a long --aad is refused before it is sent: its ciphertext could not be sent back to decrypt. For anything larger, use envelope mode through the API.
Secrets
What it does with your key and your data
- The API key never goes on a command line. curl reads the Authorization header from a pipe on a file descriptor, so
pscannot show it, and curl ignores your~/.curlrc, so no setting there can print it.RYPT_API_KEYis removed from the environment before any other program starts. - Plaintext moves through pipes. It never becomes an argument to any program.
- Everything is checked before it is sent. The API key, the key id and the ciphertext read from a file must match their exact formats, so a doctored file or name cannot change the request.
- Files are private and never clobbered. Output goes to a new mode-600 file, created under a random name beside the destination and linked into place when it is complete. An existing file is kept unless you pass
--force, and a bad output path is caught before any request is sent. - Nothing leaks by accident. Tracing with
sh -x, core dumps and curl's TLS key log are switched off, and control characters in anything the server sends are removed before it is printed. - The installer never takes the API key as an argument. It asks with the input hidden, or reads
RYPT_API_KEYin CI, and saves the key with mode 600. It checks the script's SHA-256 before installing, never uses sudo, and does nothing if its own download was cut short. - It talks to one place. Only the rypt API, over HTTPS; plain http is refused except on localhost, and a proxy setting never sees a localhost request. There is no telemetry and no update check.
Your plaintext still reaches rypt, which encrypts it and holds the key. The security page says where keys live and what rypt sees.