# Search encrypted data with Prisma 8 and CipherStash

*Published on 2026-07-30*

*By Dan Draper — CEO and Founder*

First-class support for Prisma 8 brings searchable field-level encryption to Prisma applications — encrypted queries, identity-based key management and almost no change to the way you work with your database.

## Content

Most developers think their production database is already encrypted.

Strictly speaking, they're right.

Most managed databases enable **encryption at rest** by default. But encryption at rest probably doesn't protect data in the way you think it does.

Today, we're excited to announce first-class support for **[Prisma 8 RC1](https://pris.ly/pn-cipherstash)**, making it simple to add searchable field-level encryption to Prisma applications—with encrypted queries, identity-based key management and almost no change to the way you work with your database.

But first: why encrypt fields at all?

## Why encrypt?

### Encryption at rest protects the disk

Imagine someone gains access to your running database.

The disks underneath Postgres may be encrypted, but Postgres still needs to read the data. Once the database is running, queries and indexes only see plaintext values.

Encryption at rest is important. It protects physical storage, snapshots, and discarded hardware.

But it protects the storage layer, not the data from someone who can access the database.

### Encrypt the fields instead.

The next step is field-level encryption.

Instead of relying on the database to encrypt its storage, your application encrypts sensitive values before inserting them. If the database is leaked, fields such as email addresses, financial details, or medical records remain unintelligible.

That is a much stronger security boundary.

Unfortunately, it creates two new problems.


{% eyebrow index="01" label="Problem" heading="Search breaks" level=3 /%}

Once an email address is encrypted, PostgreSQL can no longer do this:

```sql
SELECT *
FROM users
WHERE email = 'dan@example.com';
```

The database doesn’t know what the encrypted value represents.

Equality lookups, free-text search, range queries, sorting, and indexing all become impossible without decrypting entire datasets, which obliterates both performance and security.

{% eyebrow index="02" label="Problem" heading="Key management becomes the weakest link" level=3 /%}

Many field-level encryption implementations protect an entire table (or even the entire database) with the same encryption key.

That creates a dangerous trade-off: the data is encrypted, but a single leaked key can expose it all.

You have reduced the risk of a breach, but the blast radius of a key compromise remains enormous.

## Searchable encryption

CipherStash approaches the problem differently.

When enabled for a field, every sensitive value is encrypted independently using its own derived data key.
Data keys are not stored alongside the data and never leave the application.

Queries on EQL columns are encrypted in the same way.
Postgres compares encrypted query terms against encrypted values.
Standard B-tree and GIN indexes work, too, so performance is sub-millisecond for many queries, even on very large datasets.
See our [benchmarks][benchmarks] for more information.

This means encrypted fields can still support:

* exact-match lookups
* free-text search
* range queries
* sorting
* queries over encrypted JSON

CipherStash can also tie key access directly to the user's identity.
It works with identity providers like Clerk, Auth0, and others, using the user's identity token to control access to encrypted data.
Applications don’t need to store reusable data keys, and a database credential alone isn't enough to decrypt identity-bound values.

{% figure src="/images/blog/prisma-next-encrypted-data-flow.svg" alt="Encrypted insert, query and decryption paths" caption="Encrypted insert, query and decryption paths" /%}

## Searchable encryption in Prisma 8

ORMs have long supported encrypting fields.
What they haven’t been able to do is preserve the queries that make an ORM useful.

Prisma 8 changes that.

{% quote author="Will Madden" title="Engineering Manager, Prisma" avatar="/images/blog/author-will-madden.png" %}
Prisma 8 is designed to make advanced database capabilities feel native to application developers. CipherStash brings searchable field-level encryption into that model, allowing teams to protect sensitive data without giving up Prisma's type-safe developer experience.
{% /quote %}

Prisma 8 uses a contract-first workflow: your data contract describes the state of your database, and the framework plans, applies, and verifies the migrations required to produce it.

The CipherStash extension makes encryption part of that same contract.
Instead of specifying a column type as `String` or `Int`, the `@cipherstash/stack-prisma` package provides special types for encrypted columns.

For example, a `User` model might use the `Text`, `Date`, and `Json` types from the cipherstash extension to store encrypted versions of the equivalent Prisma types.

```prisma
model User {
  id String @id
  email cipherstash.Text()        // encrypted string
  birthday cipherstash.Date()     // encrypted Date
  preferences cipherstash.Json()  // encrypted JSON "blob"
}
```

Unlike regular plaintext types, CipherStash encrypted types are not searchable. To enable searchable encryption on a field, you can use a type variant.

For example, for encrypted text values that support sorting, simple lookups, and fuzzy text search, use the TextSearch type:

```prisma
model User {
  id String @id
  email cipherstash.TextSearch()  // *searchable* encrypted string
  // ..
}
```

The contract is the single source of truth for which fields are encrypted and how they can be queried (or not).
Prisma 8 manages the encrypted column types, database extension, and searchable indexes through the same migration lifecycle as the rest of your schema.

Each encrypted type and all its variants are installed in Postgres automatically from the [Encrypt Query Language][eql] (EQL) package.
EQL also provides functions and operators that Prisma 8 uses for query comparisons and sorting.

### Encrypted queries that look like Prisma queries

Queries are performed using type-safe operator functions provided by the extension:

```typescript
// Find user with specific email address
const users = await db.orm.public.User
  .where(user =>
    user.email.eqlEq("dan@example.com")
  )
  .all()
```

The string "dan@example.com" is encrypted inside the application.

PostgreSQL receives an encrypted query and evaluates it against the encrypted index. The plaintext query never reaches the database.

The same model supports free-text and range queries:

```typescript
// Search for email addresses containing 'dan'
// and order results
const users = await db.orm.public.User
  .where(user =>
    user.email.eqlMatch("dan")
  )
  .orderBy((u) => eqlAsc(u.email))
  .all()
```

These operators are deliberately namespaced. Standard SQL equality against randomised ciphertext would produce the wrong result, so Prisma 8 prevents ordinary comparison operators from being used accidentally on CipherStash columns.

### Explicit decryption

Query results remain encrypted until the application explicitly decrypts them.

```typescript
import { decryptAll } from "@cipherstash/stack-prisma/runtime"

const users = // query

await decryptAll(users)
const email = await users[0].email.decrypt()
```

This creates a clear boundary around plaintext access.

Encrypted values cannot accidentally appear in a JSON response, application log, or AI prompt simply because an object was serialized. Reaching plaintext requires an explicit decryption operation in application code.

## Use cases

Searchable encryption isn't just about protecting databases.

It's about proving control over sensitive data.

Organizations use CipherStash to:

* give AI agents access to only the information a user is authorized to see
* satisfy enterprise vendor security reviews
* meet regulatory and contractual obligations around sensitive data
* reduce the impact of application or database compromise
* enforce data sovereignty and residency requirements

As regulators and enterprise customers increasingly expect evidence of technical controls, encryption is becoming less about protecting storage and more about proving who could—and couldn't—access sensitive information.


## Built for the Prisma stack

The integration works with Prisma Postgres and fits naturally into applications deployed with Prisma Compute.

{% figure src="/images/blog/prisma-next-studio-encrypted-columns.png" alt="Encrypted columns in Prisma Studio" caption="Prisma Studio browsing the same table an attacker would: the encrypted columns are ciphertext, all the way down." /%}

{% figure src="/images/blog/prisma-next-postgres-queries.png" alt="Encrypted queries in the Prisma Postgres query dashboard" caption="Encrypted queries in the Prisma Postgres dashboard, running sub-millisecond alongside everything else." /%}

## Get started in four steps

The fastest way to understand searchable encryption is to run it. You don't need to migrate an existing application to try it — scaffold a throwaway app, encrypt a field, query it, decrypt it, and then decide where it belongs in your own codebase.

{% eyebrow index="01" label="Scaffold" heading="Create a Prisma 8 app" level=3 /%}

```sh
npx create-prisma@next
```

This gives you a fresh project with the contract-first workflow already wired up: a `contract.prisma` describing your models, and a migration loop that plans and applies the changes needed to match it.

{% eyebrow index="02" label="Database" heading="Create a Prisma Postgres database" level=3 /%}

The scaffold connects your app to Prisma Postgres as part of setup. There's no proxy to deploy and no database extension to install by hand — Prisma 8 installs the [EQL][eql] package for you during migration, alongside your own schema.

If you'd rather stay local while you experiment, `npx prisma dev` gives you a local Postgres instance instead.

{% eyebrow index="03" label="Install" heading="Add the CipherStash extension" level=3 /%}

```sh
npx stash init --prisma
```

This detects Prisma 8, installs `@cipherstash/stack` and `@cipherstash/stack-prisma` pinned to the CLI release, and signs you in to CipherStash. Register the extension pack in your Prisma config:

```typescript
// prisma-next.config.ts
import cipherstash from '@cipherstash/stack-prisma/control'

export default defineConfig({
  // ...your existing config
  extensionPacks: [cipherstash],
})
```

Then wire the runtime, which builds the encryption client from your CipherStash credentials and hands Prisma the extensions it needs:

```typescript
// src/db.ts
import { cipherstashFromStack } from '@cipherstash/stack-prisma/v3'
import postgres from '@prisma-next/postgres/runtime'
import type { Contract } from './prisma/contract.d'
import contractJson from './prisma/contract.json' with { type: 'json' }

const cipherstash = await cipherstashFromStack({ contractJson })

export const db = postgres<Contract>({
  contractJson,
  extensions: cipherstash.extensions,
  middleware: cipherstash.middleware,
})
```

That's the whole setup. From here, encryption is just another part of your contract.

{% eyebrow index="04" label="Encrypt" heading="Encrypt, query, and decrypt" level=3 /%}

Change a field to a cipherstash type:

```prisma
model User {
  id String @id
  email cipherstash.TextSearch()  // searchable encrypted string
}
```

Then emit the contract, plan the migration, and apply it:

```sh
npx prisma-next contract emit
npx prisma-next migration plan --name initial
npx prisma-next migrate
```

The planner reads your contract, sees the new encrypted field, and emits a reviewable TypeScript migration that adds the column. The EQL bundle installs in the same sweep — the extension contributes its own migration history alongside your application's, so there's no separate install step.

Now write a value. It's encrypted inside your application before it ever reaches Postgres:

```typescript
import { EncryptedString } from "@cipherstash/stack-prisma/runtime"

await db.orm.public.User.create({
  id: "user-0",
  email: EncryptedString.from("dan@example.com"),
})
```

Query it without decrypting anything:

```typescript
const users = await db.orm.public.User
  .where(user => user.email.eqlEq("dan@example.com"))
  .all()
```

And when you actually need the plaintext, ask for it explicitly:

```typescript
import { decryptAll } from "@cipherstash/stack-prisma/runtime"

await decryptAll(users)
const email = await users[0].email.decrypt()
```

Four steps, and the value was never in plaintext in your database, in your query, or in your logs.

You can also skip the scaffold and clone the [example app](https://github.com/cipherstash/stack/tree/main/examples/prisma) instead.

{% callout type="note" title="Prisma 8 RC1" %}
Prisma 8 is currently a release candidate. See [the Prisma 8 announcement](https://pris.ly/pn-cipherstash) for what's in it and where it's heading.
{% /callout %}



## Data Level Access Control

Searchable encryption does more than protect sensitive values.
It changes what applications can prove about data access.

When every value is encrypted independently, every query is encrypted, and every decryption is authorized against the identity making the request, access control moves from the application perimeter to the data itself.

This is known as Data Level Access Control (DLAC).

{% callout type="important" title="The DLAC question" %}
Instead of asking "Who can access the database?", DLAC asks a more important question:

**Who can access this specific piece of data, right now?**
{% /callout %}

That shift matters as organizations adopt AI agents, work with third-party services, and face increasing demands to demonstrate technical controls rather than simply document them.

Searchable encryption is what makes DLAC practical.
Without searchable encryption, encrypted data becomes difficult to use. Without identity-bound decryption, encryption can't enforce who is allowed to see a value.
Together, they make it possible to build applications where access is enforced cryptographically, not just by convention.

This is only the beginning.
In the coming months, we'll introduce Access Intelligence, making those cryptographic decisions observable so that developers, security teams, and auditors can understand not only who accessed sensitive data but also why, when, and under what authority.

That's where we believe data security is heading, not just encrypted databases, but data that can enforce and prove its own access controls.

[Read the CipherStash for Prisma docs →](https://cipherstash.com/docs/stack/cipherstash/encryption/prisma-next)


[benchmarks]: https://github.com/cipherstash/benches
[eql]: https://cipherstash.com/docs/reference/eql

## Related blog posts

- [CipherStash + Prisma Next: Data Level Access Control, declared in your contract](https://cipherstash.com/blog/cipherstash-prisma-next-data-level-access-control.md) — Add searchable field-level encryption to a Prisma Next app the same way you'd add any other field — declared once in your contract, evolved the same way.
- [Introducing @cipherstash/stack](https://cipherstash.com/blog/introducing-cipherstash-stack.md) — Building blocks for Data Level Access Control in TypeScript

