Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
a65856d
docs(stack): design spec for eql_v3 text_search schema DSL (increment 1)
tobyhede Jun 30, 2026
73c1ab3
docs(stack): implementation plan for eql_v3 text_search schema DSL
tobyhede Jun 30, 2026
71e0198
docs(stack): incorporate plan review feedback for eql_v3 text_search
tobyhede Jun 30, 2026
35bb46c
docs(stack): scope v3 to working client integration + apply batch-2 r…
tobyhede Jun 30, 2026
a822660
docs(stack): apply batch-3 plan review (widen internal consumers, sco…
tobyhede Jun 30, 2026
445be40
docs(stack): split query column contract so encryptQuery rejects non-…
tobyhede Jun 30, 2026
1713caf
docs(stack): pin bulk-encrypt widen-site + note WASM v3 boundary
tobyhede Jun 30, 2026
840dc66
docs(stack): pin concrete BuiltMatchIndexOpts type for v3 match opts
tobyhede Jun 30, 2026
d8d659e
docs(stack): fix @ts-expect-error placement in negative type tests
tobyhede Jun 30, 2026
2fac5c8
feat(stack): add eql_v3 text_search column builder
tobyhede Jun 30, 2026
aaa88c9
feat(stack): add eql_v3 encryptedTable and buildEncryptConfig
tobyhede Jun 30, 2026
888c170
feat(stack): wire @cipherstash/stack/schema/v3 export subpath
tobyhede Jun 30, 2026
7a32262
test(stack): type-level tests for eql_v3 schema DSL + scoped CI typec…
tobyhede Jun 30, 2026
8c221d5
feat(stack): widen public client types so v3 builders work with the c…
tobyhede Jun 30, 2026
1aa9a11
chore(stack): changeset for eql_v3 text_search DSL (minor)
tobyhede Jun 30, 2026
656841f
style(stack): biome format v3 tests + drop now-unused EncryptedTable …
tobyhede Jun 30, 2026
28b3cfc
fix(stack): guard v3 encryptedTable against reserved column names
tobyhede Jun 30, 2026
4fad589
fix(stack): resolve column name structurally in wasm-inline encrypt e…
tobyhede Jun 30, 2026
8721ff7
feat(stack): guard v3 buildEncryptConfig against duplicate table names
tobyhede Jun 30, 2026
4515807
feat(stack): extend v3 reserved-column guard to inherited prototype keys
tobyhede Jun 30, 2026
ece1b7e
docs(stack): note text_search defaults to equality, needs queryType:'…
tobyhede Jun 30, 2026
359dd55
test(stack): cover eql v3 typed schema domains
tobyhede Jul 1, 2026
cae3eb7
feat(stack): add eql v3 domain builders for all generated SQL domains
tobyhede Jul 1, 2026
7f646bb
feat(stack): support v3 tables and Date/bigint in client encryption
tobyhede Jul 1, 2026
282c462
test(stack): stabilize v3 client, pg, and wasm-inline coverage
tobyhede Jul 1, 2026
b8a3d20
chore(stack): add changeset for eql v3 typed schema
tobyhede Jul 1, 2026
db2840f
docs: add eql v3 typed schema plan and query API walkthrough
tobyhede Jul 1, 2026
d142d9e
fix(stack): address PR review feedback for eql v3 typed schema
tobyhede Jul 1, 2026
af2d04e
feat(stack): strongly-typed EQL v3 client surface (@cipherstash/stack…
tobyhede Jul 1, 2026
ba7f467
fix(stack): use string plaintext for eql v3 int8 domains
tobyhede Jul 1, 2026
430f487
fix(stack): address code review findings for eql v3 typed client
tobyhede Jul 1, 2026
b25bf4a
fix(stack): key v3 encrypt config by DB column name, not JS property
tobyhede Jul 1, 2026
ebf2c74
fix(stack): match v3 model fields by JS property, encrypt by DB name
tobyhede Jul 1, 2026
64a1def
ci(stack): add blocking FTA complexity gate for EQL v3
tobyhede Jul 1, 2026
39ac793
test(stack): type-driven v3 domain test matrix
tobyhede Jul 2, 2026
3b01be1
feat(stack): equality-via-ORE fix + live v3 domain coverage
tobyhede Jul 2, 2026
be6af18
test(stack): live Postgres SQL coverage for all 35 v3 domains
tobyhede Jul 2, 2026
b0f4f4d
fix(stack): address CodeRabbit review findings on eql v3 typed client
tobyhede Jul 2, 2026
45782f0
test(stack): harden v3 CJS export check and preserve run() transcript…
tobyhede Jul 2, 2026
dfb2890
test(stack): disable timestamptz and matrix-live-pg tests failing on CI
tobyhede Jul 2, 2026
53cf854
fix(stack): wrap ciphertext params with sql.json() in matrix-live-pg
tobyhede Jul 2, 2026
5ccf13d
Merge pull request #540 from cipherstash/feat/eql-v3-test-matrix
tobyhede Jul 3, 2026
0d9a107
docs(stack): design for Stryker v3 blocking CI gate
tobyhede Jul 2, 2026
5e228ce
docs(stack): confirm lean, single-workflow Stryker v3 gate
tobyhede Jul 2, 2026
c20cecd
refactor(stack): extract EQL v3 DSL into eql/v3 types module
tobyhede Jul 3, 2026
380be64
fix(stack): guard against duplicate v3 column DB names in build()
tobyhede Jul 3, 2026
684bdbc
fix(stack): export v3 Buildable* contracts from public types entrypoint
tobyhede Jul 3, 2026
a672451
test(stack): migrate v3-matrix suite to eql/v3 types namespace
tobyhede Jul 3, 2026
790f2f6
test(stack): cover lock-context encrypt guards and wasm newClient shape
tobyhede Jul 3, 2026
4f0982d
test(stack): re-enable matrix-live-pg live SQL coverage (stale skip)
tobyhede Jul 3, 2026
c627aba
fix(stack): emit hm index for text order domains (eql_v3 SQL domain r…
tobyhede Jul 3, 2026
c7690e3
test(stack): v3 lock-context coverage + text_match/no-op/empty-config…
tobyhede Jul 3, 2026
0eebebb
chore(stack): EQL v3 maintainability follow-ups from the #547 review
freshtonic Jul 4, 2026
beddbf0
test(stack): cover empty text order pg rejection
tobyhede Jul 5, 2026
b0b9789
test(stack): avoid empty text search pg seed
tobyhede Jul 5, 2026
dcd0fe7
test(stack): normalize date storage decrypt assertion
tobyhede Jul 5, 2026
348e4fa
Merge pull request #541 from cipherstash/feat/eql-v3-types-module
tobyhede Jul 6, 2026
35bd9a8
fix(stack): address EQL v3 types-module review follow-ups
tobyhede Jul 6, 2026
63fe076
Merge pull request #559 from cipherstash/fix/eql-v3-review-followups
tobyhede Jul 6, 2026
615e09c
docs(stack): design for EQL v3 Drizzle concrete-type support
tobyhede Jul 6, 2026
6469cc7
chore(stack): upgrade EQL v3 bundle — timestamptz -> timestamp
tobyhede Jul 3, 2026
c93b694
fix(stack): give v3 timestamp domains cast_as 'timestamp' (preserve t…
tobyhede Jul 3, 2026
893d6ed
test(stack): cover timestamp cast_as wasm mapping
tobyhede Jul 6, 2026
fc33d6b
test(stack): pin timestamp time-of-day preservation + dedupe date-lik…
tobyhede Jul 6, 2026
a26655f
docs(stack): design for EQL v3 Drizzle concrete-type support
tobyhede Jul 6, 2026
4d5bf63
Merge pull request #542 from cipherstash/feat/eql-v3-timestamp-bundle
tobyhede Jul 7, 2026
1fdb1f5
Update EQL v3 concrete type names
tobyhede Jul 8, 2026
15e63a8
Fix EQL v3 concrete domains
tobyhede Jul 8, 2026
afe550f
Adjust EQL v3 changeset bump
tobyhede Jul 8, 2026
1173da7
Fix bulk decrypt error code test
tobyhede Jul 8, 2026
bf43681
Sync EQL v3 concrete domain names
tobyhede Jul 8, 2026
f84e4c1
chore(stack): upgrade @cipherstash/protect-ffi 0.27 → 0.28
tobyhede Jul 8, 2026
175b1ed
chore(stack): add protect-ffi 0.28 content (bump, lockfile, test, cha…
tobyhede Jul 8, 2026
16c9dab
feat(stack): v3 drizzle plain-jsonb codec
tobyhede Jul 6, 2026
bb891f0
feat(stack): v3 drizzle column wrapper and detection
tobyhede Jul 6, 2026
1867599
feat(stack): v3 drizzle types namespace
tobyhede Jul 6, 2026
31f1ac2
feat(stack): v3 drizzle schema extraction
tobyhede Jul 6, 2026
34e11b8
feat(stack): v3 drizzle sql dialect
tobyhede Jul 6, 2026
930f440
feat(stack): v3 drizzle capability-checked operators
tobyhede Jul 6, 2026
a67e21b
feat(stack): export @cipherstash/stack/eql/v3/drizzle + changeset
tobyhede Jul 6, 2026
ca411f4
test(stack): v3 drizzle live postgres integration
tobyhede Jul 6, 2026
54ee973
chore(stack): format v3 drizzle tests
tobyhede Jul 6, 2026
fc29a7a
fix(stack): bind v3 drizzle columns without name collisions
tobyhede Jul 6, 2026
9ebebab
fix(stack): detect v3 drizzle columns via getSQLType
tobyhede Jul 6, 2026
6f5ac16
fix(stack): align v3 drizzle detection and ore equality
tobyhede Jul 6, 2026
160258b
fix(stack): address v3 drizzle review follow-ups
tobyhede Jul 6, 2026
dc77ac9
test(stack): seed v3 drizzle live test through drizzle
tobyhede Jul 6, 2026
c0700f7
Align EQL v3 Drizzle integration
tobyhede Jul 6, 2026
ce04bda
test: drive drizzle v3 operators from matrix
tobyhede Jul 7, 2026
2daaba5
test: expand live drizzle v3 matrix coverage
tobyhede Jul 7, 2026
bdf7d43
test(stack): strengthen v3 live coverage (range/boundary/lock-context…
tobyhede Jul 7, 2026
57e4483
test(stack): address v3 drizzle review coverage
tobyhede Jul 7, 2026
17857ee
feat(stack): re-enable EQL v3 bigint domain family on protect-ffi 0.28
tobyhede Jul 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/eql-v3-bigint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@cipherstash/stack': minor
---

Re-enable the EQL v3 bigint domain family now that native bigint round-tripping landed in `@cipherstash/protect-ffi` 0.28. Adds the `types.Bigint` / `types.BigintEq` / `types.BigintOrd` / `types.BigintOrdOre` factories (concrete Postgres domains `eql_v3.int8`, `eql_v3.int8_eq`, `eql_v3.int8_ord`, `eql_v3.int8_ord_ore`), their `EncryptedBigint*Column` classes, and full test-matrix + Drizzle operator coverage. Bigint columns take and decrypt to a JS `bigint`; i64 bounds (`-2^63 … 2^63 - 1`) are enforced at the protect-ffi boundary.
17 changes: 17 additions & 0 deletions .changeset/eql-v3-drizzle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"@cipherstash/stack": minor
---

Add EQL v3 Drizzle support at `@cipherstash/stack/eql/v3/drizzle`. A Drizzle-native
`types` namespace (same PascalCase names as `@cipherstash/stack/eql/v3`) declares
encrypted columns whose Postgres type is the semantic `eql_v3.<domain>`; the concrete
type drives the legal query operators. `createEncryptionOperatorsV3` provides
capability-checked `eq`/`ne`/`gt`/`gte`/`lt`/`lte`/`between`/`contains`/`inArray`/
`asc`/`desc`/`and`/`or` that emit the latest two-argument `eql_v3` SQL functions with
full-envelope operands, and
`extractEncryptionSchemaV3` rebuilds the schema for `EncryptionV3`. The existing v2
`@cipherstash/stack/drizzle` integration is unchanged.

The v3 text-search helper is `contains`; obsolete `like`/`ilike` helpers are not
exposed because v3 free-text search is token containment rather than SQL wildcard
matching.
5 changes: 5 additions & 0 deletions .changeset/eql-v3-ffi-0-28-concrete-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@cipherstash/stack': minor
---

Upgrade `@cipherstash/protect-ffi` to 0.28.0 and update EQL v3 concrete Postgres domain names to match the SQL fixture (`integer*`, `smallint*`, `bool`, `real*`, and `double*`). The public factories remain semantic (`Integer`, `Smallint`, `Boolean`, `Real`, `Double`) while their concrete domains change, so this is a minor release.
18 changes: 18 additions & 0 deletions .changeset/eql-v3-text-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
"@cipherstash/stack": minor
---

Add the EQL v3 `text_search` authoring DSL on a new `@cipherstash/stack/eql/v3`
subpath (`types.TextSearch`, v3 `encryptedTable` / `buildEncryptConfig`). The v3
builders emit the existing `EncryptConfig` shape, so encryption, payloads, and
query paths are unchanged at runtime.

Also widens the public client types (`EncryptionClientConfig.schemas`,
`EncryptOptions`, `SearchTerm`/`EncryptQueryOptions`) to a structural contract so
both v2 and v3 builders are accepted by `Encryption` / `encrypt` / `decrypt` /
`encryptQuery`. This is a backward-compatible widening — existing v2 usage is
unaffected. The structural contracts themselves (`BuildableColumn`,
`BuildableQueryColumn`, `BuildableV3QueryableColumn`, `BuildableTable`,
`BuildableTableColumns`) and the `encryptModel` return-type mapper
(`EncryptedFromBuildableTable`) are exported from `@cipherstash/stack/types` so
consumers can name them.
29 changes: 29 additions & 0 deletions .changeset/eql-v3-typed-client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
"@cipherstash/stack": minor
---

Add a strongly-typed EQL v3 client surface on a new `@cipherstash/stack/v3`
subpath (`EncryptionV3`, `typedClient`, `TypedEncryptionClient`). It re-exports
the v3 `types` namespace and table API (from `@cipherstash/stack/eql/v3`), so a
single import provides everything needed to author and use a v3 schema.

Every method derives its types from the concrete `table` / `column` builder
arguments:

- `encrypt` / `encryptQuery` pin the plaintext to the column's domain type
(`text → string`, `timestamp → Date`, …).
- `encryptQuery` constrains `queryType` to the column's capabilities and rejects
storage-only columns at compile time.
- `encryptModel` / `bulkEncryptModels` validate schema-column fields against their
inferred plaintext type (passthrough fields are untouched) and return a precise
encrypted model.
- `decryptModel` / `bulkDecryptModels` return the precise plaintext model,
reconstructing `Date` values from the encrypt-config `cast_as`.

Because the typed methods bind to the concrete branded v3 classes, a hand-rolled
structural table/column is rejected — closing the soundness gap where a non-branded
table could be encrypted at runtime while typed as plaintext.

Runtime behaviour is unchanged: the encrypt/query paths return the same operations
as the base client; only the model-decrypt paths add a per-column `Date`
reconstruction step. The v2 client surface (`Encryption`) is untouched.
7 changes: 7 additions & 0 deletions .changeset/eql-v3-typed-schema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@cipherstash/stack': minor
---

Add EQL v3 schema builders for supported generated SQL domains under `@cipherstash/stack/eql/v3`, exposed as the `types` namespace (one member per supported EQL v3 domain, e.g. `types.TextEq` / `types.IntegerOrd` / `types.Timestamp`), including explicit query capability metadata (`getQueryCapabilities()` / `isQueryable()`) and v3 table support in model encryption helpers (`encryptModel` / `bulkEncryptModels`).

Also widen the accepted plaintext input type for `encrypt` / `encryptQuery` to include `Date` (via the new `Plaintext` type), so v3 `date` / `timestamp` domains can be encrypted and queried with their natural JavaScript values.
65 changes: 65 additions & 0 deletions .github/workflows/fta-v3.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: "FTA Complexity (EQL v3)"

# Blocking complexity gate for the EQL v3 text-search schema. Runs the Fast
# TypeScript Analyzer (fta-cli) against the v3 source directory only and fails
# the check when any file exceeds the score cap (`--score-cap` in the
# `analyze:complexity` script). FTA is pure static source analysis, so this job
# needs no build step, database, or credentials.

on:
push:
branches:
- 'main'
paths:
- 'packages/stack/src/eql/v3/**'
# Shared match-index defaults live outside src/eql/v3 but shape every
# emitted v3 match block (load-bearing `k`/`m` ciphertext params), so edits
# here must trigger the v3 gate too.
- 'packages/stack/src/schema/match-defaults.ts'
- 'packages/stack/package.json'
- '.github/workflows/fta-v3.yml'
pull_request:
branches:
- "**"
paths:
- 'packages/stack/src/eql/v3/**'
- 'packages/stack/src/schema/match-defaults.ts'
- 'packages/stack/package.json'
- '.github/workflows/fta-v3.yml'

permissions:
contents: read

jobs:
fta:
name: Analyze v3 complexity
runs-on: blacksmith-4vcpu-ubuntu-2404

steps:
- name: Checkout Repo
uses: actions/checkout@v6

- uses: pnpm/action-setup@v6.0.8
name: Install pnpm
with:
run_install: false

- name: Install Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: 'pnpm'

# node-pty's install hook falls back to `node-gyp rebuild` when no
# linux-x64 prebuild matches. pnpm/action-setup v6 no longer ships
# node-gyp on PATH, so install it explicitly.
- name: Install node-gyp
run: npm install -g node-gyp

- name: Install dependencies
run: pnpm install --frozen-lockfile

# Non-zero exit (score above the cap) fails the check — this is the
# blocking gate. No `continue-on-error`.
- name: Analyze v3 complexity
run: pnpm --filter @cipherstash/stack run analyze:complexity
15 changes: 14 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Type tests (stack)
run: pnpm --filter @cipherstash/stack run test:types

- name: Lint — no hardcoded package-manager runners
run: pnpm run lint:runners

Expand All @@ -82,6 +85,11 @@ jobs:
echo "CS_CLIENT_KEY=${{ secrets.CS_CLIENT_KEY }}" >> ./packages/stack/.env
echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/stack/.env
echo "DATABASE_URL=postgres://cipherstash:password@localhost:5432/cipherstash" >> ./packages/stack/.env
# Fail loudly if the live suites would silently skip (missing creds):
# the skip-guard in __tests__/live-gate-required.test.ts asserts the
# live gates are actually enabled when these are set.
echo "REQUIRE_LIVE=1" >> ./packages/stack/.env
echo "REQUIRE_LIVE_PG=1" >> ./packages/stack/.env

- name: Create .env file in ./packages/protect-dynamodb/
run: |
Expand Down Expand Up @@ -157,7 +165,7 @@ jobs:
run: pnpm exec turbo run test:e2e --filter @cipherstash/e2e

# Verifies @cipherstash/stack/wasm-inline works under Deno — i.e. the
# WASM build of protect-ffi 0.25+ and auth 0.38+ can round-trip an
# WASM build of protect-ffi 0.26+ and auth 0.40+ can round-trip an
# encryption against ZeroKMS / CTS in a runtime with no native
# bindings available. The deno.json deliberately omits --allow-ffi so
# a silent fallback to the NAPI module is impossible.
Expand Down Expand Up @@ -294,6 +302,11 @@ jobs:
echo "CS_CLIENT_KEY=${{ secrets.CS_CLIENT_KEY }}" >> ./packages/stack/.env
echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/stack/.env
echo "DATABASE_URL=postgres://cipherstash:password@localhost:5432/cipherstash" >> ./packages/stack/.env
# Fail loudly if the live suites would silently skip (missing creds):
# the skip-guard in __tests__/live-gate-required.test.ts asserts the
# live gates are actually enabled when these are set.
echo "REQUIRE_LIVE=1" >> ./packages/stack/.env
echo "REQUIRE_LIVE_PG=1" >> ./packages/stack/.env

- name: Create .env file in ./packages/protect-dynamodb/
run: |
Expand Down
77 changes: 77 additions & 0 deletions docs/query-api-walkthrough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Query API Walkthrough — API → FFI → CipherStash Client

How a query value travels from the public API down to the Rust SDK across the FFI boundary. Terse by design.

## Flow

```mermaid
flowchart TD
subgraph JS["@cipherstash/stack (TypeScript)"]
A["User query builder<br/>ops.eq / Supabase filter / client.encryptQuery()"]
B["EncryptionClient.encryptQuery(value | terms[])<br/>encryption/index.ts:259"]
C["EncryptQueryOperation.execute()<br/>BatchEncryptQueryOperation.execute()"]
D["resolveIndexType() + queryTypeToFfi/QueryOp<br/>build QueryPayload{plaintext,column,table,indexType,queryOp}"]
E["validate: validateNumericValue<br/>assertValueIndexCompatibility"]
end

subgraph FFI["@cipherstash/protect-ffi (Neon bindings)"]
F["JS wrapper encryptQuery / encryptQueryBulk<br/>lib/index.cjs:155"]
G["native handle via @neon-rs/load<br/>lib/load.cjs:9"]
H["platform .node addon<br/>protect-ffi-darwin-arm64/index.node"]
end

subgraph RUST["CipherStash Client (Rust SDK)"]
I["EQL term generation<br/>ORE / match / unique / ste_vec"]
J["ZeroKMS key ops"]
end

A --> B --> C --> E --> D --> F --> G --> H --> I
I --> J
I -- "Encrypted | EncryptedQuery" --> F
F -- "formatEncryptedResult()" --> C
C -- "SQL/PostgREST WHERE clause" --> A
```

## Layers

| # | Layer | Entry point | Role |
|---|-------|-------------|------|
| 1 | Public API | `encryption/index.ts:259/270` `encryptQuery()` | Overloaded: single value → `EncryptQueryOperation`; `ScalarQueryTerm[]` → `BatchEncryptQueryOperation`. |
| 1a | Query builders | `drizzle/operators.ts:976`, `supabase/query-builder.ts:44` | `eq/gt/...` operators & deferred builders that batch-encrypt RHS values, then emit a WHERE clause. |
| 2 | Operations | `operations/encrypt-query.ts:41`, `operations/batch-encrypt-query.ts:115` | `execute()`: validate → resolve index → call FFI. `*WithLockContext` resolves `LockContextInput` via `resolveLockContext` before the FFI call. |
| 3 | EQL resolution | `helpers/infer-index-type.ts:89`, `types.ts:292` | `resolveIndexType` + `queryTypeToFfi`/`queryTypeToQueryOp` map public `QueryTypeName` → FFI `indexType`/`queryOp`. |
| 4 | FFI JS wrapper | `protect-ffi/lib/index.cjs:155` | `encryptQuery`/`encryptQueryBulk` → `wrapAsync(native.*)`. |
| 5 | Native loader | `protect-ffi/lib/load.cjs:9` | `@neon-rs/load` proxies to the platform prebuilt `.node`. |
| 6 | Rust SDK | compiled into `.node` | CipherStash Client: encryption, EQL/ORE/STE-vec term gen, ZeroKMS. Not a JS dep — shipped inside the addon. |

## Query-type mapping (Layer 3)

```mermaid
flowchart LR
subgraph Public["QueryTypeName"]
eq[equality]
ord[orderAndRange]
txt[freeTextSearch]
sel[steVecSelector]
trm[steVecTerm]
end
subgraph FFI["indexType / queryOp"]
u[unique]
o[ore]
m[match]
sv[ste_vec]
end
eq --> u
ord --> o
txt --> m
sel --> sv
trm --> sv
```

## Notes

- **Client init:** `EncryptionClient.init()` (`encryption/index.ts:81`) calls FFI `newClient()` once; the returned `Client` handle is passed into every `encryptQuery` call.
- **`cipherstashclient`** = the CipherStash Client **Rust SDK**, compiled via Neon into the platform `.node` binary inside `@cipherstash/protect-ffi`. It performs the actual crypto and talks to ZeroKMS.
- **Result shape:** `EncryptedQueryResult` (`types.ts:175`); shaped by `formatEncryptedResult(..., returnType)` (`eql` vs raw).
- **Version:** `package.json` pins `@cipherstash/protect-ffi@0.27.0` for the current EQL v3 semantic-domain bundle.
- `packages/protect/src/ffi/*` mirrors this flow under the older `protect` package name.
Loading