npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cipherstash/stack-supabase

v1.1.1

Published

CipherStash Stack Supabase integration: transparent, searchable field-level encryption for Supabase.

Readme

Why

Anyone with database access — a leaked service_role key, a misconfigured RLS policy, a SQL injection, a stolen backup — normally sees everything. CipherStash encrypts each value with its own key before it leaves your app, so what reaches Supabase is ciphertext; you can only decrypt what you're explicitly authorized to, and every decryption is audited.

The trick is queries still work: searchable encrypted terms let equality and range filters run against native Postgres indexes without decrypting the table. RLS stays exactly where it is — this complements authorization, it doesn't replace it. Security architecture →

Encrypted columns. Real Supabase queries.

The email and amount columns below are stored as ciphertext with a unique key per row — and the same Supabase.js calls keep working, because the filters run on the ciphertext:

import { encryptedSupabase } from '@cipherstash/stack-supabase'

const es = await encryptedSupabase(supabaseUrl, supabaseKey)

// Insert — encrypted transparently on the way in
await es.from('users').insert({ email: '[email protected]', amount: 30 })

// Query — filters encrypted on the way in, results decrypted on the way out
const { data } = await es
  .from('users')
  .select('id, email, amount')
  .eq('email', '[email protected]') // encrypted equality — runs on ciphertext
  .gte('amount', 10)                // encrypted range

You can also wrap an existing client: await encryptedSupabase(supabaseClient, options).

| Query type | Filters | Notes | |---|---|---| | Equality | .eq, .neq, .in, .match({ … }) | on equality-capable domains | | Range | .gt / .gte / .lt / .lte | on *Ord domains | | Ordering | .order() | on OPE-backed encrypted ordering columns (and plaintext columns) | | Compound | .or(…) | over the filters above |

Each column's query capabilities are fixed by its eql_v3_* type, so an unsupported operation is rejected loudly instead of silently scanning.

PostgREST limitation (EQL 3.0.4). Encrypted free-text matches(), encrypted JSON contains(), and selectorEq()/selectorNe() need typed query-domain casts that PostgREST cannot express, so they fail fast with this EQL release — the requirement began in EQL 3.0.2 and remains in 3.0.4. Use the Drizzle or Prisma adapter, or a carefully scoped SQL/RPC path, for those query shapes. Plaintext like/ilike on encrypted columns is rejected by design.

Quick start

About five minutes, starting on the free developer tier (sign up). The setup wizard handles authentication, the EQL install, and your schema:

npx stash init

Or install manually (this package depends on @cipherstash/stack; install all three):

npm install @cipherstash/stack @cipherstash/stack-supabase @supabase/supabase-js

Full guide: Supabase quickstart →

How the wrapper works

encryptedSupabase introspects your database at connect time: it discovers the native public.eql_v3_* column domains, so there is no schema argument and no client-side column config to maintain — select('*') just works, inserts and updates encrypt automatically, and reads decrypt automatically.

Introspection needs a direct Postgres connection (DATABASE_URL), so pg is an optional peer dependency and the factory cannot run in an edge Worker or the browser — construct it in your server-side code.

It runs alongside Supabase Auth and RLS, and supports identity-locking encryption — binding a row's data key to the signed-in user's JWT claim — via the same lock-context API as the rest of the Stack.

encryptedSupabaseV3 remains as a @deprecated, type-identical alias of encryptedSupabase, so existing imports keep working.

How it works

Every value is encrypted into an EQL payload: the ciphertext plus the searchable terms its column type declares — an HMAC term for equality, an order-preserving term for range and sorting. The EQL SQL bundle defines the Postgres domains and operators, so an encrypted .eq() resolves to a comparison of equality terms and engages a functional index. Keys come from ZeroKMS — one per value — so a leaked key or a dumped table never exposes more than it should, and the EQL install needs no superuser (it works on cloud-hosted Supabase as-is).

Docs