Search data you've encrypted
Veil
Exact, prefix, contains, and fuzzy search over encrypted fields.
Once an email column is encrypted, you can no longer look anyone up by email. Veil keeps a blind index of keyed tokens instead of plaintext, so you can still search by exact value, prefix, substring, or approximate match. In end-to-end mode the client computes the tokens and the server never sees the value.
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");
await db.veil.indexCreate("customer_emails");
// When you save a customer
await db.veil.put("customer_emails", customerId, b64(email));
// Support looks someone up by part of their email
const { results } = await db.veil.search("customer_emails", "alice@", {
mode: "prefix",
});What it does
- Exact, prefix, contains, and fuzzy matching.
- Stores keyed tokens, never the plaintext value.
- End-to-end mode: the client computes tokens, the server never sees the value.
- Index rotation and rebuild.
- Works alongside Cipher: encrypt the value, index it with Veil.
Turn it on
Veil 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.veil]
enabled = true # uses Cipher, which must also be enabledCommands
Over RESP3, prefix each command with VEIL. Over HTTP, send POST /v1/veil {"args": [...]}. The SDKs have a method for each.
| Command | Args | Description |
|---|---|---|
| INDEX CREATE | <name> | Create a search index |
| PUT | <index> <id> <base64> | Index a value |
| SEARCH | <index> <query> [MODE exact|prefix|contains|fuzzy] | Search |
| DELETE | <index> <id> | Remove an entry |
| INDEX ROTATE | <name> | Rotate the index key |
| INDEX REINDEX | <name> | Rebuild an index |
Try Veil locally
The quickstart takes about five minutes.