Skip to content

feat: client 0.38.0 migration — drop vendored stack-auth + EQL v2.3 mapper support (CIP-3233)#409

Merged
freshtonic merged 16 commits into
v3from
james/cip-3233-proxy-drop-vendor-patch
Jun 30, 2026
Merged

feat: client 0.38.0 migration — drop vendored stack-auth + EQL v2.3 mapper support (CIP-3233)#409
freshtonic merged 16 commits into
v3from
james/cip-3233-proxy-drop-vendor-patch

Conversation

@freshtonic

@freshtonic freshtonic commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Moves Proxy off the vendor/stack-auth [patch.crates-io] workaround onto the released cipherstash-client group (CIP-3233 follow-up) — and carries the proxy-side work to keep the EQL/encrypted-query path working against the v2.3 payload format introduced by the 0.38.x client.

The 0.38.0 bump broke 38 integration tests (v2.3 changed how encrypted values are stored/searched). All are fixed and CI is green across PostgreSQL 14–17 on the published cipherstash-client 0.38.1. Base branch: v3.

Note

Green and merge-ready (pending review). Now on the published 0.38.1 (the stable release of the scalar-OPE op-store fix, CIP-3280 / suite #2061); the temporary CI-only prerelease pin was reverted and re-pointed at the real release.

What's in this PR

Area Commits What
Deps / setup (CIP-3233) 2e11f69d, b38eeb0a, 7b8500a2 client 0.34.1-alpha.4 → 0.38.1 + drop vendored stack-auth/patch/exclude; EQL pin → eql-2.3.1.
jsonb range/order/projection (CIP-3279) d1abdd2b RewriteJsonbSteVecOrdering → CLLW ste_vec_entry extractor form; proxy reshapes/decrypts bare sv entries.
concrete-index-aware mapper + scalar OPE (CIP-3283) 1af99682, 7248515a side-channel IndexResolver (no inference/unifier change); RewriteScalarOpeOrderingdecode((col)::jsonb ->> 'op','hex') bytea compare — built-ins only, no EQL change.
jsonb equality (CIP-3281) df28b0f8 =/<> via XOR-aware eql_v2.eq_term (hm/oc); no EQL/param-encoding change.
e2e-surfaced fixes 4f3c2a45, af5c963d ::jsonb cast before ->> 'op'; bare numeric sv literals encrypt as scalar Plaintext::Float.
review feedback 76a8bfc5, fd20beab SteVecTerm fails fast w/o ste_vec index (CodeRabbit); dedup, module tests, perf (Toby).
CI ed7d29e3 add v3 to Test + Benchmark branch filters (PR retargeted to v3).
deferred / housekeeping 1ad4c3f493ac7b1d, 10b06cf5717b960a temp prerelease pin (reverted); bool sv-leaf equality deferred (see below).

Why the extra scope

cipherstash-client 0.38.x (v2.3 EQL payload) changed encrypted storage/search: JSONB leaves became STE-vec elements (CLLW oc / HMAC hm), and scalar OPE columns no longer carry a root ordering term the bare operators read. CIP-3279/3281 rewrite jsonb comparisons to the ste_vec_entry extractor forms; CIP-3283 makes the mapper concrete-index-aware so scalar OPE ordering rewrites to a built-in op bytea compare — all Proxy-side, no EQL change (so CIP-3282 / the EQL U-005 reversal is cancelled as unnecessary).

Verification

CI green on PG 14–17 (717b960a, against published 0.38.1): integration without_multitenant 348 + multitenant 69 + showcase; eql-mapper 122 unit; proxy lib 128; mise run check clean.

Deferred (not in scope, tracked)

jsonb sv boolean-leaf equality (col -> 'flag' = <bool>) — CIP-3293. It was never implemented (only bool containment worked) and can't be wired Proxy-side: the SteVecTerm query encoding requires an orderable term, but a boolean has only the hm equality term — needs a cipherstash-client change. The proxy-side bool work was reverted; pre-existing scalar bool equality is unaffected.

Review

All inline threads resolved — 9 CodeRabbit + 5 @tobyhede. Real fixes from review: 2 (SteVecTerm fail-fast; the rest false-positives or quality/test/perf).

Notes

  • Integration tests need live Postgres + ZeroKMS (CI / creds).
  • Tracking: CIP-3233 (umbrella) · CIP-3279 · CIP-3280 · CIP-3281 · CIP-3283 · CIP-3293 (deferred bool) · CIP-3282 (cancelled, superseded by CIP-3283).

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR updates proxy encryption and SQL rewriting to use EqlOutput and index-aware STE-vec/OPE handling, adds legacy ciphertext parsing for PostgreSQL data rows, bumps related workspace and EQL version pins, and removes the vendored stack-auth patch and code references.

Changes

Proxy EQL and index-aware rewrite migration

Layer / File(s) Summary
Workspace and public contracts
Cargo.toml, mise.toml, mise.local.example.toml, packages/cipherstash-proxy/src/lib.rs, packages/cipherstash-proxy/src/error.rs, packages/cipherstash-proxy/src/proxy/mod.rs, packages/cipherstash-proxy/src/proxy/encrypt_config/*, packages/eql-mapper/src/eql_mapper.rs, packages/eql-mapper/src/type_checked_statement.rs
Workspace pins and EQL version settings change, proxy exports add EqlOutput and the encrypt-config index resolver, and index-aware type-checking plumbing is added to the mapper and statement wrapper.
EqlOutput pipeline propagation
packages/cipherstash-proxy/src/postgresql/frontend.rs, packages/cipherstash-proxy/src/postgresql/context/mod.rs, packages/cipherstash-proxy/src/postgresql/messages/bind.rs, packages/cipherstash-proxy/src/proxy/zerokms/zerokms.rs, packages/cipherstash-proxy/src/postgresql/backend.rs
Frontend, context, bind rewriting, ZeroKMS, and backend test-service code switch encryption outputs to EqlOutput and pass index-aware resolver data into type checking.
Ciphertext parsing and backend checks
packages/cipherstash-proxy/src/postgresql/messages/data_row.rs, packages/cipherstash-proxy/src/postgresql/backend.rs, packages/cipherstash-proxy/src/proxy/encrypt_config/manager.rs
Data-row parsing accepts current and legacy EQL payload shapes, backend column checks use identifier accessors, and STE-vec config parsing expectations are updated.
STE-vec term typing and plaintext conversion
packages/eql-mapper/src/index_resolver.rs, packages/eql-mapper/src/inference/unifier/types.rs, packages/eql-mapper/src/inference/unifier/eql_traits.rs, packages/cipherstash-proxy/src/postgresql/data/from_sql.rs, packages/eql-mapper/src/ste_vec_ordering.rs
The EQL term model gains SteVecTerm, ordering traits include STE-vec terms, and SQL-to-plaintext conversion handles STE-vec JSON inputs and scalar reductions.
Ordering rewrite rules
packages/eql-mapper/src/lib.rs, packages/eql-mapper/src/eql_mapper.rs, packages/eql-mapper/src/transformation_rules/*, CHANGELOG.md, packages/cipherstash-proxy-integration/src/map_ope_index_order.rs
Shared STE-vec predicates, index-aware type checking, and transformation rules are added for JSONB STE-vec ordering and scalar OPE ordering, with tests covering the new rewrite paths and a changelog note.

Vendored stack-auth cleanup

Layer / File(s) Summary
Workspace patch cleanup
Cargo.toml
The workspace exclude entry and crates.io patch override for vendor/stack-auth are removed.
Vendored auth code removal
vendor/stack-auth/examples/device_code.rs, vendor/stack-auth/src/auto_refresh.rs
The vendored crate’s device-code example and refresh module are removed.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90+ minutes

Possibly related issues

Possibly related PRs

  • cipherstash/proxy#364: It changes the same STE-vec index configuration parsing path that this PR updates in encrypt_config/manager.rs.
  • cipherstash/proxy#386: It touches the same proxy encryption-config pipeline around canonical_to_map and related manager behavior.
  • cipherstash/proxy#407: It added the vendored stack-auth patch that this PR removes from Cargo.toml.

Suggested reviewers

  • tobyhede
  • coderdan

Poem

A rabbit hopped through SQL night air,
with EqlOutput now everywhere.
The stack-auth burrow faded out,
while STE-vec bits danced all about. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: the 0.38.0 client migration, removal of vendored stack-auth, and EQL v2.3 mapper support.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch james/cip-3233-proxy-drop-vendor-patch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…tack-auth patch (CIP-3233)

Moves Proxy off the `vendor/stack-auth` `[patch.crates-io]` workaround and onto the
current released cipherstash-client group, built against the fixed stack-auth.

Background: 2.2.4 (PR #408) shipped the CIP-3233 access-key token-refresh fix via
a vendored stack-auth patched on top of the 0.34.1-alpha.4 source. cipherstash-client
0.38.0 links stack-auth 0.38.0, which carries the same fix from crates.io, so the
vendored copy and patch are no longer needed.

Changes:
- cipherstash-client / cipherstash-config / cts-common: 0.34.1-alpha.4 -> 0.38.0
  (carries the API migration from PR #406's 0.37.0 upgrade; 0.37 -> 0.38 needed no
  further source changes)
- Remove `[patch.crates-io] stack-auth = { path = "vendor/stack-auth" }`, the
  `exclude = ["vendor/stack-auth"]` workspace entry, and the vendor/stack-auth tree
- stack-auth now resolves from crates.io (0.38.0); single version of the
  cipherstash-client group in the lock (zerokms-protocol 0.12.19)

Verified: `cargo check --workspace`, `cargo clippy --workspace --all-targets`, and
`cargo test --workspace --lib` (111 proxy unit tests) all pass. Integration tests
need a live DB/ZeroKMS and were not run here.
@freshtonic freshtonic force-pushed the james/cip-3233-proxy-drop-vendor-patch branch from 256ad08 to 2e11f69 Compare June 24, 2026 06:48
@freshtonic freshtonic changed the title chore(deps): upgrade cipherstash-client to 0.37.1, drop vendored stack-auth patch (CIP-3233) chore(deps): upgrade cipherstash-client to 0.38.0, drop vendored stack-auth patch (CIP-3233) Jun 24, 2026
cipherstash-client 0.38.0 emits structured JSONB (SteVec) encrypted
values as {"k":"sv",...} without a top-level `c` field. The pinned EQL
2.3.0-pre.3 enforced a top-level `c` on every encrypted value via
eql_v2._encrypted_check_c, rejecting these payloads with EP0001
("Encrypted column missing ciphertext (c) field") and failing all
JSONB/SteVec integration tests.

EQL 2.3.1 relaxes the check to `(val ? 'c') OR (val ? 'sv')`, accepting
the new SteVec format.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/cipherstash-proxy/src/postgresql/messages/bind.rs (1)

84-96: 🗄️ Data Integrity & Integration | 🟠 Major

Confirm EqlOutput serialization format mismatch for EQL 2.3.1

The workspace is pinned to cipherstash-client version 0.38.0, where EqlOutput serializes via serde_json::to_value into a JSON object containing a top-level c (ciphertext) field. The required EQL 2.3.1 format expects a structure without this top-level c wrapper. As written, rewrite() writes the legacy payload, which will fail or corrupt data in eql_v2_encrypted columns expecting the new format. Update the serialization logic to produce the schema-free format expected by the EQL 2.3.1 backend, likely requiring manual construction of the JSON payload from the EqlOutput components or updating the dependency if a fix is available.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cipherstash-proxy/src/postgresql/messages/bind.rs` around lines 84 -
96, The bind::rewrite method is serializing EqlOutput with serde_json::to_value,
which preserves the legacy top-level c wrapper and does not match the EQL 2.3.1
payload shape. Update rewrite() to emit the schema-free JSON expected by
eql_v2_encrypted columns, either by manually building the payload from
EqlOutput’s fields or by switching to a dependency/version that already produces
the new format. Keep the change localized to bind::rewrite and the EqlOutput
serialization path it uses.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Cargo.toml`:
- Around line 46-48: The Cargo.toml dependency update points cipherstash-client,
cipherstash-config, and cts-common at unpublished 0.38.0 versions, which will
break dependency resolution. Revert these entries to a published crates.io
version or restore the [patch.crates-io] override to a valid local/git source
for stack-auth until those crates are available. Keep the existing dependency
names and patch configuration aligned so Cargo can resolve them successfully.

---

Outside diff comments:
In `@packages/cipherstash-proxy/src/postgresql/messages/bind.rs`:
- Around line 84-96: The bind::rewrite method is serializing EqlOutput with
serde_json::to_value, which preserves the legacy top-level c wrapper and does
not match the EQL 2.3.1 payload shape. Update rewrite() to emit the schema-free
JSON expected by eql_v2_encrypted columns, either by manually building the
payload from EqlOutput’s fields or by switching to a dependency/version that
already produces the new format. Keep the change localized to bind::rewrite and
the EqlOutput serialization path it uses.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 25c7c19a-7daf-43a1-9720-a75b783fae42

📥 Commits

Reviewing files that changed from the base of the PR and between 4facf29 and b38eeb0.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • vendor/stack-auth/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (35)
  • Cargo.toml
  • mise.local.example.toml
  • mise.toml
  • packages/cipherstash-proxy/src/error.rs
  • packages/cipherstash-proxy/src/lib.rs
  • packages/cipherstash-proxy/src/postgresql/backend.rs
  • packages/cipherstash-proxy/src/postgresql/context/mod.rs
  • packages/cipherstash-proxy/src/postgresql/frontend.rs
  • packages/cipherstash-proxy/src/postgresql/messages/bind.rs
  • packages/cipherstash-proxy/src/postgresql/messages/data_row.rs
  • packages/cipherstash-proxy/src/proxy/encrypt_config/manager.rs
  • packages/cipherstash-proxy/src/proxy/mod.rs
  • packages/cipherstash-proxy/src/proxy/zerokms/zerokms.rs
  • vendor/stack-auth/.gitignore
  • vendor/stack-auth/Cargo.toml
  • vendor/stack-auth/LICENSE
  • vendor/stack-auth/README.md
  • vendor/stack-auth/examples/auto_strategy.rs
  • vendor/stack-auth/examples/device_code.rs
  • vendor/stack-auth/src/access_key.rs
  • vendor/stack-auth/src/access_key_refresher.rs
  • vendor/stack-auth/src/access_key_strategy.rs
  • vendor/stack-auth/src/auto_refresh.rs
  • vendor/stack-auth/src/auto_strategy.rs
  • vendor/stack-auth/src/device_client.rs
  • vendor/stack-auth/src/device_code/mod.rs
  • vendor/stack-auth/src/device_code/protocol.rs
  • vendor/stack-auth/src/device_code/tests.rs
  • vendor/stack-auth/src/lib.rs
  • vendor/stack-auth/src/oauth_refresher.rs
  • vendor/stack-auth/src/oauth_strategy.rs
  • vendor/stack-auth/src/refresher.rs
  • vendor/stack-auth/src/service_token.rs
  • vendor/stack-auth/src/static_token_strategy.rs
  • vendor/stack-auth/src/token.rs
💤 Files with no reviewable changes (22)
  • vendor/stack-auth/src/static_token_strategy.rs
  • vendor/stack-auth/src/device_code/tests.rs
  • vendor/stack-auth/README.md
  • vendor/stack-auth/src/auto_strategy.rs
  • vendor/stack-auth/src/oauth_refresher.rs
  • vendor/stack-auth/.gitignore
  • vendor/stack-auth/src/access_key.rs
  • vendor/stack-auth/Cargo.toml
  • vendor/stack-auth/src/device_client.rs
  • vendor/stack-auth/src/device_code/protocol.rs
  • vendor/stack-auth/src/device_code/mod.rs
  • vendor/stack-auth/src/refresher.rs
  • vendor/stack-auth/src/service_token.rs
  • vendor/stack-auth/src/oauth_strategy.rs
  • vendor/stack-auth/src/access_key_refresher.rs
  • vendor/stack-auth/src/access_key_strategy.rs
  • vendor/stack-auth/examples/auto_strategy.rs
  • vendor/stack-auth/LICENSE
  • vendor/stack-auth/src/token.rs
  • vendor/stack-auth/src/lib.rs
  • vendor/stack-auth/src/auto_refresh.rs
  • vendor/stack-auth/examples/device_code.rs

Comment thread Cargo.toml Outdated
…nt 0.38.0 (CIP-3279)

cipherstash-client 0.38.0 stores jsonb leaves as STE-vec elements carrying
CLLW ORE (`oc`), and EQL 2.3.1's `->` returns `eql_v2.ste_vec_entry`. Bare
comparison/order on the extracted entry routed to the root Block-ORE path
(`eql_v2.compare` → `ore_block_u64_8_256`, requires `ob`) and failed with
`Expected an ore index (ob) value`. Field projection returned the raw
encrypted sv element instead of decrypting it.

Group B (range/order): new RewriteJsonbSteVecOrdering rule rewrites
`(col -> sel) <op> $param` into
`eql_v2.ore_cllw((col -> sel)::JSONB) <op> eql_v2.ore_cllw($param::JSONB)`,
with a new EqlTerm::SteVecTerm variant (bound Ord) and post-inference RHS
reclassification so the param/literal is encrypted as a CLLW STE-vec query
term (`oc`) via QueryOp::SteVecTerm.

Group C (field projection): data_row reshapes a bare ste_vec_entry jsonb
(text `{` and binary `0x01`) into a decryptable EqlCiphertext; from_sql
parses sv-entry scalars (numbers as f64 to match storage's orderable
encoding).

Fixes 20 integration tests (8 range/order + 12 field projection), verified
end-to-end against live proxy + PostgreSQL + ZeroKMS. jsonb sv equality
(select_where_jsonb_eq, jsonb_term_filter) remains a separate follow-up.
…dering (CIP-3283)

eql-mapper previously saw only abstract EqlTraits for an encrypted column,
never the concrete index type, so transformation rules could not target
index-specific SQL. Introduce a side-channel IndexResolver (TableColumn ->
{IndexKind}) consulted only by transformation rules — inference, unification,
and the EqlValue/EqlTerm output types are deliberately untouched, and the
crate takes no dependency on cipherstash-config. The proxy populates the
resolver from EncryptConfig (already keyed by table+column); an
EmptyIndexResolver reproduces prior behaviour as the safe default.

First consumer: RewriteScalarOpeOrdering rewrites scalar (non-jsonb) OPE
columns so PostgreSQL compares the order-preserving `op` ciphertext directly
with built-ins only:
  col <op> $p     -> decode(col->>'op','hex') <op> decode($p->>'op','hex')
  ORDER BY col    -> ORDER BY decode(col->>'op','hex')  (ASC/DESC/NULLS kept)
ORE columns, equality, and jsonb sv accessors are left untouched.

This lets scalar OPE ordering work Proxy-side with NO EQL change (potentially
retiring CIP-3282), gated on the client storing/query-encoding `op` (CIP-3280).

Verified: cargo test -p eql-mapper (94 pass, +9), proxy lib (new tests pass;
config::tandem failures are a pre-existing parallelism flake), mise run check
clean. NOT yet code-reviewed (the review pass did not complete). e2e blocked
on the unpublished CIP-3280 client.
@freshtonic freshtonic changed the title chore(deps): upgrade cipherstash-client to 0.38.0, drop vendored stack-auth patch (CIP-3233) feat: client 0.38.0 migration — drop vendored stack-auth + EQL v2.3 mapper support (CIP-3233) Jun 25, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/cipherstash-proxy/src/postgresql/data/from_sql.rs (1)

325-352: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve eql_term in the binary TEXT/VARCHAR fallback.

The new SteVecTerm JSON path is skipped when a driver sends the RHS as binary TEXT/VARCHAR: this fallback still hard-codes EqlTermVariant::Full, so jsonb STE-vec ordering values get converted to Plaintext::Json instead of the scalar Plaintext that QueryOp::SteVecTerm expects.

Suggested fix
-        (_, _, &Type::TEXT | &Type::VARCHAR) => parse_bytes_from_sql::<String>(bytes, pg_type)
-            .and_then(|val| text_from_sql(&val, EqlTermVariant::Full, col_type)),
+        (eql_term, _, &Type::TEXT | &Type::VARCHAR) => {
+            parse_bytes_from_sql::<String>(bytes, pg_type)
+                .and_then(|val| text_from_sql(&val, eql_term, col_type))
+        }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cipherstash-proxy/src/postgresql/data/from_sql.rs` around lines 325
- 352, The TEXT/VARCHAR fallback in from_sql::from_sql is dropping the original
eql_term by hard-coding EqlTermVariant::Full, which breaks the SteVecTerm JSON
scalar path. Update that fallback to pass through the incoming eql_term instead
of forcing Full, so QueryOp::SteVecTerm still reaches
text_from_sql/json_scalar_to_plaintext with the correct variant. Keep the
existing JSON/JSONB/BYTEA handling intact and make sure the fallback in the
final &Type::TEXT | &Type::VARCHAR arm preserves the caller’s term for both
binary and text driver inputs.
🧹 Nitpick comments (1)
packages/eql-mapper/src/lib.rs (1)

2333-2335: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert the exact Partial variant here.

This guard says root-scalar RHS params must remain EqlTerm::Partial, but !matches!(SteVecTerm) would still pass for Full or another wrong EQL variant.

Proposed test tightening
-        assert!(
-            !matches!(value, Value::Eql(EqlTerm::SteVecTerm(_))),
-            "root-scalar ordering RHS must NOT be reclassified to SteVecTerm, got {value}"
-        );
+        assert!(
+            matches!(value, Value::Eql(EqlTerm::Partial(_, _))),
+            "root-scalar ordering RHS must remain EqlTerm::Partial, got {value}"
+        );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eql-mapper/src/lib.rs` around lines 2333 - 2335, The guard in the
root-scalar ordering check is too loose because it only excludes
Value::Eql(EqlTerm::SteVecTerm(_)) and would still allow other incorrect
variants. Update the assertion in the relevant ordering/RHS validation path to
match the exact expected EqlTerm::Partial shape, using the same value being
checked there, so the logic fails for any non-Partial EQL term. Tighten the
corresponding test around this root-scalar ordering case to verify the RHS
remains Partial and does not accept Full or other variants.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cipherstash-proxy/src/proxy/encrypt_config/index_resolver.rs`:
- Around line 53-56: The config lookup is using a bare table name in index
resolution, which breaks matches for schema-qualified EncryptConfig keys. Update
the identifier construction in `IndexResolver` so `Identifier::new(...)` uses
the fully qualified table name for `TableColumn` lookup, either by
preserving/retrieving the schema when building `TableColumn` or by resolving it
at lookup time before matching against config entries. Ensure the fix is applied
in the `IndexResolver` path that currently reads `table_column.table.value` so
schema-qualified tables no longer fall back to the empty-set rewrite path.

In `@packages/cipherstash-proxy/src/proxy/zerokms/zerokms.rs`:
- Around line 205-214: The SteVecTerm mapping in the zero-kms EqlTermVariant
match currently falls back to Store when no SteVec index is found, which can
silently drop the STE-vec query term. Update the EqlTermVariant::SteVecTerm
branch in the zerokms mapper to fail fast or otherwise prevent this path from
returning EqlOperation::Store; keep the operation as a query only when an
IndexType::SteVec is present, and use the surrounding EqlOperation::Query /
QueryOp::SteVecTerm handling as the place to enforce the invariant.

In
`@packages/eql-mapper/src/transformation_rules/rewrite_jsonb_ste_vec_ordering.rs`:
- Around line 191-197: The rewrite in would_edit currently gates on
self.is_eql_typed(right), which is too broad and allows non-SteVec encrypted
expressions to be rewritten incorrectly. Update the condition in
rewrite_jsonb_ste_vec_ordering::would_edit to require the RHS to be classified
as a SteVecTerm (using the existing term/classification helpers in this rule)
instead of any EQL-typed expression, so only single STE-vec comparison terms
trigger the jsonb accessor ordering rewrite.

In `@packages/eql-mapper/src/transformation_rules/rewrite_scalar_ope_ordering.rs`:
- Around line 173-185: The `would_edit_comparison` check in
`rewrite_scalar_ope_ordering.rs` is too permissive because it only verifies that
the RHS is EQL-typed, not that it is OPE-backed. Update the guard in
`would_edit_comparison` so the rewrite only applies when the right-hand
expression is confirmed to carry the needed OPE backing/`op` term, using the
same kind of resolver-based validation already used for `is_scalar_ope_column`
on the left. Keep the binary-operator and left-column checks intact, but add the
missing RHS-specific predicate before allowing the rewrite.

In `@packages/eql-mapper/src/type_checked_statement.rs`:
- Around line 167-170: Gate the JSONB STE-vec rewrite behind IndexResolver
checks in RewriteJsonbSteVecOrdering and the type_checked_statement wiring.
Update RewriteJsonbSteVecOrdering to accept the resolver alongside node_types,
and use the same resolve(table_column).contains(&IndexKind::SteVec) predicate
before rewriting JsonLike columns or classifying RHS values as SteVecTerm, so
columns without a configured STE-vec index do not get rewritten to
ore_cllw(...).

---

Outside diff comments:
In `@packages/cipherstash-proxy/src/postgresql/data/from_sql.rs`:
- Around line 325-352: The TEXT/VARCHAR fallback in from_sql::from_sql is
dropping the original eql_term by hard-coding EqlTermVariant::Full, which breaks
the SteVecTerm JSON scalar path. Update that fallback to pass through the
incoming eql_term instead of forcing Full, so QueryOp::SteVecTerm still reaches
text_from_sql/json_scalar_to_plaintext with the correct variant. Keep the
existing JSON/JSONB/BYTEA handling intact and make sure the fallback in the
final &Type::TEXT | &Type::VARCHAR arm preserves the caller’s term for both
binary and text driver inputs.

---

Nitpick comments:
In `@packages/eql-mapper/src/lib.rs`:
- Around line 2333-2335: The guard in the root-scalar ordering check is too
loose because it only excludes Value::Eql(EqlTerm::SteVecTerm(_)) and would
still allow other incorrect variants. Update the assertion in the relevant
ordering/RHS validation path to match the exact expected EqlTerm::Partial shape,
using the same value being checked there, so the logic fails for any non-Partial
EQL term. Tighten the corresponding test around this root-scalar ordering case
to verify the RHS remains Partial and does not accept Full or other variants.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d0ef3fd4-1fb6-44ea-aac1-a8954e8b56b6

📥 Commits

Reviewing files that changed from the base of the PR and between b38eeb0 and 1af9968.

📒 Files selected for processing (19)
  • packages/cipherstash-proxy/src/postgresql/context/mod.rs
  • packages/cipherstash-proxy/src/postgresql/data/from_sql.rs
  • packages/cipherstash-proxy/src/postgresql/frontend.rs
  • packages/cipherstash-proxy/src/postgresql/messages/data_row.rs
  • packages/cipherstash-proxy/src/proxy/encrypt_config/index_resolver.rs
  • packages/cipherstash-proxy/src/proxy/encrypt_config/manager.rs
  • packages/cipherstash-proxy/src/proxy/encrypt_config/mod.rs
  • packages/cipherstash-proxy/src/proxy/mod.rs
  • packages/cipherstash-proxy/src/proxy/zerokms/zerokms.rs
  • packages/eql-mapper/src/eql_mapper.rs
  • packages/eql-mapper/src/index_resolver.rs
  • packages/eql-mapper/src/inference/unifier/eql_traits.rs
  • packages/eql-mapper/src/inference/unifier/types.rs
  • packages/eql-mapper/src/lib.rs
  • packages/eql-mapper/src/ste_vec_ordering.rs
  • packages/eql-mapper/src/transformation_rules/mod.rs
  • packages/eql-mapper/src/transformation_rules/rewrite_jsonb_ste_vec_ordering.rs
  • packages/eql-mapper/src/transformation_rules/rewrite_scalar_ope_ordering.rs
  • packages/eql-mapper/src/type_checked_statement.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/cipherstash-proxy/src/proxy/encrypt_config/manager.rs
  • packages/cipherstash-proxy/src/postgresql/messages/data_row.rs

Comment thread packages/cipherstash-proxy/src/proxy/zerokms/zerokms.rs Outdated
Comment thread packages/eql-mapper/src/type_checked_statement.rs
…IP-3283)

Address non-blocking review nits on RewriteScalarOpeOrdering:

- Add tests pinning behaviour for shapes around the rule's match surface:
  - col BETWEEN $a AND $b (Expr::Between) is not rewritten
  - MIN(col)/MAX(col) route to eql_v2 aggregates, not decode(op)
  - $p < col (column on the right) IS rewritten symmetrically -- a bare
    comparison param unifies to the same eql column, so the rule fires for
    either operand ordering (documented in the test; not a coverage gap)
- Add a multi-key ORDER BY test asserting only the OPE sort key is
  rewritten to decode(col->>'op','hex') with directions/NULLS preserved
- Tighten the rule rustdoc: mem::replace is the ownership mechanism that
  moves the RHS node intact (so downstream cast rules re-encrypt it), not
  the reason the op slot is preserved
- Leave a TODO(CIP-3280) for the NULL-invariant scalar-OPE e2e test, which
  is blocked on the unpublished client that can carry op end-to-end
…P-3281)

Rewrites jsonb STE-vec equality comparisons so they bind to EQL 2.3.1's
XOR-aware eql_v2.eq_term extractor instead of root eql_v2_encrypted
equality. The LHS sv-element accessor (col -> sel, already an
eql_v2.ste_vec_entry) is wrapped in eql_v2.eq_term; the jsonb_path_query_first
form (an eql_v2_encrypted) is cast to ::JSONB::eql_v2.ste_vec_entry first.

The RHS query payload is a root sv payload ({k:sv, ..., hm|oc}) that lacks
the s/c fields the ste_vec_entry DOMAIN CHECK requires, so it cannot use the
documented ::eql_v2.ste_vec_entry recipe. Instead the eq_term body is inlined
on the raw jsonb: decode(coalesce(rhs ->> 'hm', rhs ->> 'oc'), 'hex'). This
coalesces whichever deterministic term the column leaf carries (oc for CLLW
ORE leaves, hm for hmac/term-filter leaves), so no proxy-side query-op change
is needed: the existing QueryOp::SteVecTerm path already emits the matching
term.

The RHS param/literal is reclassified to EqlTerm::SteVecTerm via the
generalised ste_vec_term_rhs_keys, which now collects both ordering and
equality RHS operands. Both rewrite rules and the reclassification gate on the
same shared predicates plus the shared rhs_as_jsonb operand helper, so they
cannot drift apart.

Verified: cargo test -p eql-mapper (100 passed), proxy lib (121 passed),
fmt/clippy -D warnings/check --locked clean. e2e (select_where_jsonb_eq,
jsonb_term_filter) compiles but was NOT run -- no live PG/ZeroKMS in sandbox.
Points the proxy at the published cipherstash-client 0.38.1-alpha.1 — a
pre-release of the suite `fix/scalar-ope-oc-term` branch (CIP-3280) that
stores + query-encodes the scalar OPE `op` term. This lets CI exercise the
full integration suite, including scalar OPE ordering (map_ope_*), against
a client that carries the op term.

Temporary: revert to a stable published cipherstash-client before merge
(once CIP-3280 / suite PR #2061 lands and a release ships).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/eql-mapper/src/ste_vec_ordering.rs`:
- Around line 97-144: The rhs_as_jsonb helper is based on an outdated cast shape
and now always falls back because RewriteJsonbSteVecOrdering runs before
CastParamsAsEncrypted and CastLiteralsAsEncrypted. Update rhs_as_jsonb to match
the actual transformer order by removing the special-case logic for stripping
eql_v2_encrypted and simplifying it to return the expression unchanged when it
is already a JSONB cast, otherwise wrap it in a JSONB cast. Keep the behavior
localized in rhs_as_jsonb so the STE-vec ordering rewrite remains aligned with
make_transformer execution.

In
`@packages/eql-mapper/src/transformation_rules/rewrite_jsonb_ste_vec_equality.rs`:
- Around line 146-154: The RHS handling in build_eq_term_rhs is cloning the
expression before extracting hm and oc, which can break NodeKey identity and
prevent CastLiteralsAsEncrypted from rewriting encrypted literals. Update
rewrite_jsonb_ste_vec_equality::build_eq_term_rhs to follow the same
single-consumption pattern used by the ordering rule: derive the hm/oc path
without passing cloned Expr values into field_text, or extract the needed fields
before any clone so the original rhs node remains intact for the transformer.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5929dbcc-b3e9-4b2f-871e-904dac54d064

📥 Commits

Reviewing files that changed from the base of the PR and between 1af9968 and 1ad4c3f.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • CHANGELOG.md
  • Cargo.toml
  • packages/cipherstash-proxy-integration/src/map_ope_index_order.rs
  • packages/cipherstash-proxy/src/postgresql/data/from_sql.rs
  • packages/cipherstash-proxy/src/proxy/zerokms/zerokms.rs
  • packages/eql-mapper/src/eql_mapper.rs
  • packages/eql-mapper/src/lib.rs
  • packages/eql-mapper/src/ste_vec_ordering.rs
  • packages/eql-mapper/src/transformation_rules/mod.rs
  • packages/eql-mapper/src/transformation_rules/rewrite_jsonb_ste_vec_equality.rs
  • packages/eql-mapper/src/transformation_rules/rewrite_jsonb_ste_vec_ordering.rs
  • packages/eql-mapper/src/transformation_rules/rewrite_scalar_ope_ordering.rs
  • packages/eql-mapper/src/type_checked_statement.rs
✅ Files skipped from review due to trivial changes (3)
  • CHANGELOG.md
  • packages/cipherstash-proxy-integration/src/map_ope_index_order.rs
  • packages/eql-mapper/src/transformation_rules/mod.rs
🚧 Files skipped from review as they are similar to previous changes (8)
  • packages/eql-mapper/src/transformation_rules/rewrite_jsonb_ste_vec_ordering.rs
  • packages/eql-mapper/src/type_checked_statement.rs
  • packages/eql-mapper/src/transformation_rules/rewrite_scalar_ope_ordering.rs
  • packages/cipherstash-proxy/src/proxy/zerokms/zerokms.rs
  • packages/cipherstash-proxy/src/postgresql/data/from_sql.rs
  • packages/eql-mapper/src/eql_mapper.rs
  • Cargo.toml
  • packages/eql-mapper/src/lib.rs

Comment thread packages/eql-mapper/src/ste_vec_ordering.rs
…CIP-3283)

The scalar OPE ordering/range rewrite emitted `decode(col ->> 'op','hex')`,
but `eql_v2_encrypted` defines its own `->>` operator (a selector accessor,
alias of `->`), so the bare `->>` bound to that instead of native jsonb
extraction — PostgreSQL then tried to parse 'op' as a composite literal and
raised `EP0001/22P02 malformed record literal: "op"` at runtime.

Cast the operand to its base `jsonb` type first
(`decode((col)::jsonb ->> 'op','hex')`) so native jsonb field extraction is
used, mirroring the jsonb STE-vec rules. Unit tests asserted the SQL string
so they passed while the SQL was invalid; only e2e (CI, PG14-17) caught it.
Updated the rewrite tests to expect the `::JSONB` cast.
A jsonb STE-vec *term* comparison with a bare numeric literal on the
right-hand side (e.g. `jsonb_path_query_first(pii, '$.path') > 70`)
failed at runtime with a fatal `SteVecTerm only supports scalar values`
error.

The eql-mapper correctly reclassifies the literal to `EqlTerm::SteVecTerm`,
but `literal_from_sql` converted bare numeric literals via the generic
`Value::Number` arm, which ignores `eql_term` and (for a Json cast type)
produced a non-scalar `Plaintext::Json`. The client's `QueryOp::SteVecTerm`
rejects non-scalars. Parameters and quoted string literals were unaffected
because they route through `text_from_sql`/`binary_from_sql`, which already
reduce sv-term values to scalars.

Add a guarded arm that maps a bare numeric sv-term literal to
`Plaintext::Float` (f64), matching how cipherstash-client encodes stored
jsonb numeric leaves so ordering/equality comparisons stay consistent.

Verified end-to-end: the showcase Test 4 (high cardiovascular risk,
`> 70`) now passes and the full showcase completes successfully.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cipherstash-proxy/src/postgresql/data/from_sql.rs`:
- Around line 603-625: The SteVecTerm RHS collection in eql_mapper’s
reclassification loop only handles Expr::Value, so negative literals parsed as
Expr::UnaryOp are skipped. Update the matching logic to also recognize
Expr::UnaryOp with Minus and unwrap its inner Number or Placeholder before
inserting into the literals set. Keep the existing Expr::Value behavior intact,
and ensure the same path is used for both direct values and unary-negated RHS
operands.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 476940ea-358a-4c14-b47c-ad8596083d00

📥 Commits

Reviewing files that changed from the base of the PR and between 1ad4c3f and af5c963.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • packages/cipherstash-proxy/src/postgresql/data/from_sql.rs
  • packages/eql-mapper/src/lib.rs
  • packages/eql-mapper/src/transformation_rules/rewrite_scalar_ope_ordering.rs
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/eql-mapper/src/transformation_rules/rewrite_scalar_ope_ordering.rs
  • packages/eql-mapper/src/lib.rs

Comment thread packages/cipherstash-proxy/src/postgresql/data/from_sql.rs
@freshtonic freshtonic requested review from coderdan and tobyhede June 25, 2026 13:46
Reverts the CI-only pin to the suite PR #2061 prerelease, restoring the
stable published cipherstash-client 0.38.0. The prerelease was used to
validate scalar OPE end-to-end in CI (it carries the CIP-3280 `op`-store
fix); that validation is complete (full suite green on PG14-17).

Until CIP-3280 (suite PR #2061) is released and this is repointed to the
new stable version, the 11 scalar `map_ope_*` integration tests will fail
(0.38.0 does not store the OPE `op` term). All other tests remain green.
@tobyhede

Copy link
Copy Markdown
Contributor

CodeRabbit review notes (verified)

Ran CodeRabbit + manually verified each finding against current code. Notes below cover the findings in this PR's diff. (Several higher-severity CodeRabbit findings were on untracked working-tree files — bloom_credit/*.json PII, .github/secrets.env.plaintext, generate_ore_fixtures.rs, large_json_insert.rs, .serena/ — those are not part of this PR and are being handled separately.)

Should address

  • from_sql.rs (~L349-374) — sv-term RHS as TEXT/VARCHAR mis-encrypted. SteVecTerm + ColumnType::Json only matches the JSON|JSONB|BYTEA arm. A binary TEXT/VARCHAR-typed RHS falls through to the generic (_, _, TEXT|VARCHAR) arm, which calls text_from_sql(val, EqlTermVariant::Full, …) (hardcoded Full) and returns a full Plaintext::Json payload instead of the scalar sv-term. Add an explicit (SteVecTerm, Json, TEXT|VARCHAR) arm that preserves the SteVecTerm variant. Narrow trigger, but silently wrong when hit.

  • eql_mapper.rs (L244-256) — reused bind param reclassified globally. reclassify_as_ste_vec_term_if(value, ste_vec_params.contains(&p)) flips a param for all its occurrences. If the same $n is used in both a jsonb sv-term comparison and an ordinary encrypted predicate, it gets the wrong encrypted representation everywhere. Either error on mixed-use params or track typing per occurrence.

  • manager.rs (L105-119) — InvalidEncryptionConfig fails open. On a malformed (not missing) config at startup we log and fall through to an empty EncryptConfig::new(), i.e. start serving with encryption disabled. Consider failing closed (return the error) for the invalid case, distinct from the missing-table bootstrap path.

Known limitation (confirm intent)

  • rewrite_jsonb_ste_vec_equality.rs (L68-78) — reversed operand orientation. value = col -> selector is intentionally left to fall back to root eql_v2_encrypted equality; the reclassification pass gates on the same is_ste_vec_accessor(left) predicate so the two passes stay in lockstep. Verified consistent and documented. Worth confirming the reversed form doesn't silently mis-compare (vs erroring) — if it can, a guard/error would be safer than a wrong result.

Minor

  • common.rs (L94-96)format!("TRUNCATE {}", table) splices an identifier; test-controlled names only, but quoting the identifier removes the foot-gun.
  • error.rs (L188)InvalidEncryptionConfig has no docs link (some neighbors do); add one for the customer-facing path.
  • CHANGELOG.md — current-stack entries are split across Unreleased/older sections; consolidate under 2.2.4.

Comment thread packages/cipherstash-proxy-integration/src/map_ope_index_order.rs Outdated
Comment thread packages/eql-mapper/src/ste_vec_ordering.rs
Comment thread packages/cipherstash-proxy/src/postgresql/data/from_sql.rs
Comment thread packages/eql-mapper/src/ste_vec_ordering.rs
Comment thread packages/eql-mapper/src/eql_mapper.rs
A jsonb sv comparison RHS classified as `SteVecTerm` whose column has no
ste_vec index in the EncryptConfig previously fell back to `EqlOperation::Store`.
Because every `eql_v2_encrypted` column is granted `JsonLike` in the eql-mapper
schema regardless of its configured indexes, such a value can reach the encrypt
path while its column lacks a ste_vec index. The SQL has already been rewritten
to extract a STE-vec term (`oc`/`hm`), so storing a generic payload silently
drops the predicate. Fail fast with `EncryptError::UnknownIndexTerm` instead.

Extracts the EQL-operation selection into `eql_operation_for_column` (unit
testable, behaviour-preserving for Full/Partial/Tokenized/JsonPath/JsonAccessor).

Adds regression/characterization tests pinning safety invariants surfaced while
reviewing CodeRabbit feedback on PR #409:
- negative-literal jsonb sv comparisons are rejected at type-check (UnaryOp RHS
  forced to Native), never rewritten with a mismatched encryption;
- a different encrypted column on the RHS is rejected by EQL term unification
  (jsonb sv + scalar), so the rewrite `is_eql_typed(right)` gates are sufficient;
- literal-form jsonb sv equality encrypts the RHS literal (no plaintext leak).
@freshtonic freshtonic changed the base branch from main to v3 June 29, 2026 05:55
- from_sql: add Value::Bool arm to json_scalar_to_plaintext so boolean sv
  leaves support hm equality (col -> 'flag' = true) instead of being
  rejected; add unit + integration coverage
- ste_vec_ordering: add unit tests for the shared predicates/operand
  helpers (is_ste_vec_accessor, is_ordering/equality_operator,
  rhs_as_jsonb, is_eql_typed)
- dedupe is_eql_typed: point the scalar OPE and containment rewrite rules
  at the shared ste_vec_ordering::is_eql_typed helper
- eql_mapper: compute ste_vec_term_rhs_keys once in resolve() and pass the
  param/literal halves into param_types/literal_types (was scanned twice)
- map_ope_index_order: rewrite the stale CIP-3280 TODO to reflect that
  NULL ORDER BY placement is already covered and the remaining gap is
  gated on the cipherstash-client pin bump
Points the proxy at the published cipherstash-client 0.38.1 — the stable
release of the scalar-OPE `op`-store fix (CIP-3280, suite PR #2061),
replacing the prior 0.38.0 pin. Cargo.lock refreshed (also pulls
zerokms-protocol 0.12.19 -> 0.12.20 as required by 0.38.1).

With 0.38.1, scalar OPE columns store the `op` term, so the full
integration suite (incl. the scalar map_ope_* tests) is expected green.
Local: cargo check --workspace + eql-mapper tests + mise check all pass.
PR #409 was retargeted from `main` to the `v3` integration branch, but the
Test and Benchmark workflows only triggered on `main`, so CI stopped running.
Add `v3` to the pull_request + push branch filters of both workflows.
The new select_where_jsonb_bool_eq tests bound a raw Rust `bool` as the
`$2` query param, but the encrypted jsonb column expects a jsonb value —
failing at param binding with `ToSql WrongType { postgres: Jsonb, rust: bool }`
before reaching the proxy. Bind `serde_json::Value::Bool(...)` like the other
jsonb equality tests so the boolean-leaf equality path is actually exercised.
Bool sv-leaf equality (`col -> 'flag' = $bool`) was never implemented and is
not wired end-to-end: the equality rewrite reclassifies the RHS to SteVecTerm,
but cipherstash-client's SteVecTerm query encoding requires an *orderable*
term, and a boolean has only the `hm` equality term (no `oc`) — so it fails
with "Cannot convert value of type Option<bool> to orderable term".

Enabling it needs a cipherstash-client change (emit the hm term for
non-orderable sv leaves). Revert the proxy-side bool work that can't function
without it: the json_scalar_to_plaintext Value::Bool arm + its two unit tests,
and the select_where_jsonb_bool_eq integration tests. Pre-existing scalar
bool handling/tests are unaffected. Tracked as a follow-up.

@coderdan coderdan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Just minor things that should be addressed.

Comment on lines +49 to +51
/// ```sql
/// eql_v2.ore_cllw(col -> selector) > eql_v2.ore_cllw($param::jsonb)
/// ```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be addressed in EQL V3 but this approach locks Proxy in to CLWW. Probably not worth addressing for 2.3 but leaving as a comment for anyone who looks at this in the future (including Claude).

);
}

/// Group B (CIP-3279): ordering comparisons on a jsonb sv-element extracted

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should only reference public GH issues in public repos.

@@ -1,3 +1,20 @@
// TODO(CIP-3280): add an end-to-end test for `<`/`<=`/`>`/`>=` comparisons

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should address the TODO. Also reference a public GH issue instead of Linear.

@freshtonic freshtonic merged commit 4329a25 into v3 Jun 30, 2026
6 checks passed
@freshtonic freshtonic deleted the james/cip-3233-proxy-drop-vendor-patch branch June 30, 2026 01:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants