What is CipherStash?
Field-level encryption that stays queryable in Postgres: the mental model, the pieces, and which door to walk through.
CipherStash encrypts individual fields in your application, before they reach the database, and keeps them queryable in Postgres. The database stores ciphertext and can still answer WHERE, ORDER BY, and containment queries against it.
A breach yields ciphertext. So does a compromised agent, an over-permissioned service, and a curious insider.
The problem
Encrypting a column has always meant losing the query. So teams encrypt at rest, which protects the disk and nothing else: the moment a query runs, the database has plaintext, and so does anyone who reached the database.
That was tolerable when the thing reading your data was a person. It is less so now that it is an agent running on application credentials, at machine speed, one prompt injection away from exfiltration.
The mental model
Three ideas carry the whole product.
Encryption happens in your process. Not in the database, not on our infrastructure. Plaintext never leaves your application.
Every value gets its own key. Not a table key or a column key. A per-value key, derived for one operation and discarded. Compromising one value tells an attacker nothing about the next.
Ciphertext stays queryable. Alongside each encrypted value, the client stores index terms that let Postgres compare ciphertext without decrypting it. Which terms a column carries is something you choose, and it decides what the database could infer: see choosing what each column reveals.
The pieces
| Piece | What it does | When you need it |
|---|---|---|
| Stack SDK | Encrypts and decrypts values in your application | The default path. Start here. |
| EQL | The Postgres surface: encrypted column types, operators, and term extractors | Always, if the data lives in Postgres. Installed once per database. |
| Proxy | Sits in front of Postgres and speaks EQL for you | When you cannot change the application |
| ZeroKMS | Returns a key seed per value; never sees your client key or a data key | Always. See cryptography. |
| CTS | Federates your identity provider so decryption can be bound to a user | Only for identity-aware encryption |
You rarely need all of them at once. The Stack SDK plus EQL is the common case. Proxy is the alternative to the SDK, not an addition to it.
Note that EQL is only for Postgres. The SDK also encrypts values that never touch a database, and non-Postgres stores like DynamoDB, with no EQL involved.
Choosing what each column reveals
Querying ciphertext works because each value carries index terms derived from its plaintext. Those terms are not the plaintext, and what each one reveals is bounded and published rather than incidental: each scheme has a formal leakage profile.
That turns leakage into a design choice rather than a surprise. You decide, per column, which capabilities to declare, and that decides what an observer of the database could infer:
| You declare | So you can | An observer could infer |
|---|---|---|
| Nothing | Store and decrypt | Nothing from the ciphertext |
| Equality | WHERE email = ?, GROUP BY, joins | Which rows share a value |
| Ordering | ORDER BY, ranges, MIN / MAX | The relative order of values |
| Free-text | Token containment | Probabilistic token overlap |
The engineering is in matching those choices to your threat model. For most columns, knowing that two rows share some unknown value is not an exposure. For a column whose frequency distribution is itself the secret, it might be, and then you encrypt without that capability and filter after decryption.
So the goal is not to eliminate leakage. It is to choose capabilities whose leakage your threat model already tolerates, and to know exactly what you chose. Declare only what you query on. The per-scheme detail is in searchable encryption.
Which door
You are building. Start with the Quickstart, then choose your stack to find the integration that matches your platform and ORM.
You are evaluating. Architecture & security is self-contained for a vendor review, starting with cryptography. Solutions covers the problems teams bring us.
You are deciding whether this is the right tool. Start with searchable encryption to understand how encrypted queries work. The comparisons are written to be honest about what CipherStash is not.
Performance
Encryption in use is usually assumed to be slow. It is, if you do it with fully homomorphic encryption. Searchable encryption is a different trade: bounded, published leakage in exchange for the database doing ordinary index work on ciphertext.
| Query overhead | Sub-millisecond. The encrypted operators inline into functional indexes, so Postgres does a normal index scan. |
| vs fully homomorphic encryption | 410,000x faster on the per-row primitives a database actually executes. |
| vs AWS KMS | Up to 14x the throughput, because ZeroKMS derives keys in bulk rather than one call per value. |
The FHE comparison is an open benchmark harness you can run yourself. See CipherStash vs FHE for the methodology, and for the workloads where FHE is genuinely the right tool.
What it protects against
| Threat | What happens |
|---|---|
| Database breach | Ciphertext. The keys were never in the database. |
| Compromised application credentials | Ciphertext, unless the attacker also holds the client key. |
| AI agent exfiltration | The agent reaches the database and decrypts nothing, because its credentials are not the user's keys. |
| Curious insider | Every decryption is a key derivation, and ZeroKMS records who performed it. |
What it does not protect against: an attacker who has compromised your running application process, holding the client key and able to call decrypt. Encryption in the application means the application can decrypt. See cryptography for the full trust model.