Skip to content

Commit cf2c57c

Browse files
authored
feat: support protect-ffi 0.30 JSON queries (#711)
* feat: support protect-ffi 0.30 JSON queries * fix: address PR 711 review feedback * fix: regenerate Prisma example migration * test: configure Supabase EQL 3.0.2 boundary * fix: address final PR 711 feedback * feat: add JSON selector ordering parity * fix: resolve shared JSON integration suite * fix: preserve Prisma JSON selector hashes * fix: reconcile PR 711 with EQL v3-only main
1 parent f0b20d3 commit cf2c57c

138 files changed

Lines changed: 5115 additions & 4671 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
'@cipherstash/stack': minor
3+
'@cipherstash/stack-drizzle': minor
4+
'@cipherstash/stack-supabase': patch
5+
'@cipherstash/prisma-next': minor
6+
'stash': patch
7+
---
8+
9+
Upgrade Stack to `@cipherstash/protect-ffi` 0.30 and EQL 3.0.2.
10+
11+
Prisma Next includes a versioned EQL 3.0.2 upgrade migration, so databases
12+
that have already recorded the original EQL v3 baseline still install the new
13+
domains and functions.
14+
15+
Encrypted JSON now uses the `public.eql_v3_json_search` storage domain and
16+
`eql_v3.query_json` query domain. Drizzle selector equality uses exact,
17+
GIN-indexable value-selector containment, while selector range comparisons use
18+
a ciphertext-free path selector plus string/number query term. Prisma Next gains
19+
the equivalent `eqlJsonPathEq`, `eqlJsonPathNeq`, `eqlJsonPathGt`,
20+
`eqlJsonPathGte`, `eqlJsonPathLt`, and `eqlJsonPathLte` operators. Selector
21+
Selector-based `ORDER BY` is available as
22+
`ops.selector(column, path).asc()/desc()` in Drizzle
23+
and `eqlJsonPathAsc(column, path)` / `eqlJsonPathDesc(column, path)` in Prisma
24+
Next; both lower to `ORDER BY eql_v3.ord_term` over the selected entry.
25+
26+
If you call `encryptQuery` with an explicit `queryType`, note that
27+
`steVecTerm` now produces a scalar JSON ordering term. It no longer means
28+
structural containment; use the recommended `searchableJson` query type with
29+
an object or array for containment, or `steVecValueSelector` with
30+
`{ path, value }` for exact equality at a path.
31+
32+
The FFI now rejects free-text needles shorter than the configured n-gram size
33+
at the core query-encryption boundary, including callers that bypass adapter
34+
guards.
35+
36+
This EQL release changes the SteVec storage format. Existing EQL v3 encrypted
37+
JSON rows must be re-encrypted before they can be queried with the new domain.
38+
Legacy EQL v2 `searchableJson()` schemas are rejected during client setup
39+
because the old selector envelope can no longer be emitted; migrate them to the
40+
v3 `types.Json` domain.
41+
42+
EQL 3.0.2 requires typed query-domain operands for encrypted free-text and JSON
43+
operators. PostgREST cannot express those casts, so Supabase v3 fails fast for
44+
`matches()`, encrypted `contains()`, and `selectorEq()`/`selectorNe()` instead
45+
of placing a decryptable storage envelope in a GET query string that the new
46+
SQL surface will reject. Use the Drizzle or Prisma Next adapter, or a carefully
47+
scoped direct SQL/RPC path.

examples/prisma/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ A single `User` model with one column per plaintext family, exercised end-to-end
1111
| `accountId` | `cipherstash.BigIntOrd()` | `public.eql_v3_bigint_ord` | equality + order/range (true `bigint`) |
1212
| `birthday` | `cipherstash.DateOrd()` | `public.eql_v3_date_ord` | equality + order/range |
1313
| `emailVerified` | `cipherstash.Boolean()` | `public.eql_v3_boolean` | storage-only (no operators) |
14-
| `preferences` | `cipherstash.Json()` | `public.eql_v3_json` | `eqlJsonContains` (`@>`) |
14+
| `preferences` | `cipherstash.Json()` | `public.eql_v3_json_search` | `eqlJsonContains` (`@>`) |
1515

1616
📖 See the [Prisma Next encryption docs](https://cipherstash.com/docs/stack/cipherstash/encryption/prisma-next) for the full operator reference, security model, and known limitations.
1717

@@ -102,7 +102,7 @@ Found 2 user(s) with a dark-theme preference.
102102

103103
Two v3 behaviours worth noticing in that output:
104104

105-
- **`eqlMatch` is fuzzy bloom-filter token matching** (`eql_v3.contains`), not SQL `ILIKE` — the needle's tokens must appear in the ciphertext's index. Plain literal terms like `example.com` are the idiomatic needle: leading/trailing `%` are stripped for compatibility, while an interior `%` or any `_` throws (the tokenizer would treat them as ordinary characters), as do needles shorter than the match tokenizer length.
105+
- **`eqlMatch` is fuzzy bloom-filter token matching** (`eql_v3.matches`), not SQL `ILIKE` — the needle's tokens must appear in the ciphertext's index. Plain literal terms like `example.com` are the idiomatic needle: leading/trailing `%` are stripped for compatibility, while an interior `%` or any `_` throws (the tokenizer would treat them as ordinary characters), as do needles shorter than the match tokenizer length.
106106
- **`eql_v3_boolean` is storage-only.** It round-trips `true`/`false` losslessly but surfaces no search operators — calling one throws `EncryptionOperatorError`. Filter on a searchable column and decrypt the boolean from the result set.
107107

108108
## References

0 commit comments

Comments
 (0)