Skip to content

Commit ed78233

Browse files
committed
docs: add eql v3 typed schema plan and query API walkthrough
1 parent 61fdcf7 commit ed78233

2 files changed

Lines changed: 1613 additions & 0 deletions

File tree

‎docs/query-api-walkthrough.md‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Query API Walkthrough — API → FFI → CipherStash Client
2+
3+
How a query value travels from the public API down to the Rust SDK across the FFI boundary. Terse by design.
4+
5+
## Flow
6+
7+
```mermaid
8+
flowchart TD
9+
subgraph JS["@cipherstash/stack (TypeScript)"]
10+
A["User query builder<br/>ops.eq / Supabase filter / client.encryptQuery()"]
11+
B["EncryptionClient.encryptQuery(value | terms[])<br/>encryption/index.ts:259"]
12+
C["EncryptQueryOperation.execute()<br/>BatchEncryptQueryOperation.execute()"]
13+
D["resolveIndexType() + queryTypeToFfi/QueryOp<br/>build QueryPayload{plaintext,column,table,indexType,queryOp}"]
14+
E["validate: validateNumericValue<br/>assertValueIndexCompatibility"]
15+
end
16+
17+
subgraph FFI["@cipherstash/protect-ffi (Neon bindings)"]
18+
F["JS wrapper encryptQuery / encryptQueryBulk<br/>lib/index.cjs:155"]
19+
G["native handle via @neon-rs/load<br/>lib/load.cjs:9"]
20+
H["platform .node addon<br/>protect-ffi-darwin-arm64/index.node"]
21+
end
22+
23+
subgraph RUST["CipherStash Client (Rust SDK)"]
24+
I["EQL term generation<br/>ORE / match / unique / ste_vec"]
25+
J["ZeroKMS key ops"]
26+
end
27+
28+
A --> B --> C --> E --> D --> F --> G --> H --> I
29+
I --> J
30+
I -- "Encrypted | EncryptedQuery" --> F
31+
F -- "formatEncryptedResult()" --> C
32+
C -- "SQL/PostgREST WHERE clause" --> A
33+
```
34+
35+
## Layers
36+
37+
| # | Layer | Entry point | Role |
38+
|---|-------|-------------|------|
39+
| 1 | Public API | `encryption/index.ts:259/270` `encryptQuery()` | Overloaded: single value → `EncryptQueryOperation`; `ScalarQueryTerm[]` → `BatchEncryptQueryOperation`. |
40+
| 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. |
41+
| 2 | Operations | `operations/encrypt-query.ts:41`, `operations/batch-encrypt-query.ts:115` | `execute()`: validate → resolve index → call FFI. `*WithLockContext` adds CTS token + ZeroKMS lock context. |
42+
| 3 | EQL resolution | `helpers/infer-index-type.ts:89`, `types.ts:292` | `resolveIndexType` + `queryTypeToFfi`/`queryTypeToQueryOp` map public `QueryTypeName` → FFI `indexType`/`queryOp`. |
43+
| 4 | FFI JS wrapper | `protect-ffi/lib/index.cjs:155` | `encryptQuery`/`encryptQueryBulk` → `wrapAsync(native.*)`. |
44+
| 5 | Native loader | `protect-ffi/lib/load.cjs:9` | `@neon-rs/load` proxies to the platform prebuilt `.node`. |
45+
| 6 | Rust SDK | compiled into `.node` | CipherStash Client: encryption, EQL/ORE/STE-vec term gen, ZeroKMS. Not a JS dep — shipped inside the addon. |
46+
47+
## Query-type mapping (Layer 3)
48+
49+
```mermaid
50+
flowchart LR
51+
subgraph Public["QueryTypeName"]
52+
eq[equality]
53+
ord[orderAndRange]
54+
txt[freeTextSearch]
55+
sel[steVecSelector]
56+
trm[steVecTerm]
57+
end
58+
subgraph FFI["indexType / queryOp"]
59+
u[unique]
60+
o[ore]
61+
m[match]
62+
sv[ste_vec]
63+
end
64+
eq --> u
65+
ord --> o
66+
txt --> m
67+
sel --> sv
68+
trm --> sv
69+
```
70+
71+
## Notes
72+
73+
- **Client init:** `EncryptionClient.init()` (`encryption/index.ts:81`) calls FFI `newClient()` once; the returned `Client` handle is passed into every `encryptQuery` call.
74+
- **`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.
75+
- **Result shape:** `EncryptedQueryResult` (`types.ts:175`); shaped by `formatEncryptedResult(..., returnType)` (`eql` vs raw).
76+
- **Version:** `package.json` pins `@cipherstash/protect-ffi@0.24.0` (installed tree observed at `0.23.0` — confirm before relying on it).
77+
- `packages/protect/src/ffi/*` mirrors this flow under the older `protect` package name.

0 commit comments

Comments
 (0)