A post-quantum command-line encryption tool with recipient anonymity and zero-seek streaming.
Experimental and unaudited cryptographic software. Use at your own risk.
# Generate a keypair
velum keygen --out-pub key.pub --out-sec key.sec
# Encrypt a file for multiple recipients
velum encrypt -i document.pdf -o document.pdf.velum \
-r alice.pub -r bob.pub -r carol.pub
# Decrypt (automatically detects format)
velum decrypt -i document.pdf.velum -o document.pdf --secret key.sec
# Stream large files with constant memory
velum encrypt -i bigfile.tar.gz -o bigfile.tar.gz.velum \
-r recipient.pub --streamVELUM is a command-line encryption tool designed for the post-quantum era. It combines the simplicity of tools like age with post-quantum cryptography and advanced features like recipient anonymity and zero-seek streaming.
Post-Quantum Security
- Post-quantum resistant encryption using NIST-standardized algorithms
- Hybrid construction: X25519 + ML-KEM-768 (Kyber)
- Hybrid signatures: Ed25519 + ML-DSA-65 (Dilithium3)
Recipient Anonymity
- No metadata about who can decrypt
- Recipients are unlinkable to observers
- Fast O(1) recipient discovery for decryptors
Zero-Seek Streaming
- Encrypt/decrypt files of any size with constant memory
- Works with pipes:
tar c bigdir | velum encrypt -r key.pub > backup.velum - No temporary files needed
Simple & Secure
- One command to encrypt, one to decrypt
- Automatic format detection
- Secure passphrase handling
- Progress bars with throughput stats
Download the latest release for your platform from GitHub Releases:
# macOS (Apple Silicon)
curl -LO https://github.com/velum-project/velum/releases/latest/download/velum-macos-aarch64
chmod +x velum-macos-aarch64
sudo mv velum-macos-aarch64 /usr/local/bin/velum
# macOS (Intel)
curl -LO https://github.com/velum-project/velum/releases/latest/download/velum-macos-x86_64
chmod +x velum-macos-x86_64
sudo mv velum-macos-x86_64 /usr/local/bin/velumRequires Rust 1.70 or later:
git clone https://github.com/velum-project/velum.git
cd velum
cargo build --release
sudo cp target/release/velum /usr/local/bin/velum keygen --out-pub ~/.velum/identity.pub --out-sec ~/.velum/identity.secYou'll be prompted for a passphrase to protect your secret key. This uses Argon2id with secure defaults (96 MiB memory, 4 iterations).
Share identity.pub with anyone who wants to send you encrypted files.
Keep identity.sec secret and backed up!
# Encrypt for one recipient
velum encrypt -i secrets.txt -o secrets.txt.velum -r alice.pub
# Encrypt for multiple recipients (anyone can decrypt)
velum encrypt -i document.pdf -o document.pdf.velum \
-r alice.pub -r bob.pub -r carol.pub
# Encrypt from stdin to stdout
echo "secret message" | velum encrypt -r key.pub > message.velum# Basic decryption
velum decrypt -i secrets.txt.velum -o secrets.txt --secret identity.sec
# Decrypt from stdin to stdout
cat message.velum | velum decrypt --secret identity.sec
# Auto-detects format (armored text or binary)
velum decrypt -i message.velum --secret identity.sec# Sign while encrypting
velum encrypt -i document.pdf -o document.pdf.velum \
-r recipient.pub \
--signer-secret signer.sec
# Verify signature while decrypting
velum decrypt -i document.pdf.velum -o document.pdf \
--secret recipient.sec \
--expect-public signer.pub
# Output:
# [sig] VERIFIED# Interactive (prompts for passphrase)
velum keygen --out-pub key.pub --out-sec key.sec
# With passphrase from environment
export VELUM_PASS="my-secure-passphrase"
velum keygen --out-pub key.pub --out-sec key.sec
# Output to stdout
velum keygen > keys.txt# Basic file encryption
velum encrypt -i file.txt -o file.txt.velum -r recipient.pub
# Multiple recipients
velum encrypt -i file.txt -o file.txt.velum \
-r alice.pub -r bob.pub -r carol.pub
# Streaming mode (for large files)
velum encrypt -i bigfile.tar.gz -o bigfile.tar.gz.velum \
-r recipient.pub --stream
# Custom chunk size (default: 4 MiB)
velum encrypt -i file.bin -o file.bin.velum \
-r recipient.pub --stream --chunk-size 1048576 # 1 MiB
# ASCII-armored output (non-streaming only)
velum encrypt -i message.txt -o message.txt.velum \
-r recipient.pub --armor
# Sign the message
velum encrypt -i file.txt -o file.txt.velum \
-r recipient.pub \
--signer-secret signer.sec --signer-pass "passphrase"
# Pipe usage
tar czf - /path/to/dir | velum encrypt -r backup.pub > backup.tar.gz.velum# Basic decryption
velum decrypt -i file.velum -o file.txt --secret identity.sec
# With passphrase from environment
export VELUM_PASS="my-passphrase"
velum decrypt -i file.velum -o file.txt --secret identity.sec
# Verify signature
velum decrypt -i file.velum -o file.txt \
--secret recipient.sec \
--expect-public signer.pub
# Auto-detect format (default behavior)
velum decrypt -i message.velum --secret identity.sec
# Force format interpretation
velum decrypt -i message --secret identity.sec --binary-input
velum decrypt -i message --secret identity.sec --armor-input
# Pipe usage
cat backup.tar.gz.velum | velum decrypt --secret backup.sec | tar xzf -# Change passphrase (keeps same keys)
velum rewrap --input-secret identity.sec --output identity_new.sec
# Increase security (more memory/iterations)
velum rewrap --input-secret identity.sec --output identity_new.sec \
--m-mib 128 --t-cost 5
# Decrease security (faster, less secure - not recommended)
velum rewrap --input-secret identity.sec --output identity_new.sec \
--m-mib 64 --t-cost 3# Encrypt directory before uploading
tar czf - ~/Documents | \
velum encrypt -r backup.pub --stream > \
documents_$(date +%Y%m%d).tar.gz.velum
# Upload to S3/cloud storage
aws s3 cp documents_20250126.tar.gz.velum s3://my-backups/
# Restore later
aws s3 cp s3://my-backups/documents_20250126.tar.gz.velum - | \
velum decrypt --secret backup.sec | \
tar xzf - -C ~/Restored# Alice encrypts for Bob and Carol
velum encrypt -i report.pdf -o report.pdf.velum \
-r bob.pub -r carol.pub \
--signer-secret alice.sec
# Bob decrypts and verifies Alice's signature
velum decrypt -i report.pdf.velum -o report.pdf \
--secret bob.sec \
--expect-public alice.pub
# Output: [sig] VERIFIED# Daily backup script
#!/bin/bash
DATE=$(date +%Y%m%d)
pg_dump mydb | gzip | \
velum encrypt -r backup.pub --stream > \
"/backups/mydb_${DATE}.sql.gz.velum"
# Restore
velum decrypt -i mydb_20250126.sql.gz.velum --secret backup.sec | \
gunzip | psql mydb# Encrypt before committing sensitive config
velum encrypt -i config.yaml -o config.yaml.velum -r team.pub
git add config.yaml.velum
git commit -m "Add encrypted config"
# Decrypt in production
velum decrypt -i config.yaml.velum -o config.yaml --secret prod.secVELUM supports two wire formats that are automatically detected:
Human-readable, copy-pasteable format:
-----BEGIN VELUM MESSAGE-----
v:1
enc_ecdh:kH8p2vGxN...
nonce:mJ7FqR...
recipients:wK3nP...
ct:xR2vN...
signature:pQ8mL...
-----END VELUM MESSAGE-----
When to use:
- Embedding in emails or documents
- Storing in text-based systems
- Copy-paste scenarios
Compact binary format for efficient storage:
VLM1 [version] [flags] [header_len]
[ephemeral_key] [nonce] [commitment]
[recipients] [signature]
[ciphertext+tag or chunked frames]
When to use:
- File encryption (default for
--stream) - Network protocols
- Space-constrained environments
| Operation | Time | Throughput |
|---|---|---|
| Key Generation | ~230 ms | - |
| Encrypt (1 MB, 1 recipient, unsigned) | - | 214 MB/s |
| Decrypt (1 MB, 1 recipient, unsigned) | - | 202 MB/s |
| Encrypt (1 MB, 1 recipient, signed) | - | 109 MB/s |
| Decrypt (1 MB, 1 recipient, signed) | - | 118 MB/s |
Key generation is intentionally slow due to Argon2id (memory-hard password hashing)
# Non-streaming: 2× file size in memory
velum encrypt -i 1GB.bin -o 1GB.bin.velum -r key.pub
# Memory: ~2 GB
# Streaming: constant memory regardless of file size
velum encrypt -i 1GB.bin -o 1GB.bin.velum -r key.pub --stream
# Memory: ~6.5 MB (chunk size + 2.5 MB overhead)Streaming vs. Non-Streaming:
- Use
--streamfor files > 100 MB - Use
--streamwhen piping or working with unknown sizes - Skip
--streamfor small files (faster, can use--armor)
Chunk Size Selection:
- 64 KiB - Network streaming, low latency
- 1 MiB - Balanced for most use cases
- 4 MiB - Default, best throughput (recommended)
| Component | Algorithm | Security Level |
|---|---|---|
| KEM (Classical) | X25519 | ~128-bit |
| KEM (Post-Quantum) | ML-KEM-768 (Kyber) | NIST Level 3 (~192-bit) |
| Signature (Classical) | Ed25519 | ~128-bit |
| Signature (Post-Quantum) | ML-DSA-65 (Dilithium3) | NIST Level 3 (~192-bit) |
| Content Encryption | XChaCha20-Poly1305 | 256-bit |
| Keystore Encryption | AES-256-GCM-SIV | 256-bit |
| Password Hashing | Argon2id | Configurable |
✅ DO:
- Use strong passphrases (≥20 characters, high entropy)
- Keep secret keys on encrypted storage
- Back up your secret key securely
- Use
--streamfor large files - Verify signatures with
--expect-publicwhen trust matters
❌ DON'T:
- Share your secret key (only share
.pubfiles) - Use weak passphrases
- Store passphrases in shell history (use
VELUM_PASSor prompts) - Encrypt to more than ~10 recipients (performance degradation)
- Lower Argon2id parameters below defaults
Protected Against:
- Quantum computer attacks (via post-quantum algorithms)
- Passive eavesdropping
- Active message tampering
- Recipient identification by observers
- Chosen-ciphertext attacks
Not Protected Against:
- Endpoint compromise (malware, keyloggers)
- Weak passphrases (brute-force attacks)
- Replay attacks (add timestamps at application level)
- Side-channel attacks on the host system
Known Trade-offs:
- Index hints enable statistical recipient linking across multiple messages (performance vs. perfect anonymity)
- Static long-term keys (compromise breaks past message confidentiality)
See SECURITY.md for detailed security documentation.
| Feature | VELUM | age | GPG |
|---|---|---|---|
| Post-Quantum | ✅ | ❌ | ❌ |
| Recipient Anonymity | ✅ | ❌ | ❌ |
| Multi-Recipient | ✅ | ✅ | ✅ |
| Streaming | ✅ | ✅ | ❌ |
| Signatures | ✅ | ❌ | ✅ |
| ASCII Armor | ✅ | ✅ | ✅ |
| Simplicity | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ |
# Skip passphrase prompts
export VELUM_PASS="my-passphrase"
velum decrypt -i file.velum --secret identity.sec
# For signing operations
export VELUM_SIGN_PASS="signer-passphrase"
velum encrypt -i file.txt -r key.pub --signer-secret signer.sec# High security (slow, 128 MiB memory)
velum rewrap --input-secret identity.sec --output identity_strong.sec \
--m-mib 128 --t-cost 5 --parallelism 4
# Fast unlock (less secure, 32 MiB memory)
velum rewrap --input-secret identity.sec --output identity_fast.sec \
--m-mib 32 --t-cost 2 --parallelism 2# Default limit: 512 recipients
velum encrypt -i file.txt -o file.velum -r recipient*.pub
# Override limit (not recommended - performance impact)
velum encrypt -i file.txt -o file.velum -r recipient*.pub \
--allow-many-recipientsProgress bars are shown automatically for streaming encryption/decryption.
Example output:
[enc] 45.2% | 452.0 MiB / 1000.0 MiB | 125.3 MiB/s
Disable by redirecting stderr:
velum encrypt -i file.bin --stream 2>/dev/nullPossible causes:
- Wrong passphrase
- Corrupted ciphertext
- Not a valid recipient
Solutions:
# Verify your passphrase works
velum rewrap --input-secret identity.sec --output /dev/null
# Check file integrity
sha256sum message.velum
# Verify you're in the recipient list (should succeed if you are)
velum decrypt -i message.velum --secret identity.sec > /dev/nullPossible causes:
- Message was tampered with
- Wrong public key provided
Solutions:
# Try without signature verification
velum decrypt -i message.velum --secret identity.sec
# Check if signature is present
grep -q "signature:" message.velum && echo "Has signature" || echo "No signature"For large files:
# Use streaming mode
velum encrypt -i largefile.bin -o largefile.velum \
-r key.pub --stream
# Reduce chunk size
velum encrypt -i largefile.bin -o largefile.velum \
-r key.pub --stream --chunk-size 1048576 # 1 MiBWhen piping to commands that exit early (like head), you may see:
Error: Broken pipe (os error 32)
This is expected behavior and indicates the receiving process closed the pipe. The data up to that point was successfully transmitted.
#!/bin/bash
set -euo pipefail
# Configuration
RECIPIENT_KEY="/path/to/recipient.pub"
SIGNER_KEY="/path/to/signer.sec"
# Encrypt all .txt files in a directory
for file in /data/*.txt; do
velum encrypt -i "$file" -o "${file}.velum" \
-r "$RECIPIENT_KEY" \
--signer-secret "$SIGNER_KEY"
done# /etc/systemd/system/velum-backup.service
[Unit]
Description=Daily encrypted backup with VELUM
After=network.target
[Service]
Type=oneshot
Environment=VELUM_PASS=your-passphrase
ExecStart=/usr/local/bin/backup.sh
[Install]
WantedBy=multi-user.targetVELUM can also be used as a Rust library:
[dependencies]
velum = "0.1.0"use velum::{generate_keypair, encrypt, decrypt};
let (public, secret) = generate_keypair("passphrase")?;
let ciphertext = encrypt(b"data", &public, None)?;
let (plaintext, _) = decrypt(&ciphertext, &secret, "passphrase", None)?;- Rust 1.70 or later
- Cargo
- Git
# Clone repository
git clone https://github.com/velum-project/velum.git
cd velum
# Build release binary
cargo build --release
# Install locally
cargo install --path .
# Run tests
cargo test
# Run with optimizations for your CPU
RUSTFLAGS="-C target-cpu=native" cargo build --release# Linux → Windows
cargo build --release --target x86_64-pc-windows-gnu
# Linux → macOS (requires osxcross)
cargo build --release --target x86_64-apple-darwin
# Using cross
cargo install cross
cross build --release --target aarch64-unknown-linux-gnuVELUM is licensed under the Apache 2.0 License.
VELUM builds on excellent cryptographic libraries:
- pqcrypto - Post-quantum primitives
- RustCrypto - Pure Rust cryptography
- dalek-cryptography - Curve25519 & Ed25519
- Argon2 - Password hashing
Special thanks to:
- The NIST Post-Quantum Cryptography standardization project
- The age project for inspiration
- All contributors and security researchers
Email: [email protected]
PGP Fingerprint:
3720 D73C 646E 1F22 923E 9014 ADC4 2172 FBA8 3645
For security vulnerabilities, please see SECURITY.md for reporting procedures.