Encrypt customer data before it reaches your database
Cipher
Encryption and signing. Your app never holds a key.
Your app sends Cipher a value and stores the ciphertext it gets back, in Postgres, S3, or anywhere else. Keys are created, rotated, and kept inside ShrouDB. Old ciphertext stays readable after a rotation, and REWRAP moves it to the newest key version.
Use it from your app
typescript
import { ShrouDB } from "@shroudb/sdk";
const db = new ShrouDB({
moat: process.env.SHROUDB_URL, // e.g. http://localhost:8200
token: process.env.SHROUDB_TOKEN,
});
const b64 = (s: string) => Buffer.from(s).toString("base64");
// Encrypt before writing to your own database
const { ciphertext } = await db.cipher.encrypt("customers", b64(ssn), {
context: customerId,
});
await sql`UPDATE customers SET ssn = ${ciphertext} WHERE id = ${customerId}`;
// Decrypt when you need the value
const { plaintext } = await db.cipher.decrypt("customers", row.ssn, {
context: customerId,
});What it does
- Keys are generated and stored inside ShrouDB. Your app only sees ciphertext.
- Keyrings rotate on a schedule you set. Old ciphertext keeps decrypting.
- REWRAP re-encrypts existing ciphertext under the newest key version.
- Optional CONTEXT binds a ciphertext to a record, so it can't be swapped onto another row.
- Convergent mode produces the same ciphertext for the same input, for equality lookups.
- Data keys for encrypting large payloads locally (envelope encryption).
- Signing and signature verification.
Turn it on
Cipher runs inside Moat, the ShrouDB server. Add this to moat.tomland restart. It shares Moat's encrypted storage, access tokens, and audit log with the other engines.
moat.toml
[engines.cipher]
enabled = true
[engines.cipher.keyrings.customers]
algorithm = "aes-256-gcm"
rotation_days = 90Commands
Over RESP3, prefix each command with CIPHER. Over HTTP, send POST /v1/cipher {"args": [...]}. The SDKs have a method for each.
| Command | Args | Description |
|---|---|---|
| KEYRING CREATE | <name> <algorithm> [ROTATION <days>] | Create a keyring |
| ENCRYPT | <keyring> <base64> [CONTEXT <aad>] | Encrypt a value |
| DECRYPT | <keyring> <ciphertext> [CONTEXT <aad>] | Decrypt a value |
| REWRAP | <keyring> <ciphertext> | Re-encrypt under the latest key version |
| ROTATE | <keyring> [FORCE] | Create a new key version now |
| GENERATE_DATA_KEY | <keyring> | Get a data key for local encryption |
| SIGN | <keyring> <base64> | Sign data |
| VERIFY_SIGNATURE | <keyring> <base64> <signature> | Check a signature |
| KEY_INFO | <keyring> | Show key versions and algorithm |
Try Cipher locally
The quickstart takes about five minutes.