You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#621 adds EQL v3 encrypted JSON (types.Json) with containment querying only — ops.contains(col, subObject) emitting the @> operator with a query_jsonb needle.
EQL v3 supports a second JSON query form that #621 does not implement: JSONPath selector with a constraint, e.g.
foo->'bar'= $1
foo->'wee'> $1
The eql_v3_json domain ships the operators for it — ->(text|int) and ->>(text|int) — and encryptQuery(value, { queryType: 'searchableJson' }) already auto-infers a selector term for string (JSONPath) needles. What's missing is the adapter surface: the v3 Drizzle operators expose no ->/->> selector, so a selector-with-constraint query can't be expressed, and there are no tests for it.
Proposed change
Add selector operators (-> / ->>) to the v3 Drizzle integration for types.Json columns.
Compose the extracted selector value with the existing comparison operators so col->'bar' = $1 / col->'wee' > $1 work end-to-end.
Add a live oracle + tests (mirroring json-contains.integration.test.ts).
Document the pattern in skills/stash-encryption / skills/stash-drizzle.
Notes
Values returned by a selector should be usable in the other query forms depending on their type (equality, ordering, etc.).
Additional design note surfaced in #621: the typed client shares one plaintext type (PlaintextForColumn<Col>) between encrypt and encryptQuery. For a json column that's now JsonDocument (object/array/null). But a selector query needs a top-level string needle ('$.user.email'), which JsonDocument excludes. So selector support needs encryptQuery's json plaintext to differ from encrypt's (document for containment | string for selector) — encrypt must still reject a bare scalar. Factor that into the typing when implementing.
Shipped in #651 (merged 2026-07-15). Everything in the proposed change landed:
Selector querying on types.Json columns via ops.selector(col, '$.path') returning eq/ne/gt/gte/lt/lte, emitted as eql_v3.jsonb_path_query_first(col, <selector>) compared against an encrypted operand — so col->'bar' = $1 and col->'wee' > $1 work end-to-end. Paths are validated (no wildcards/array steps, prototype-polluting segments rejected), scalar-leaf capability is guarded, and ne includes absent-path rows (OR ... IS NULL) by documented design.
Live oracle + tests: selector.test.ts (unit), json-selector.integration.test.ts (real Postgres + real crypto, including an absent-path row), plus type tests — and the CI gap that had left stack-drizzle/stack-supabase test:types unwired got fixed along the way.
Skills: the pattern is documented in stash-encryption and stash-drizzle, including the ciphertext-in-WHERE disclosure.
Activity
refactor(stack): review wording/hygiene for v3 JSON (no behaviour cha…
coderdan commentedon Jul 12, 2026
Additional design note surfaced in #621: the typed client shares one plaintext type (
PlaintextForColumn<Col>) betweenencryptandencryptQuery. For ajsoncolumn that's nowJsonDocument(object/array/null). But a selector query needs a top-level string needle ('$.user.email'), whichJsonDocumentexcludes. So selector support needsencryptQuery's json plaintext to differ fromencrypt's (document for containment | string for selector) — encrypt must still reject a bare scalar. Factor that into the typing when implementing.refactor(stack-drizzle): ciphertext-free v3 JSON selector-with-constr…
fix(stack-drizzle): v3 JSON selector — storage-needle RHS (interim)
docs(skills): document v3 JSON selector-with-constraint (#623)
fix(stack-drizzle): address CodeRabbit review on v3 JSON selector
fix(stack-drizzle): address freshtonic + Toby review on v3 JSON selector
coderdan commentedon Jul 15, 2026
Shipped in #651 (merged 2026-07-15). Everything in the proposed change landed:
types.Jsoncolumns viaops.selector(col, '$.path')returningeq/ne/gt/gte/lt/lte, emitted aseql_v3.jsonb_path_query_first(col, <selector>)compared against an encrypted operand — socol->'bar' = $1andcol->'wee' > $1work end-to-end. Paths are validated (no wildcards/array steps, prototype-polluting segments rejected), scalar-leaf capability is guarded, andneincludes absent-path rows (OR ... IS NULL) by documented design.selector.test.ts(unit),json-selector.integration.test.ts(real Postgres + real crypto, including an absent-path row), plus type tests — and the CI gap that had left stack-drizzle/stack-supabasetest:typesunwired got fixed along the way.stash-encryptionandstash-drizzle, including the ciphertext-in-WHERE disclosure.Not closed by this issue, tracked separately:
encryptQuerycan't mint an ordering (op) JSON needle → EQL v3 ste_vec: encryptQuery can't mint an ordering (op) JSON query needle for selector-with-constraint protectjs-ffi#137; the operator layer already isolates the swap.https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w