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
docs(test-kit): correct the ORE deferral reason — unusable, not silently wrong
Measured on supabase/postgres as the non-superuser `postgres` role: the ORE
domains are created, but their CHECK calls `ore_domain_unavailable()` and the
first INSERT raises, with a hint naming the alternatives. The same insert
succeeds on plain Postgres as a superuser.
So ORE is loudly unusable on managed Postgres, not silently wrong. The bundle
guards correctly. The earlier reason — 'ORDER BY silently mis-sorts' — described
a type-level property (`ore_block_256` is a composite type, so with no opclass
Postgres falls back to record comparison) that no value can reach, because the
write is refused first.
The deferral still stands, for a better reason: a matrix that must pass on both
a superuser and a managed database cannot seed an ORE column at all.
Corrects the claim in the plan doc, and in cipherstash/encrypt-query-language#395.
Copy file name to clipboardExpand all lines: docs/superpowers/plans/2026-07-10-eql-v3-integration-tests.md
+2-5Lines changed: 2 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -26,11 +26,8 @@
26
26
-**v3 JSON is unimplemented, not merely untested.** The bundle ships `public.eql_v3_json` and `public.eql_v3_jsonb_entry` (51 `CREATE DOMAIN`s, 56 `eql_v3.{ste_vec,jsonb_path,selector,contained_by}` functions), but the SDK models 41 domains and `eql/v3/columns.ts` admits only `bigint | boolean | date | number | string | timestamp` as `cast_as`. There is no `'json'` kind, so a v3 JSON column cannot be declared. **The gap is in the core v3 schema; both adapters inherit it.**`eql/v3/drizzle/codec.ts:38` already carries defensive SteVec decode logic (`sv[0].c`) for documents nothing can currently create.
27
27
-**`@cipherstash/stack/drizzle` is not `@cipherstash/drizzle`.** They are a fork: `@cipherstash/drizzle@3.0.3` peer-depends on the predecessor SDK `@cipherstash/protect@12` and exports `createProtectOperators` (2,038 lines); stack's in-tree copy exports `createEncryptionOperators` (1,945 lines); ~645 lines have diverged. There is no dependency between them — hence no cycle today, only duplication. Docs reference the stack subpath 13× and the package 2×.
28
28
-**Ordering is safe on OPE.** EQL 3.0.0 pins `_ord` domains to `op` (CLLW-OPE), which orders via a native `bytea` btree, so `ORDER BY eql_v3.ord_term(col)` works on every provider without superuser.
29
-
-**`_ord_ore` is not "absent" on managed Postgres — it is silently wrong, and only for `ORDER BY`.** Measured against `supabase/postgres:17.4.1.048` after `stash eql install --eql-version 3 --supabase --direct` as the non-superuser `postgres` role: all 51 `public.eql_v3_*` domains install, and so do the 33 ORE comparison functions; only the btree opclass `eql_v3_internal.ore_block_256_operator_class` is skipped (`pg_opclass` count 0 vs 1 on plain Postgres).
30
-
-**Range filters still work everywhere.**`<`/`>`/`<=`/`>=` on `ore_block_256` are backed by `ore_block_256_lt` → the ORE comparator, and operators need no opclass.
31
-
-**`ORDER BY eql_v3.ord_term_ore(col)` does not error — it sorts, wrongly.**`ore_block_256` is a *composite* type, so with no opclass to resolve, Postgres falls back to built-in record comparison, which walks down to the raw `bytes` field and compares bytewise. That order is deterministic and stable but uncorrelated with plaintext: over 200 random well-formed terms it disagreed with the ORE comparator on **87**. Proof that the two variants take different paths: `ORDER BY` on a malformed 1-byte term returns rows on Supabase and raises `Malformed ORE term` on plain Postgres.
32
-
- A btree **index** on such a column cannot be created at all.
33
-
- That is why ORE gets its own suite rather than a skip. See `docs/eql-v3-ord-term-ordering-defect.md`.
29
+
-**`_ord_ore` columns cannot hold data on managed Postgres — and that is correct.** Measured against `supabase/postgres:17.4.1.048`, EQL 3.0.0, as the non-superuser `postgres` role: the nine ORE domains are created, but their CHECK calls `eql_v3_internal.ore_domain_unavailable()`, so the first `INSERT` raises with a hint naming `_eq` / `_ord` / `_ord_ope` as alternatives. The same insert succeeds on plain Postgres as a superuser. The bundle guards correctly; there is no silent wrongness. (An earlier draft of this plan claimed ORDER BY silently mis-sorts. It does at the *type* level — `ore_block_256` is a composite type, so with no opclass Postgres falls back to record comparison — but no value can reach a table to be ordered. `cipherstash/encrypt-query-language#395` records the correction.) A matrix that must pass on both databases cannot cover ORE, because the seed insert fails on one; ORE gets a superuser-only suite.
30
+
-**Supabase CAN order encrypted `_ord` columns.** A bare `ORDER BY col` sorts the ciphertext envelope through jsonb's default opclass (measured: `r00,r04,r08,r01,…` for plaintext `r00..r09`). But `eql_v3.ord_term` returns the `op` term, OPE is order-preserving, and PostgREST can emit the jsonb path `order=col->>op`, which reproduces plaintext order in both directions for `integer_ord` and `text_search`. The adapter now does exactly that, and rejects only ORE-backed and ordering-less columns.
34
31
-**The CLI installs fine as a non-superuser.**`stash eql install --eql-version 3 --supabase --direct --database-url …` runs non-interactively as `postgres` on `supabase/postgres` and grants `anon` USAGE on **both**`eql_v3` and `eql_v3_internal`. No `SUPABASE_ADMIN_URL` is needed. (`--supabase` without `--direct`/`--migration` prompts, so pass `--direct`.)
35
32
-**`postgres-eql:17-2.3.1` ships EQL v2, not v3.** Every DB variant must install v3 at setup.
36
33
-**`./supabase` and `./drizzle` are published stack subpaths** (`npm view @cipherstash/stack exports`); `./eql/v3/drizzle` is not.
0 commit comments