Introducing @cipherstash/stack

CipherStash Stack is a set of composable building blocks for adding Data Level Access Control to the TypeScript apps you already have: encryption in use, key management, a transparent SQL proxy, identity-bound auth, and agent skills.
Data Level Access Control (DLAC) enforces access on individual data values: each one carries its own encryption, and the right to decrypt it is bound to an identity. Encryption at rest protects the disk; row-level security gates which rows a user sees — but in both, the database still reads every value in the clear. DLAC protects the data itself, and that protection travels with the value wherever it goes.
CipherStash Stack is our set of composable building blocks for adding DLAC to the TypeScript app you already have, one field at a time.
What agents change about data access
Application security has always assumed one kind of principal: a human behind a session. An agent doesn't fit — it acts for a user without being one, chains tools, and spreads data through prompts, logs, and traces. That's not misuse; it's how agents work, and a poor fit for controls built around a person reading data once per request.
DLAC doesn't depend on that assumption. Because protection lives in each value, it holds whoever the principal is — human or agent.
Five pieces: encryption, keys, proxy, auth, and skills
Stack is five composable pieces. You don't have to adopt them all on day one. Most teams start with one or two and grow into the rest.
- Encryption. Value-level, searchable encryption for any field. Postgres filters, sorts, and joins run directly over the ciphertext via EQL, so your data stays encrypted at rest, in transit, and in use.
- Key management. ZeroKMS, our hyper-fast key service backed by AWS KMS. A unique data key per value, with no caching gymnastics, up to 14x faster than AWS KMS direct.
- Proxy. CipherStash Proxy sits between your app and Postgres and applies encryption transparently. Drop it in front of a legacy app with zero code changes, or use it as a debugging tool when you want to inspect what your SDK is producing.
- Auth. OIDC integration with caching and claims mapping, so an end-user's identity is what authorises a decrypt, not a service account or a shared key. Built on the CipherStash Token Service.
- Skills. Agent skills for encryption and data security with CipherStash Stack. The same building blocks your team uses are documented in a form your agents can pick up correctly the first time.
Get a field encrypted in under 15 minutes
The fastest way in is the stash CLI:
npx stash initIt'll walk you (or an agent in your editor) through choosing a database connection, generating an encryption schema, and installing the EQL extension into your Postgres. Same flow whether you drive it manually step-by-step or let an agent handle it end-to-end. From a fresh checkout to your first encrypted field, most teams are there in under fifteen minutes.
Stack is designed to be adopted one field at a time. Pick a single sensitive column, an email address, a phone number, a free-text note, encrypt it, ship it, then come back next sprint and do the next one. No big-bang migrations.
01 / Encryption
Encrypting a field, then querying it two ways
Declare which fields are encrypted in a schema:
// src/encryption/schema.tsimport { encryptedTable, encryptedColumn } from '@cipherstash/stack/schema';export const users = encryptedTable('users', { email: encryptedColumn('text'), meta: encryptedColumn('json'),});Searchable encryption is opt-in and specified by query type on each field:
// src/encryption/schema.tsexport const users = encryptedTable('users', { // serachable encrypted text email: encryptedColumn('text').equality().freeTextSearch(), // searchable encrypted JSON meta: encryptedColumn('json').searchableJson(),});Simple equality lookups, ILIKE style free-text search, range queries, ORDER BY and even operations on JSON objects are supported.
From your TypeScript application, encrypt and query through the SDK:
import { client } from '@cipherstash/stack';import { users } from './encryption/schema';await db.insert(users).values({ email: await client.encrypt('[email protected]', { column: users.email }),});// encrypted query termconst needle = await client.encrypt('[email protected]', { column: users.email });// use it in a queryconst rows = await db .select() .from(users) .where(eq(users.email, query));Stack composes with what you already use. Prisma Next declares encrypted fields directly in the data contract. Drizzle gets encrypted column types and operators that fit alongside the existing query builder. Supabase wraps with encryptedSupabase and keeps .eq(), .like(), .order() working.
Not on Postgres? DynamoDB is supported today as well, with more integrations to follow.
02 / Key management
ZeroKMS: a key for every value
Every encrypted value gets its own data key. That should be expensive — millions of keys, one per value, fetched on every read — but ZeroKMS makes it cheap. Backed by AWS KMS, it mints and retrieves keys in bulk, up to 14× faster than calling AWS KMS directly, with no key caching for your application to get wrong. Per-value keys are what make identity-bound decryption and a precise, cryptographic audit trail possible in the first place.
03 / Proxy
A no-code escape hatch
If you don't want to touch the application — say you need to reach the same data from a different service, from psql, or from a BI tool — point at Proxy and write plain SQL:
-- Connecting through CipherStash Proxy on :6432INSERT INTO users (email) VALUES ('[email protected]');SELECT * FROM users WHERE email = '[email protected]';SELECT * FROM users WHERE email ILIKE '%@example.com';Proxy rewrites the statement, encrypts the parameters, executes against Postgres, and decrypts the result on the way back. Your application sees a regular Postgres connection. And it's not just a debugging aid — teams run Proxy permanently in production, directly in their application's hot path.
Whether you use Typescript or Proxy (or both!) your data remains securely accessible. Two paths in, depending on what fits your team.
04 / Auth
Decryption bound to a real identity
Encryption controls where plaintext exists; auth controls who can bring it back. Stack authorises every decryption against a real end-user identity from your own identity provider — never a shared service account, never a long-lived key.
You wire this up once. In your workspace settings, add your existing IDP as an OIDC provider — choose the vendor, paste the issuer URL — and from then on the CipherStash Token Service verifies tokens, caches them, and maps claims on every encrypt and decrypt. The same model extends to agents, which carry their own identity rather than borrowing a human's.

05 / Skills
Encryption your agents get right
The fastest way to get encryption wrong is to hand-roll it — and an agent writing your code will hand-roll it confidently. Stack ships agent skills: the same building blocks, documented in a form an agent in your editor can pick up and apply correctly the first time. npx stash init is already agent-drivable, and the skills cover everything after it, so encryption decisions land as reviewable code in a pull request instead of improvised guesses.
Add the skills to your project in one command:
npx skills add cipherstash/stackA billion values in production
The numbers:
- 454μs median for an encrypted exact-match query
- 1.46ms1 median for an encrypted range query
- Up to 14× faster than AWS KMS direct, for bulk encrypt/decrypt against ZeroKMS
The same primitives are protecting over a billion sensitive values in production right now — we crossed that milestone earlier this year. Three of the teams running on Stack today:
- Amber Electric. Climate-focused energy retailer expanding internationally. Calls the Encryption SDK from TypeScript Lambdas against Postgres and DynamoDB to satisfy GDPR and per-market compliance without re-platforming
- Journalia. AI-driven medical transcription for European healthcare. Identity-bound encryption so only the doctor who created a patient note can decrypt it — special-category data protected cryptographically rather than by policy
- BNDRY. Financial-crime risk and compliance platform. Runs CipherStash Proxy in their Kubernetes cluster to protect customer JSONB data, including encrypted search over form responses
Three of the most regulated industries in the world, on the same set of building blocks.
Where Stack fits
If you've already evaluated transparent database encryption, row-level security, or an external tokenisation vault, here's the placement.
Transparent database encryption protects against stolen disks. Against a stolen credential, it does nothing — the database still hands plaintext to anyone who can log in. Row-level security takes the problem a step further and gates access by policy, but the policy lives in the database's own configuration, and the database itself still reads plaintext to evaluate it. External tokenisation vaults solve the problem differently again: by moving the plaintext out of your application entirely. The cost is that every read becomes a round trip to the vault, and the rest of your code gradually starts to revolve around it.
CipherStash Stack puts ciphertext in your existing database and keeps the queries working. Per-value keys, identity-bound decryption, and a cryptographic audit trail are properties of the encryption itself — not properties of the configuration that wraps it. A misconfigured rule produces ciphertext; it does not leak plaintext.
We'll be publishing deeper comparisons of Stack against each of these approaches — transparent database encryption, row-level security, and tokenisation vaults — in upcoming articles.
Agents are first-class identities, not service accounts
Stack treats agents as first-class identities. An agent gets its own keys and its own policy, distinct from the user it's acting for. Because enforcement happens in the encryption layer, the agent only ever sees decrypted bytes for the fields it is explicitly authorised to read, scoped to the user and the task at hand. Everything else stays as ciphertext: through tool calls, prompts, logs, traces, and whatever context gets snapshotted along the way.
A prompt-injected agent cannot exfiltrate what it does not hold keys for. A background worker that should only summarise one user's chats cannot accidentally pull another's. Row-level security still bounds what rows an agent can fetch; CipherStash bounds what it can actually read. The two compose.
Get Started
npx stash initnpx skills add cipherstash/stackCheck out the docs, the GitHub or come and say hi in Discord.
Measured against a 10-million-row dataset. Methodology and raw results are in our open-source benchmark suite (full report).
↩