diff --git a/content/docs/concepts/compare/aws-kms.mdx b/content/docs/concepts/compare/aws-kms.mdx
new file mode 100644
index 0000000..9b3a568
--- /dev/null
+++ b/content/docs/concepts/compare/aws-kms.mdx
@@ -0,0 +1,211 @@
+---
+title: CipherStash vs AWS KMS
+description: How CipherStash compares to AWS KMS for application-level encryption — the distributed-trust ZeroKMS key-management architecture, plus a developer-experience comparison covering searchable encryption, identity-aware keys, bulk operations, and type safety.
+type: concept
+components: [platform]
+audience: [developer, cto, ciso]
+reviewBy: "2027-01-05"
+---
+
+CipherStash and AWS KMS solve overlapping problems differently. AWS KMS is a centralized key-management service: keys live in AWS-controlled HSMs, and it returns binary ciphertext that you encode and store yourself. CipherStash encrypts application data through a developer SDK backed by **ZeroKMS**, a key-management service that issues a unique key per record and never holds the keys needed to decrypt your data.
+
+This page compares them on two axes: the **architecture and trust model**, and the **developer experience** of encrypting application data.
+
+## What's being compared
+
+| Term | What it is |
+|---|---|
+| **AWS KMS** | AWS's managed key-management service. Keys live in AWS-controlled HSMs; you call `Encrypt`/`Decrypt` and manage ciphertext storage yourself. |
+| **ZeroKMS** | CipherStash's key-management service. Derives a unique key per record on demand from client- and server-side components; no party — including CipherStash — holds a complete data key. |
+| **CipherStash Encryption SDK** (`@cipherstash/stack`) | The TypeScript library that encrypts/decrypts application data and runs searchable queries, backed by ZeroKMS. |
+| **CipherStash Proxy** | A drop-in SQL proxy that applies the same primitives server-side with no application code changes. |
+
+The SDK and Proxy are two deployment shapes of the same primitives; the examples below use the SDK.
+
+## Architecture & trust model
+
+AWS KMS provides managed key management with high availability and deep AWS integration, but key custody and the underlying hardware remain with AWS. Two properties matter for a security evaluation:
+
+- **Shared keys, larger blast radius.** A KMS key (or a derived data key) typically protects many records, so a single key compromise exposes everything it covers.
+- **Provider custody.** AWS controls the HSMs and the IAM boundary around them, so your trust model includes AWS.
+
+ZeroKMS takes a different approach:
+
+- **Unique key per record.** Every value gets its own key, so a compromised key exposes a single record rather than a dataset.
+- **Distributed trust.** Keys are derived on demand by combining client- and server-side components and are never stored. No single party — including CipherStash — can decrypt data unilaterally (CipherStash calls this *zero-knowledge* key management).
+- **Identity-bound access.** Decryption is gated by OIDC identity, with per-record access control, instant revocation, and a real-time audit log of every access.
+- **Performance.** Sub-5ms overhead for most operations ([latency benchmarks](https://app.artillery.io/share/sh_75edb9d22a060633bfdce04777ffd60d1bcfc88989393dc38d3a4924b83fc6cd)). Because ZeroKMS processes a whole batch in one round-trip while AWS KMS has no bulk API, the gap widens with batch size: at a realistic 100-record batch an in-region benchmark measured **~16× lower p95 latency** and, under sustained load, **roughly two orders of magnitude (~85×) higher throughput** before AWS KMS hits its rate limit.
+{/* TODO: ~16× latency / ~85× throughput are from the in-region two-host benchmark in cipherstash/benches (kms-app/REPORT.md). The throughput multiple is at default per-region KMS quotas. Replace with a public-facing citation before relying on the exact figures externally, as the FHE page does for its ORE numbers. */}
+- **Deployment.** Managed service across regions, or self-hosted as a container for jurisdictional control.
+
+The same architecture comparison applies to Google Cloud KMS and Azure Key Vault, which share the provider-custody, shared-key model.
+
+### What about data-key caching?
+
+The standard way to make AWS KMS fast enough for per-record encryption is **data-key caching (reuse)**: generate one data key, encrypt many records with it locally, and call KMS once per *key* instead of once per *value*. It's worth understanding why this doesn't solve the database case.
+
+- **It speeds writes and sequential reads.** Records inserted together share a data key, so an insert and a read of that same contiguous block need only one KMS call between them. This is the best case, and reuse handles it well.
+- **It collapses on real reads.** Production queries don't retrieve rows in insert order — they look up by user, time range, or a secondary index, scattered across the table. A scattered result references roughly *one distinct data key per record*, so decrypting it costs roughly *one KMS `Decrypt` per record* — exactly the per-value cost reuse was meant to avoid. The amortization you buy at write time disappears the moment the read pattern diverges from the write pattern, which is the normal case.
+- **It weakens the security model.** Sharing one key across many records is the shared-key blast radius above, by another name: you lose per-record revocation (revoking one record's key revokes it for every record that shares it) and per-record audit (the log shows one `Decrypt` of a key that unlocks many records, not which record was read).
+
+ZeroKMS reaches the same one-round-trip amortization through its [bulk API](#capabilities-aws-kms-doesnt-provide-without-custom-work) — independent of access pattern, sequential or scattered — while keeping a unique, individually auditable and revocable key per record. You don't trade the security model for the performance.
+
+### Cost
+
+The two services meter cost in fundamentally different ways, and the difference compounds exactly where database encryption spends its time — reads.
+
+- **AWS KMS** is pure usage metering: **$1 per key per month, plus $0.03 per 10,000 requests** ([standard symmetric pricing](https://aws.amazon.com/kms/pricing/)). Critically, it bills **both directions** — every `Encrypt`/`GenerateDataKey` *and* every `Decrypt` is a billable request. Under the per-value security model, that's one billable request per encrypted value, each way.
+- **ZeroKMS** is a [flat subscription](/stack/reference/billing) by encryption operations per month (Free 10k, Pro 50k, Business 500k, Enterprise unlimited), and **decryption is always free** — you're charged for encrypting data, never for reading it back.
+
+Field-level database encryption is read-heavy: a record is encrypted once and decrypted on every read for the rest of its life. So the read path dominates the bill — and it's precisely where the models diverge. Taking the benchmark's read scenario (a query returning 50 records × 3 encrypted fields = 150 `Decrypt` calls under per-value security):
+
+| Read volume (50 records × 3 fields/query) | `Decrypt` calls / month | AWS KMS | ZeroKMS |
+|---|---:|---:|---:|
+| 1 query | 150 | $0.00045 | **$0** |
+| 1M queries / month | 150M | **$450** | **$0** |
+| 10M queries / month | 1.5B | **$4,500** | **$0** |
+
+ZeroKMS reads are free regardless of volume; the encryption side sits under a fixed monthly plan. AWS KMS read cost, by contrast, is **linear and unbounded** in access volume — a hot dataset pays again on every read, forever. (And you hit per-second request quotas, throttling reads, long before the bill becomes the binding constraint.)
+
+The only lever to cut that AWS read cost is **data-key caching** — the same lever from [above](#what-about-data-key-caching), with the same failure mode: it cuts `Decrypt` calls on sequential reads but collapses back to one call per record on scattered (real) reads, and it costs you per-record audit and revocation. On AWS KMS, **cost and performance fail together, for the same architectural reason.** ZeroKMS needs no such lever — decrypts are free *and* bulk-amortized.
+
+## Developer experience
+
+Beyond the trust model, the SDK removes the boilerplate AWS KMS requires. Examples are TypeScript.
+
+### Encrypting and decrypting a value
+
+AWS KMS returns binary ciphertext you must encode, store, and decode yourself:
+
+```typescript
+import { KMSClient, EncryptCommand, DecryptCommand } from "@aws-sdk/client-kms";
+
+const client = new KMSClient({ region: "us-west-2" });
+const keyId = "arn:aws:kms:us-west-2:123456789012:key/abcd1234-...";
+
+const enc = await client.send(
+ new EncryptCommand({ KeyId: keyId, Plaintext: Buffer.from("secret@squirrel.example") }),
+);
+const ciphertext = Buffer.from(enc.CiphertextBlob).toString("base64"); // you store this
+
+const dec = await client.send(
+ new DecryptCommand({ CiphertextBlob: Buffer.from(ciphertext, "base64") }),
+);
+const plaintext = Buffer.from(dec.Plaintext).toString("utf-8");
+```
+
+You manage: key ARNs, buffer/base64 conversions, error handling, AWS credentials, and regions.
+
+The CipherStash SDK returns a JSON payload ready for `JSONB` storage, with key management handled by ZeroKMS and a type-safe `Result` rather than thrown errors:
+
+```typescript
+import { Encryption } from "@cipherstash/stack";
+import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema";
+
+const users = encryptedTable("users", { email: encryptedColumn("email") });
+const client = await Encryption({ schemas: [users] });
+
+const result = await client.encrypt("secret@squirrel.example", { column: users.email, table: users });
+if (result.failure) throw new Error(result.failure.message);
+
+const plaintext = (await client.decrypt(result.data)).data; // type: string
+```
+
+### Capabilities AWS KMS doesn't provide without custom work
+
+**Searchable encryption** — query encrypted data directly in PostgreSQL. AWS KMS requires decrypting in memory or building a custom searchable index.
+
+```typescript
+const users = encryptedTable("users", {
+ email: encryptedColumn("email").freeTextSearch().equality().orderAndRange(),
+});
+
+const terms = await client.encryptQuery("secret", { column: users.email, table: users });
+```
+
+**Identity-aware encryption** — bind encryption to an OIDC identity via `LockContext`. AWS KMS has no built-in equivalent; encryption context is a manual workaround.
+
+```typescript
+import { LockContext } from "@cipherstash/stack/identity";
+
+const lockContext = await new LockContext().identify(userJwt);
+
+await client
+ .encrypt("secret@squirrel.example", { column: users.email, table: users })
+ .withLockContext(lockContext);
+```
+
+**Bulk operations** — native batch API. AWS KMS has no bulk endpoint; you batch and handle rate limits manually.
+
+```typescript
+const result = await client.bulkEncrypt(
+ [{ id: "1", plaintext: "Alice" }, { id: "2", plaintext: "Bob" }],
+ { column: users.name, table: users },
+);
+```
+
+## Comparison
+
+### Architecture
+
+| Property | AWS KMS | CipherStash (ZeroKMS) |
+|---------|---------|------------------------|
+| Key storage | Centralized in provider HSMs | Distributed, derived on demand, never stored |
+| Control model | Provider-managed custody | Distributed trust, no single point of decryption |
+| Key granularity | Shared keys | Per-record keys |
+| Breach impact | All records under the key | Single record |
+| Access control | IAM-based | Identity-based (OIDC), per-record |
+| Audit | CloudTrail (API calls) | Real-time log of every decryption |
+| Performance | Network-dependent, rate-limited | Sub-5ms overhead; ~16× lower latency, up to ~85× throughput |
+| Cost model | Per-request, billed both ways (linear in access volume) | Flat subscription; decryption free |
+| Deployment | AWS regions | Managed service or self-hosted container |
+
+### Developer experience
+
+| Feature | AWS KMS | CipherStash Encryption |
+|---------|---------|------------------------|
+| Basic encryption | Manual buffer/base64 handling | One call, JSON payload |
+| Key management | You manage key ARNs | Automatic, via ZeroKMS |
+| Searchable encryption | Not supported | Built-in for PostgreSQL |
+| Identity-aware encryption | Manual workaround | Built-in `LockContext` |
+| Bulk operations | Manual batching | Native bulk API |
+| Error handling | Try/catch, manual types | Type-safe `Result` |
+| Storage format | Binary (needs encoding) | JSON (`JSONB`-ready) |
+| ORM integration | Manual | Built-in Drizzle support |
+
+## When to use each
+
+**AWS KMS fits when:**
+
+- You're encrypting AWS infrastructure resources (S3, EBS, RDS storage) rather than application records
+- You need envelope encryption at rest with simple key management and no querying over ciphertext
+- A shared-key model and AWS key custody are acceptable for your threat model
+
+**CipherStash fits when:**
+
+- You're encrypting application data in PostgreSQL and need to query it while it stays encrypted
+- You want per-record keys, distributed trust, and instant, identity-based (OIDC) revocation
+- You need real-time audit trails for compliance (SOC 2, HIPAA, GDPR)
+- You're on multi-cloud or hybrid infrastructure and want to avoid provider key custody
+
+**Limits to know:** searchable encryption targets PostgreSQL; identity-aware encryption requires an OIDC provider; and CipherStash encrypts at the application layer, so it complements — rather than replaces — disk- or infrastructure-level encryption (where AWS KMS fits).
+
+## Migration path
+
+Migration from AWS KMS (or any cloud KMS) can be incremental:
+
+1. **Assess** — identify high-value data that benefits from per-record encryption and searchable queries
+2. **Pilot** — start with a new application or a non-critical workload
+3. **Integrate** — connect your existing OIDC identity provider
+4. **Migrate** — move sensitive workloads to ZeroKMS in stages
+5. **Coexist** — keep AWS KMS for infrastructure-level encryption
+
+## Learn more
+
+- [CipherStash Encryption getting started](/stack/quickstart)
+- [ZeroKMS](/stack/cipherstash/kms)
+- [Schema reference](/stack/cipherstash/encryption/schema)
+- [Searchable encryption concepts](/stack/cipherstash/encryption/searchable-encryption)
+- [Security architecture](/stack/reference/security-architecture) — leakage profiles and primitives
+- [ZeroKMS vs Hardware Security Modules](/concepts/compare/zerokms-vs-hsm)
+- [AWS KMS documentation](https://docs.aws.amazon.com/kms/)
diff --git a/content/docs/concepts/compare/fhe.mdx b/content/docs/concepts/compare/fhe.mdx
new file mode 100644
index 0000000..a4905bb
--- /dev/null
+++ b/content/docs/concepts/compare/fhe.mdx
@@ -0,0 +1,228 @@
+---
+title: CipherStash vs Homomorphic Encryption
+description: Searchable encryption is not FHE. See how CipherStash queries encrypted data with EQL v3 and why it's far faster than fully homomorphic encryption for real workloads.
+type: concept
+components: [encryption, eql]
+audience: [developer, cto, ciso]
+reviewBy: "2027-01-05"
+---
+
+CipherStash is often described — incorrectly — as "homomorphic encryption for databases." It isn't. CipherStash uses a **portfolio of specialised searchable-encryption schemes**, exposed through PostgreSQL via [EQL](/reference/eql), that are 5–6 orders of magnitude faster than today's best Fully Homomorphic Encryption (FHE) at the operations a real database actually runs.
+
+This page explains the difference, with numbers.
+
+## TL;DR
+
+| | Fully Homomorphic Encryption (FHE) | CipherStash searchable encryption |
+| --- | --- | --- |
+| **Goal** | Run *arbitrary* computation on ciphertext | Make the database operations a real workload runs (equality, match, range) practical on ciphertext |
+| **Approach** | One general-purpose primitive | A portfolio of specialised primitives, one per query type |
+| **Per-operation cost (Apple M2)** | ~150 ms equality, ~165 ms compare, ~11 ms encrypt | ~755 ns equality, ~755 ns compare, ~375 µs encrypt |
+| **Speed-up vs TFHE** | 1× | ~200,000× equality, ~215,000× greater-than, ~29× encrypt |
+| **Where it runs** | Specialised libraries, custom hardware proposals | Standard PostgreSQL with [EQL v3](/reference/eql) |
+| **Production-ready today** | No — orders of magnitude too slow for OLTP | Yes — ~0.12 ms exact match and order-preserving range on million-row tables (EQL v3) |
+
+Benchmarks: primitive-level FHE-vs-ORE from [`github.com/cipherstash/tfhe-ore-bench`](https://github.com/cipherstash/tfhe-ore-bench) (Apple M2, single-threaded; TFHE vs CipherStash ORE); database-level EQL v3 numbers from CipherStash's EQL v3 benchmark suite (1M rows, Apple M1 Max, PostgreSQL 17).
+
+## Why the conflation happens
+
+Both FHE and searchable encryption answer the question *"Can I do something useful with encrypted data without decrypting it first?"* That question, framed at a whiteboard, sounds like a single problem. So when people first see CipherStash they reach for the most well-known answer — homomorphic encryption — and the comparison sticks.
+
+It's the wrong frame. FHE and searchable encryption sit on opposite ends of a generality / performance trade-off:
+
+- **FHE** keeps a single ciphertext format and lets you run *any* circuit over it. The price is that even simple operations cost tens to hundreds of milliseconds per ciphertext, and the ciphertext blows up in size as you compose operations. It is a remarkable theoretical achievement and an active area of research; it is not yet practical for production databases.
+
+- **Searchable encryption** is a family of primitives, each *purpose-built* for one class of operation. Equality has its own scheme. Substring containment has its own scheme. Range and ordering have their own scheme. Each is dramatically cheaper than FHE because each does much less.
+
+CipherStash is squarely in the second camp. The reason it can deliver microsecond-class encrypted predicates while TFHE takes 150 ms is not that we found a faster FHE — it's that we don't need FHE at all.
+
+## Two cryptographic philosophies
+
+**FHE** stakes everything on a single universal cipher and the ability to run any circuit over it. The cost is tens to hundreds of milliseconds per operation.
+
+**CipherStash** keeps a portfolio of specialised ciphers and picks the right one per query type. The cost is sub-microsecond per operation.
+
+For workloads that are essentially "find rows where X, sort by Y" — i.e. the workload of every line-of-business application on the planet — the second philosophy is dramatically more efficient and equally rigorous, *provided* each scheme has a published, formally-bounded leakage profile. CipherStash's schemes do (see [Core concepts](/reference/eql/core-concepts) for what each index term reveals, and [Security architecture](/stack/reference/security-architecture) for the formal profiles).
+
+## Performance — measured per-operation cost
+
+Numbers below are from the open-source benchmark harness at [`github.com/cipherstash/tfhe-ore-bench`](https://github.com/cipherstash/tfhe-ore-bench). Single-threaded, Apple M2, comparing TFHE — a leading FHE library — against CipherStash's Order-Revealing Encryption (ORE) on the primitive operations a database executes per row.
+
+| Operation | TFHE (FHE) | CipherStash ORE | Speed-up |
+| --- | --- | --- | --- |
+| Encrypt one value | ~11 ms | ~375 µs | ~29× |
+| Equality (`a == b`) | ~150 ms | ~755 ns | **~200,000×** |
+| Greater-than (`a > b`) | ~162 ms | ~755 ns | **~215,000×** |
+| Less-than (`a < b`) | ~166 ms | ~755 ns | **~220,000×** |
+| Greater-or-equal (`a >= b`) | ~25 ms | ~131 ns | **~190,000×** |
+| Less-or-equal (`a <= b`) | ~30 ms | ~131 ns | **~225,000×** |
+
+The implication is straightforward: a single equality check at ~150 ms is already untenable per row. Multiplied across a million-row `WHERE` clause, FHE is impossible; the same predicate in CipherStash runs in microseconds end-to-end and rides standard PostgreSQL B-tree, hash and GIN indexes.
+
+### Real-world performance on EQL v3
+
+The per-operation numbers above measure the ORE primitive in isolation. In a live database the more useful question is how an encrypted query compares to the *same query on plaintext PostgreSQL*. CipherStash's EQL v3 benchmark suite measured this on a 1M-row table (Apple M1 Max, PostgreSQL 17):
+
+| Query | EQL v3 | vs plaintext PostgreSQL |
+| --- | --- | --- |
+| Exact equality (`=`) | ~0.12 ms | ~1.3× |
+| Range + ordered, order-preserving (OPE) | ~0.12 ms | ~1.2× |
+| Range `LIMIT 100` (ORE) | — | 4× faster than EQL v2 |
+| Token match (bloom filter) | — | 100–400× vs no index |
+
+{/* TODO: EQL v3 database benchmark figures are from the EQL v3 suite in cipherstash/benches (report/V3_COMPARISON.md, 1M rows / M1 Max / PG 17). Replace with a public-facing citation once the v3 benchmarks are published, as the tfhe-ore-bench link does for the primitive numbers. */}
+
+Encrypted equality and order-preserving range queries run within ~20–30% of plaintext PostgreSQL on a million rows — the functional index on the encrypted term engages exactly as it would on a plaintext column. EQL v3 adds an **order-preserving encryption (OPE)** option alongside ORE for orderable columns; where the security model allows it, OPE brings range and `ORDER BY` to near-plaintext latency (its index also builds ~40× faster than ORE at 1M rows). This is the concrete meaning of "production-ready": not "faster than FHE in theory," but low-single-digit-millisecond encrypted predicates on realistic table sizes.
+
+> **Good to know**: FHE's selling point is that it can compute *anything*. CipherStash deliberately doesn't try to. We bet — correctly, for database workloads — that the long tail of "interesting" computation a database performs collapses to a small set of well-defined operations, and that specialised schemes for those operations beat a universal one by five or six orders of magnitude.
+
+## The CipherStash scheme portfolio
+
+In EQL v3, a column's capability is declared by its **domain type** in the `eql_v3` schema — `eql_v3.text_eq` (equality), `eql_v3.int8_ord` (range/order), `eql_v3.text_match` (token match), `eql_v3.text_search` (all three). The type fixes which searchable terms travel in the encrypted payload. In practice, only the 3–5 sensitive columns per table use these schemes; foreign keys, timestamps, status flags and most other columns remain plaintext and behave exactly as they always have. That is what makes the portfolio approach tractable in real deployments.
+
+| Scheme | Used for | Mechanism | Leakage profile |
+| --- | --- | --- | --- |
+| **HMAC-SHA-256** (`hm` term) | Exact equality (`=`, `IN`, joins, `GROUP BY`, `DISTINCT`) on `_eq` / `text_search` variants | Keyed hash — equal plaintexts produce equal tags; distinct plaintexts are indistinguishable. | Value repetition only (which rows share a value). Encrypt high-cardinality columns to limit frequency exposure. |
+| **Block ORE** (`ob` term, `eql_v3.ore_block_256`) | Range and ordering (`<`, `>`, `BETWEEN`, `ORDER BY`, `MIN`/`MAX`) on `_ord` variants | Order-revealing encryption over block terms; comparison performed via the Postgres operator class. | Relative order of two values — nothing about magnitude. |
+| **Order-preserving encryption (OPE)** (`_ord_ope` specifier) | The EQL v3 fast path for the same range / order operations | Order-preserving term giving near-plaintext index performance; pinned via the `_ord_ope` domain specifier. | Relative order of two values — same class as ORE, faster to query. |
+| **Encrypted Bloom filter** (`bf` term, `eql_v3.bloom_filter`) | Token / substring match (`@>`; there is no `LIKE` on ciphertext) on `_match` / `text_search` variants | Trigram tokens hashed into a fixed-size bit vector; queried via GIN containment with client-side false-positive filtering. | Probabilistic token overlap. False-positive rate doubles as a security knob — higher FPR obscures token frequency. |
+
+Underlying every encrypted value, AES-based authenticated encryption provides the ciphertext (`c`) used for retrieval. The schemes above are *searchable index terms* stored alongside it, not replacements for it. The database stores `ciphertext + terms`; the application receives plaintext only after key-bound decryption. See [Core concepts](/reference/eql/core-concepts) for the full payload anatomy.
+
+## What gets encrypted, and what doesn't
+
+A common misconception is that "encrypted database" means every column is opaque. In reality:
+
+- **Sensitive columns are encrypted.** Names, emails, phone numbers, financial values, health attributes, free-text notes — typically 3–5 columns per sensitive table.
+- **Everything else stays plaintext.** Surrogate primary keys, foreign keys, timestamps, enums, status flags, soft-delete markers, audit columns. They behave normally — indexed normally, joined normally, replicated normally.
+- **Joins usually happen on plaintext.** Foreign keys are not normally sensitive; the join keys are plaintext, and join performance is unchanged. Where a join *does* need to happen on a sensitive value, an equality (`_eq`) term makes it efficient.
+
+This single fact reframes most operational concerns about encrypted databases. The encrypted columns are an addition to your schema, not a replacement for it.
+
+## Where plaintext actually lives
+
+With the [Encryption SDK](/stack/cipherstash/encryption), plaintext exists only inside your client application, edge worker, or browser. The PostgreSQL database, the network between you and it, any logical-replication consumer, and any Postgres-aware tooling (pgAdmin, query logs, backups, debug snapshots) see only ciphertext and searchable terms.
+
+This matters because most production data exposure happens *outside* the database — in proxies, replicas, BI tools, debug logs, error-reporting systems. With the SDK pattern, those surfaces never see plaintext to begin with.
+
+> **Good to know**: CipherStash also offers a server-side [Proxy](/stack/cipherstash/proxy) for drop-in adoption with no code changes. Proxy and SDK are different deployment shapes of the same primitives; the SDK pattern gives you the strongest end-to-end guarantee.
+
+## EQL — open source, pluggable, extensible
+
+All of this is implemented as **EQL — Encrypt Query Language**, an open-source set of PostgreSQL types, operators, and functions ([github.com/cipherstash/encrypt-query-language](https://github.com/cipherstash/encrypt-query-language)). EQL v3 exposes the encrypted operators as standard SQL over a family of domain types in the `eql_v3` schema:
+
+- HMAC-backed equality for `=`, `IN`, joins, `GROUP BY`, and unique constraints
+- Bloom-filter–backed token containment for `@>` (encrypted free-text match — `LIKE`/`ILIKE` are blocked on ciphertext)
+- ORE- and OPE-backed comparisons for `<`, `>`, `BETWEEN`, and `ORDER BY`
+
+Because every encrypted predicate is just standard SQL on an `eql_v3` domain column, your application SQL stays standard SQL. Your ORM, your query builder, your DBA's `EXPLAIN ANALYZE` workflow, your migration tooling — all of it keeps working. The one rule: operands must be **typed** (`$1::eql_v3.text_eq`, not a bare literal) so the encrypted operator resolves rather than falling back to native `jsonb` semantics — the SDK and Proxy do this automatically. See [Core concepts](/reference/eql/core-concepts#the-typed-operand-rule).
+
+Two architectural consequences are worth calling out:
+
+**Schemes are swappable.** A column's mechanism is declared by its domain variant, so it can be migrated — for example ORE to OPE for faster ordering — without changing application SQL. As stronger primitives mature — lattice-based searchable schemes, structured encryption variants, eventually practical FHE for specific operations — they slot in behind the same EQL surface.
+
+**Quantum-resistant by construction.** The primitives (AES-based authenticated encryption, HMAC-SHA-256, and the ORE / bloom-filter constructions) avoid the cryptographic assumptions known to fall to a sufficiently large quantum computer. No RSA, no elliptic curves. A column encrypted today is still secure on the day post-quantum migration becomes urgent.
+
+## What this looks like in SQL
+
+Encrypted columns participate in standard SQL — the `eql_v3` domain types and their operators do the heavy lifting. Operands are typed with the column's domain variant:
+
+```sql title="encrypted-queries.sql"
+-- Exact equality (HMAC term)
+SELECT id, name
+FROM users
+WHERE email = $1::eql_v3.text_eq;
+
+-- Token / substring match (bloom filter — there is no LIKE on ciphertext)
+SELECT id, name
+FROM users
+WHERE name @> $1::eql_v3.text_match
+LIMIT 10;
+
+-- Range query (ORE / OPE term)
+SELECT id, amount
+FROM transactions
+WHERE amount > $1::eql_v3.int8_ord
+ORDER BY amount
+LIMIT 100;
+
+-- Join on a plaintext foreign key (unchanged)
+SELECT u.id, t.amount
+FROM users u
+JOIN transactions t ON t.user_id = u.id
+WHERE u.email = $1::eql_v3.text_eq;
+```
+
+In each case, the bound parameter is an encrypted payload produced by the SDK — the application encrypts the search value with the same key as the stored data, and PostgreSQL compares the searchable terms without ever seeing plaintext. See the [EQL reference](/reference/eql) for the full type, operator, and function surface.
+
+## Operational FAQ
+
+The questions a senior engineer typically asks before approving an encrypted-database rollout — answered directly.
+
+### Joins across encrypted columns?
+
+Supported, and effectively the same performance as plaintext. In practice the join key itself is rarely sensitive — joins happen on surrogate IDs or foreign keys, which stay plaintext. When a join *does* need to happen on a sensitive value (e.g. email), an equality (`_eq`) term makes equijoins efficient — subject to both columns sharing the same encryption keyset.
+
+### `MIN`, `MAX`, `COUNT`?
+
+`COUNT` works without modification on encrypted columns. `MIN` and `MAX` work over `_ord` (ORE/OPE) columns. `SUM` and `AVG` of encrypted numerics are not supported by the current scheme portfolio — decrypt application-side or aggregate over a plaintext bucket column.
+
+### `GROUP BY`?
+
+Supported on `_eq` (equality-indexed) columns. By definition, grouping by an encrypted column reveals the histogram of distinct values to the query — that is the result the application asked for. If the histogram itself is sensitive, group on a coarser plaintext bucket column instead, or restrict who can run the query at the application layer.
+
+### Foreign keys, cascading deletes?
+
+Unchanged. Foreign keys are not normally encrypted; cascading deletes, referential integrity, and `ON DELETE` semantics work as they always have.
+
+### NULL, empty strings, collation?
+
+Preserved. A SQL `NULL` is never encrypted, so `IS NULL` / `IS NOT NULL` work on every variant. Encrypted strings carry the metadata needed to keep ordering and equality consistent under encryption. NULL handling matches standard PostgreSQL semantics.
+
+### Logical replication, `pg_dump` / restore, read replicas?
+
+All supported. Encrypted values and terms are stored as standard PostgreSQL `jsonb` — they replicate, dump, restore and stream to read replicas like any other column.
+
+### Backups and key recovery?
+
+Key references are stored alongside the ciphertext. On restore, the SDK derives the same per-record keys via [ZeroKMS](/stack/cipherstash/kms) using those references. There is no separate "key restore" step that has to be choreographed with the data restore — the references travel with the data.
+
+### Supabase RLS and PostgREST?
+
+Both work. RLS policies execute against plaintext columns (typically authentication-related: `user_id`, `tenant_id`) just as they do today. PostgREST exposes encrypted columns as opaque values to clients, and the SDK handles encryption/decryption at the application boundary.
+
+### Schema migrations and re-keying?
+
+A column's mechanism is declared by its domain variant and can be migrated without changing application SQL. Online re-keying is supported via dual-write during the migration window.
+
+## Security & threat model
+
+Each scheme has a published, formally-bounded leakage profile (see [Core concepts](/reference/eql/core-concepts#what-the-terms-reveal)):
+
+- **HMAC-SHA-256** (`hm`): equality only. Reveals value repetition; eliminated for unique columns or by encrypting only high-cardinality values.
+- **Encrypted Bloom filter** (`bf`): false-positive rate is a deliberate parameter; raising it obscures token-frequency patterns at the cost of additional client-side filtering. Repeated query analysis is bounded by the FPR.
+- **Block ORE / OPE** (`ob`): reveals the relative order of values, and nothing about magnitude. OPE trades a faster, index-friendlier term for the same order-only leakage class.
+
+The trust boundary in the recommended SDK deployment is your client application, edge worker, or browser. The PostgreSQL instance, the network, ZeroKMS, the operator running your Postgres, and any Postgres-aware tooling are all *outside* that boundary and never see plaintext.
+
+For deeper analysis — formal definitions of each scheme's leakage function, parameter-selection guidance, and attack-model walk-throughs — see [Security architecture](/stack/reference/security-architecture). Full analysis is available on request under NDA.
+
+## When CipherStash isn't the right fit
+
+In the spirit of an honest comparison page:
+
+- **You need general-purpose computation on ciphertext.** If your workload is "run arbitrary user-defined functions over encrypted data" — for example, evaluating an opaque ML model on encrypted inputs — searchable encryption is not the right tool. That is genuinely the FHE problem, and you should track the FHE literature.
+- **The query patterns you care about aren't equality, match or range.** Searchable encryption schemes exist for many other operations (graph traversal, geometric proximity, regex), but CipherStash's portfolio today is targeted at the operations a typical OLTP / line-of-business workload runs. If your central workload is something else, [talk to us](https://cipherstash.com/contact) about what's possible.
+- **You want every column encrypted with no plaintext metadata at all.** That is achievable but expensive, and almost always unnecessary. The right design encrypts the sensitive columns and leaves the rest of the schema in plaintext.
+
+## Get started
+
+- **Primitive benchmarks (FHE vs ORE)**: [`github.com/cipherstash/tfhe-ore-bench`](https://github.com/cipherstash/tfhe-ore-bench)
+- **EQL reference**: [`/reference/eql`](/reference/eql)
+- **EQL on GitHub**: [`github.com/cipherstash/encrypt-query-language`](https://github.com/cipherstash/encrypt-query-language)
+- **Encryption SDK quickstart**: [`/stack/quickstart`](/stack/quickstart)
+- **Talk to us** about a specific workload: [cipherstash.com/contact](https://cipherstash.com/contact)
+
+---
+
+> **Bottom line**: CipherStash is *not* FHE and does not need to be. It is a portfolio of specialised, well-understood searchable-encryption schemes — HMAC for equality, encrypted Bloom filters for token match, Block ORE and OPE for range — exposed through [EQL v3](/reference/eql) on standard PostgreSQL. That is what makes it 5–6 orders of magnitude faster than TFHE at the operations a real workload runs, while keeping data encrypted end-to-end.
diff --git a/content/docs/concepts/compare/hashicorp-vault.mdx b/content/docs/concepts/compare/hashicorp-vault.mdx
new file mode 100644
index 0000000..cc9beab
--- /dev/null
+++ b/content/docs/concepts/compare/hashicorp-vault.mdx
@@ -0,0 +1,118 @@
+---
+title: CipherStash vs HashiCorp Vault
+description: How CipherStash ZeroKMS compares to HashiCorp Vault's Transit secrets engine for application-level encryption — trust model, per-record keys, plaintext exposure, and the trade-offs of Vault's direct and envelope modes.
+type: concept
+components: [platform]
+audience: [developer, cto, ciso]
+reviewBy: "2027-01-05"
+---
+
+HashiCorp Vault and CipherStash both encrypt application data, but they sit in different places in your architecture. The closest comparison is Vault's **Transit secrets engine** — "encryption as a service," where Vault holds the keys and the application calls it to encrypt and decrypt. CipherStash encrypts application data through a developer SDK backed by **ZeroKMS**, which issues a unique key per record and never holds the keys needed to decrypt your data.
+
+This page compares Transit with ZeroKMS. Vault's other engines (KV secrets storage, PKI, SSH) are out of scope.
+
+## What's being compared
+
+| Term | What it is |
+|---|---|
+| **Vault Transit** | Vault's encryption-as-a-service engine. The app sends data to Vault; Vault encrypts/decrypts with keys it holds and returns the result. Supports a batch API and key derivation. |
+| **Vault Transit `datakey`** | Transit's envelope mode: Vault generates a data key (returns plaintext + a Vault-wrapped copy), analogous to AWS KMS `GenerateDataKey`. You encrypt locally and store the wrapped key. |
+| **ZeroKMS** | CipherStash's key-management service. Derives a unique key per record on demand from client- and server-side components; no party — including CipherStash — holds a complete data key. |
+| **CipherStash Encryption SDK** (`@cipherstash/stack`) | The library that encrypts/decrypts application data and runs searchable queries, backed by ZeroKMS. |
+
+## Two ways to use Vault Transit
+
+Transit can be used in two modes, and the distinction drives the whole comparison.
+
+**Direct encryption** (`/transit/encrypt`, `/transit/decrypt`) — the app sends **plaintext to Vault**, Vault returns ciphertext. It supports a batch API (`batch_input`, many values per call) and can derive a key per *context* (`derived: true`). Fast, but your plaintext transits the Vault server.
+
+**Envelope encryption** (`/transit/datakey`) — Vault generates a data key; the app encrypts locally so **plaintext never leaves the client**. But `datakey` issues one key per call with no batch API, so a unique key per record means one Vault round-trip per record. To go fast you reuse one data key across many records — which puts you straight into data-key-reuse territory (covered below).
+
+## The trilemma
+
+For database field encryption you want three things at once: plaintext that never leaves the client, a unique key per record (so a compromised key exposes one record and access is auditable and revocable per record), and enough throughput to encrypt and decrypt in bulk. **No single Vault Transit configuration delivers all three** — each gives you two:
+
+| Configuration | Plaintext stays client-side | Per-record keys | Bulk-amortized |
+|---|:---:|:---:|:---:|
+| Transit direct, shared key | ✗ | ✗ | ✓ |
+| Transit direct, derived per-record (`context`) | ✗ | ✓ | ✓ |
+| Transit `datakey`, one key per record | ✓ | ✓ | ✗ |
+| Transit `datakey`, key reused across records | ✓ | ✗ | ✓ |
+| **ZeroKMS** | **✓** | **✓** | **✓** |
+
+ZeroKMS occupies the corner none of Vault's modes reach: the SDK encrypts locally (plaintext never sent), every record gets its own key, and a batch of up to 10,000 keys is a single round-trip.
+
+### Reading the table
+
+The trade-off lands in a different place than it does for a cloud KMS, because of one asymmetry in Vault's API: `encrypt` and `decrypt` are batched (`batch_input`), but `datakey` — which mints a data key for envelope mode — is **one key per call, with no batch**.
+
+- **Direct modes are fast both ways, but expose plaintext.** A whole batch encrypts or decrypts in one round-trip — but the plaintext is sent to Vault to be encrypted. Per-record keys are possible via a derived `context`; the exposure remains.
+- **Envelope keeps plaintext client-side, but per-record keys cost you on writes.** A unique key per record means one `datakey` round-trip *per record* on the write path (there is no batched key generation). That's the `✗` in the bulk column — it's a *write*-throughput cost.
+- **Reuse buys back write throughput by sharing a key** across many records. That's a security trade-off, not a free lunch: you lose per-record revocation (revoking one record's key revokes it for every record sharing it) and per-record audit (the log shows one unwrap of a key that opens many records, not which record was read).
+
+Note what does **not** happen: a scattered read does not collapse. Vault unwraps all the distinct data keys in a result with a single batched `transit/decrypt` call, so reads are one round-trip regardless of access pattern. This is where Vault differs from AWS KMS — whose `Decrypt` has no batch API, so a scattered read of N records costs N calls (see [CipherStash vs AWS KMS](/concepts/compare/aws-kms)). For Vault, the cost of per-record keys lands on **writes**, and the cost of reuse is the **security model** — not reads.
+
+## Architecture & trust model
+
+- **Plaintext exposure.** In Transit direct mode, plaintext is sent to Vault to be encrypted — Vault (and whoever operates it) sees it in the clear at the moment of encryption. ZeroKMS never receives plaintext: the SDK encrypts locally and ZeroKMS only performs key derivation. Vault's envelope mode also keeps plaintext client-side, but per-record keys then cost a `datakey` round-trip per record on write — or you reuse a key and take the security trade-off above.
+- **Key custody and trust.** Transit keys live in Vault; whoever controls the Vault cluster (your operators, or HashiCorp for HCP Vault) controls the keys. ZeroKMS derives keys on demand by combining client- and server-side components and never stores them — no single party, including CipherStash, can decrypt unilaterally.
+- **Key granularity.** A Transit key is typically shared across many records (shared-key blast radius), or derived per `context` if you build that in. ZeroKMS issues a unique key per record natively.
+- **Access control & audit.** Vault gates Transit operations with its policy system and logs to the audit device, at key/path granularity. ZeroKMS binds decryption to OIDC identity with per-record access control, instant revocation, and a real-time per-record audit log.
+- **Searchable encryption.** Transit's convergent mode makes encryption deterministic (equal plaintexts produce equal ciphertexts), which supports equality matching but not range or ordering. CipherStash provides searchable encryption — equality, range/order, and free-text — over PostgreSQL via EQL.
+
+
+Vault Transit is a service you send data *to*. ZeroKMS is a key service the SDK derives keys *from*, so your plaintext stays in your application and every record gets its own key — without choosing between speed and per-record security.
+
+
+## Performance
+
+Unlike AWS KMS, Vault Transit **has a batch API**, so this is not a throughput blowout — batched Transit in-region is fast, and raw speed is not the differentiator. A few things shape a fair comparison:
+
+- Transit caches keys in memory and does not touch storage on the encrypt/decrypt hot path, so a single well-provisioned Vault node fairly represents per-node throughput. Scaling means adding nodes (an operational and cost consideration), where ZeroKMS is a managed service.
+- A fair benchmark must use batch on both sides (Transit `batch_input` vs ZeroKMS bulk), run in-region with a separate load generator, and report Vault's envelope/`datakey` mode separately — its serial write path (per-record keys = one `datakey` round-trip per record, vs a reused key) is where the difference shows, not the batched read path.
+
+{/* TODO: link the in-region benchmark numbers once the `vault-transit` backend run lands (cipherstash/benches kms-app). Until then this section stays qualitative — no unsourced figures. */}
+
+The takeaway isn't that one is dramatically faster; it's that ZeroKMS reaches bulk throughput *without* sending plaintext to a server and *without* giving up per-record keys — the trade-off Vault forces.
+
+## Comparison
+
+| Property | Vault Transit | CipherStash (ZeroKMS) |
+|---|---|---|
+| Encryption location | On the Vault server (direct mode) | In the client (SDK) |
+| Plaintext exposure | Sent to Vault (direct mode) | Never leaves the client |
+| Key custody | In the Vault cluster you (or HCP) run | Derived on demand, never stored |
+| Trust model | Whoever operates Vault holds the keys | Distributed; no single point of decryption |
+| Key granularity | Shared key, or derived per `context` | Per record, native |
+| Per-record revocation & audit | Per key / key version | Per record, identity-bound (OIDC) |
+| Searchable encryption | Equality only (convergent mode) | Equality, range/order, free-text (EQL) |
+| Bulk API | Yes (`batch_input`); `datakey` is one key per call | Yes (up to 10,000 keys per round-trip) |
+| Deployment & ops | Self-host HA (unseal, storage, scaling) or HCP | Managed service, or self-host container |
+
+## When to use each
+
+**Vault Transit fits when:**
+
+- You already run Vault and want encryption-as-a-service alongside your other Vault workloads
+- Sending plaintext to a service you operate is acceptable for your threat model
+- A shared-key model (or context-derived keys you manage) meets your audit and revocation needs
+- You need fully self-hosted, air-gappable infrastructure under your own operation
+
+**CipherStash fits when:**
+
+- Plaintext should never leave your application, even to your key service
+- You want per-record keys, distributed trust, and instant identity-based (OIDC) revocation by default
+- You need to query encrypted data in PostgreSQL while it stays encrypted
+- You want managed key management without operating an HA key cluster
+- You need real-time, per-record audit trails for compliance (SOC 2, HIPAA, GDPR)
+
+Vault and CipherStash can also coexist: Vault for infrastructure secrets and its other engines, CipherStash for application-layer, per-record data encryption and searchable queries.
+
+## Learn more
+
+- [CipherStash Encryption getting started](/stack/quickstart)
+- [ZeroKMS](/stack/cipherstash/kms)
+- [Searchable encryption concepts](/stack/cipherstash/encryption/searchable-encryption)
+- [CipherStash vs AWS KMS](/concepts/compare/aws-kms) — the same data-key trade-off in a cloud KMS
+- [All comparisons](/concepts/compare)
+- [HashiCorp Vault Transit documentation](https://developer.hashicorp.com/vault/docs/secrets/transit)
diff --git a/content/docs/concepts/compare/index.mdx b/content/docs/concepts/compare/index.mdx
index 5c34ec1..3ec1952 100644
--- a/content/docs/concepts/compare/index.mdx
+++ b/content/docs/concepts/compare/index.mdx
@@ -1,9 +1,20 @@
---
title: Comparisons
-description: "How CipherStash compares to other approaches to protecting data."
+seoTitle: How CipherStash compares
+description: How CipherStash compares to alternative approaches — homomorphic encryption, cloud KMS, hardware security modules, and HashiCorp Vault.
type: concept
---
-This section is being built as part of the docs V2 overhaul ([CIP-3307](https://linear.app/cipherstash/issue/CIP-3307)). Track progress in [IA.md](https://github.com/cipherstash/docs/blob/v2/IA.md).
+Side-by-side breakdowns of CipherStash against the approaches teams most commonly weigh it against. Each page focuses on the same questions: what does each tool actually do, what are the per-operation trade-offs, and where is each one the right fit.
-Until it lands, current documentation lives in the [existing docs](/stack).
+
+
+
+
+
+
+
+## Related
+
+- [Use cases](/stack/reference/use-cases) — including a comparison against data vaults like Skyflow and VGS.
+- [Security architecture](/stack/reference/security-architecture) — the cryptographic primitives behind every comparison on this page.
diff --git a/content/docs/concepts/compare/meta.json b/content/docs/concepts/compare/meta.json
index 76e9696..956597a 100644
--- a/content/docs/concepts/compare/meta.json
+++ b/content/docs/concepts/compare/meta.json
@@ -1,5 +1,5 @@
{
"title": "Comparisons",
"icon": "Scale",
- "pages": ["..."]
+ "pages": ["fhe", "aws-kms", "hashicorp-vault", "zerokms-vs-hsm"]
}
diff --git a/content/docs/concepts/compare/zerokms-vs-hsm.mdx b/content/docs/concepts/compare/zerokms-vs-hsm.mdx
new file mode 100644
index 0000000..f0759da
--- /dev/null
+++ b/content/docs/concepts/compare/zerokms-vs-hsm.mdx
@@ -0,0 +1,229 @@
+---
+title: ZeroKMS vs Hardware Security Modules (HSM)
+description: Compare CipherStash ZeroKMS's zero-trust key management with traditional hardware security modules
+type: concept
+components: [platform]
+audience: [cto, ciso]
+reviewBy: "2026-10-05"
+---
+
+Hardware Security Modules (HSMs) provide cryptographic key management through tamper-evident hardware. They're often chosen when organizations need complete control over key custody, regulatory compliance with specific hardware standards, and jurisdictional certainty.
+
+Cloud based Key Management Services like ZeroKMS and AWS KMS use HSM internally. However, managing your own HSM can be a complex and costly undertaking. Careful consideration must be given to physical access management, key governance and disaster recovery plans.
+
+
+**ZeroKMS simplifies or eliminates self-managed HSM:**
+
+* as a viable alternative to HSM for many use-cases
+* by reducing operational complexity and improving performance when customer managed HSM is mandated
+
+
+## Benefits of HSM
+
+TODO
+
+## HSM Challenges
+
+Virtually all key management solutions will use an HSM as part of a key heirarchy.
+
+
+The question isn't whether an HSM is required but rather if self-managed HSM is necessary to meet compliance and security goals.
+
+
+Below are some of the challenges associated with self-managed HSM that should be considered before deciding on a key management strategy.
+
+### Physical access
+
+HSMs require secure physical installation — often in locked racks or safes within a data center with strict access controls, surveillance, and environmental protections.
+
+
+
+Standards like FIPS and PCI require dual control and split-knowledge policies, meaning no single person can access keys alone. This can complicate physical security which not only introduces risk but adds significant cost.
+
+
+ZeroKMS replaces physical custody with cryptographic custody — the root key still lives in hardware, but access, rotation, and audit are handled programmatically and verifiably, eliminating the physical-access bottlenecks that plague traditional HSM operations.
+
+TODO: this could be worded better
+The ZeroKMS distributed trust model means that even HSM controlled root keys are unable to perform cryptographic operations on their own.
+
+
+Learn more (link):
+TODO: Cryptographic custody and distributed trust (maybe this is an article called Trust Model)
+
+### Integration effort
+
+Integrating software applications with an HSM requires the use of low-level interfaces like PKCS#11, JCE or CNG. Additionally, integration with network-based HSMs such as AWS CloudHSM or Thales Luna require secure channel setup, VPNs, or private VPC endpoints.
+
+Using PKCS#11, JCE, or CNG directly is notoriously difficult for developers — even seasoned security engineers — because these APIs were designed primarily for hardware abstraction and compliance, not for developer ergonomics.
+
+
+ZeroKMS integrates via Protect SDK or CipherStash Proxy which eliminate the complexity of PKCS#11-, JCE-, and CNG-style APIs.
+
+These provide simple, developer-friendly interfaces that integrate directly into application code and databases without requiring HSM drivers, provider configuration, or handle management.
+
+
+### Operational complexity
+
+Traditional HSMs are difficult to scale because every device manages its own key state — meaning key generation, rotation, and backup must be coordinated manually across identical hardware. Achieving high availability or disaster recovery requires securely replicating keys between HSMs, which is slow, error-prone, and often breaks FIPS compliance if not done through vendor-specific procedures.
+
+When an HSM loses power, it wipes all active secrets and reboots into a locked, verified state.
+Recovery requires re-authenticating operators and reloading encrypted key partitions — a safe but slow process that depends on physical credentials, making automated or cloud-scale recovery difficult.
+
+TODO: key ceremonies, replication, uptime (eg. power), reliability etc
+
+### Compliance
+
+TODO: Talk about PCI/DSS etc
+
+### Expertise
+
+Managing an HSM demands cryptographic fluency, vendor-specific training, and precise procedural control — a combination that is hard to hire for. Failing to recruit sufficiently qualified personel can leave organizations vulnerable.
+
+
+ZeroKMS was designed to be simple to use without the need for specialized skills or expertise. Each system component is built on a high-assurance framework, is misuse resistant and forgiving even for engineers with limited cryptographic knowledge.
+
+
+### Cost model
+
+HSMs deliver strong assurance but come with high upfront and ongoing costs. Hardware units typically cost tens to hundreds of thousands of dollars each, plus annual support, licensing, and maintenance contracts. Scaling requires buying more physical devices, not just compute, and disaster recovery often means duplicating hardware across regions. Operating them also demands specialist staff, secure facilities, and compliance audits — making total cost of ownership (TCO) high and predictable only at small scale.
+
+
+ZeroKMS can be purchased via a monthly or annual subscription. There are no upfront or hidden costs.
+
+
+## Hybrid solutions
+
+{/* TODO: add a diagram here. The original Markdoc source used a D2 diagram (this repo has no D2 renderer yet). Original source:
+direction: down
+
+Client: {
+ label: 'Client Application'
+ App: {
+ label: 'App Logic'
+ }
+ SDK: {
+ label: 'Crypto SDK / PKCS#11'
+ }
+ App -> SDK: 'Crypto requests'
+}
+
+HSM: {
+ label: 'HSM'
+ Key: {label: 'Secret Key'}
+}
+
+Client.SDK -> HSM: 'Generate Data Key'
+*/}
+
+## Why Organizations Choose HSMs
+
+### Full Control and Ownership
+
+HSMs offer complete end-to-end ownership over cryptographic keys, including their generation, rotation, archival, and destruction. No external party has access to keys under any circumstances.
+
+### Physical Security and Isolation
+
+Keys are protected by tamper-evident hardware in a physically isolated environment. Sensitive material never leaves the secure boundary of the device.
+
+### Compliance with Regulations
+
+Many organizations require FIPS 140-2 Level 3 or Level 4, PCI DSS, or region-specific data residency laws. HSMs provide auditable hardware-based security with proof of key custody.
+
+### Assured Key Destruction
+
+Organizations can trigger and verify cryptographic erase or physically destroy HSM modules locally, meeting policy requirements for witnessed destruction and compliance audits.
+
+### Jurisdictional Certainty
+
+On-premises HSMs ensure all key management and backups remain under the organization's legal jurisdiction, preventing cross-border replication and reducing sovereignty risks.
+
+### Performance and Latency
+
+Local HSMs provide predictable, ultra-low-latency cryptographic operations for real-time applications and highly sensitive transactional workloads.
+
+### Customization and Legacy Integration
+
+HSMs integrate with legacy infrastructure, support custom backup and cluster topologies, and enable cryptographic ceremonies without being restricted by provider APIs or cloud-specific tooling.
+
+## How ZeroKMS Addresses HSM Requirements
+
+### Per-Record and Zero-Knowledge Key Management
+
+ZeroKMS derives a unique key per record on demand; keys are never stored, and no single party — including CipherStash — can decrypt data unilaterally (CipherStash calls this *zero-knowledge* key management). A compromised key exposes a single record rather than a dataset, addressing the shared-key blast radius of centralized KMS while keeping key custody distributed rather than tied to physical hardware.
+
+### No Shared Secrets and No Single Point of Decryption
+
+Keys are derived on-demand by combining client- and server-side components. No centralized key store exists, and no party (client, server, or CipherStash) holds all components needed to decrypt data unilaterally.
+
+### Instant Key Revocation and Fine-Grained Access
+
+Organizations maintain fine-grained, identity-based access control via OIDC integration. Only authorized identities can decrypt specific records. Instant revocation and real-time audit logs provide continuous, provable compliance that often exceeds traditional HSM audit capabilities.
+
+### Performance Without Bottlenecks
+
+ZeroKMS adds [sub-5ms overhead](https://app.artillery.io/share/sh_75edb9d22a060633bfdce04777ffd60d1bcfc88989393dc38d3a4924b83fc6cd) for most operations, removing the latency and throughput bottlenecks associated with hardware HSMs.
+
+### Continuous Audit and Compliance
+
+Instead of point-in-time compliance checks, ZeroKMS logs every access in real time with cryptographically verifiable audit trails. This exceeds standard attestation methods used with HSMs.
+
+### Flexible Deployment and Jurisdiction Control
+
+ZeroKMS runs as a managed cloud service across multiple regions. It's also available as a Docker container for deployment under your own jurisdiction when needed.
+
+## Comparison Table
+
+| Feature | HSMs | ZeroKMS |
+|---------|------|----------|
+| Key Storage | Centralized in hardware | Distributed, generated locally |
+| Deployment | Physical/Virtual appliance | Managed service or container |
+| Control Model | Full hardware custody | Zero-knowledge, client-side |
+| Compliance | FIPS 140-2 L3/L4 | Continuous audit, cryptographic proofs |
+| Performance | Hardware-limited | Sub-5ms overhead at scale |
+| Scaling | Hardware-dependent | Software-defined, unlimited |
+| Cost Model | CapEx + maintenance | Usage-based |
+| Jurisdictional Control | Full on-prem control | Flexible deployment |
+| Key Granularity | Shared keys | Per-record keys |
+
+## Benefits for Regulated Environments
+
+ZeroKMS enables organizations to maintain HSM-like control, auditability, and compliance while delivering lower operational overhead and greater scalability for modern applications:
+
+- Prove continuous control for compliance-sensitive workloads in financial services, healthcare, and regulated markets
+- Support real-time use cases without sacrificing speed or security
+- Eliminate vendor lock-in by never holding centralized secrets or requiring shared credentials
+- Reduce breach impact through per-record encryption and instant access revocation
+
+## Use Cases
+
+### When HSMs Make Sense
+
+- Regulatory requirements explicitly mandate FIPS 140-2 Level 3+ hardware certification
+- Legacy systems require direct HSM integration
+- On-premises-only deployments with no cloud connectivity
+- Root key protection for certificate authorities
+
+### When ZeroKMS Makes Sense
+
+- Cloud-native or hybrid architectures
+- High-throughput encryption requirements with modern applications
+- Fine-grained, per-record encryption and access control
+- Zero-trust security requirements without delegating trust to providers
+- Global, multi-region deployments
+- Rapid development and deployment cycles
+- Continuous compliance and real-time auditability
+
+## Complementary Deployment
+
+Organizations can use HSMs and ZeroKMS in complementary roles:
+
+- **HSMs**: Protect root keys and certificate authorities
+- **ZeroKMS**: Handle application-layer data encryption and key management
+
+This layered approach provides defense in depth while maintaining performance and flexibility for application development.
+
+## Learn More
+
+- [Read about ZeroKMS](/stack/cipherstash/kms)
+- [View all comparisons](/concepts/compare)
+- [Book a discovery session](/stack/reference/discovery-session)
diff --git a/public/images/hsm.png b/public/images/hsm.png
new file mode 100644
index 0000000..ccb6eda
Binary files /dev/null and b/public/images/hsm.png differ