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
The new docs site doesn't currently cover how users should create PostgreSQL indexes on encrypted columns. CREATE INDEX appears zero times across content/ — grep-confirmed. This is a regression vs. the previous docs and a real user-facing gap because the correct index-creation pattern differs between self-hosted PostgreSQL and Supabase, and the wrong path silently degrades to sequential scans even when EQL is correctly installed.
Context
EQL ships in two build variants:
Full (cipherstash-encrypt.sql) — installs custom btree and hash operator classes (encrypted_operator_class, encrypted_hash_operator_class) on the eql_v2_encrypted type. This lets users index encrypted columns directly: CREATE INDEX ... USING btree (encrypted_col).
Supabase (cipherstash-encrypt-supabase.sql) — omits all CREATE OPERATOR CLASS / CREATE OPERATOR FAMILY statements because they require superuser. On Supabase, the only working index path is functional indexes that wrap the column with an EQL extraction function:
CREATEINDEXusers_email_hmac_idxON users USING hash (eql_v2.hmac_256(email));
CREATEINDEXusers_email_bloom_idxON users USING gin (eql_v2.bloom_filter(email));
Queries must then be written in the matching wrapped form:
-- Hits the hash index aboveSELECT*FROM users
WHEREeql_v2.hmac_256(email) =eql_v2.hmac_256('alice@example.com'::jsonb::eql_v2_encrypted);
-- Bare equality on the column does NOT hit the functional index.-- On Supabase this falls back to a sequential scan.SELECT*FROM users WHERE email ='alice@example.com'::jsonb::eql_v2_encrypted;
What's currently in the docs
Page
What it says about indexes
Gap
supabase.mdx
Mentions CREATE OPERATOR FAMILY is omitted as a technical setup note.
Doesn't connect this to "here's how to create indexes that actually work."
searchable-encryption.mdx:134
One Callout: "If your PostgreSQL database does not support EQL Operator families, use the eql_v2.ore_block_u64_8_256() function for ORDER BY."
Single sentence, ORDER BY only — doesn't cover equality, pattern match, or the general principle.
eql-guide.mdx:112
"EQL leverages PostgreSQL's native indexing capabilities ... B-tree for exact/range, GIN for pattern matching."
Abstract — never shows the actual CREATE INDEX statements.
There's nothing telling a user:
Which functional indexes to create for which query type
The query-form requirement (must use wrapped eql_v2.hmac_256(col) form on Supabase)
That self-hosted users have a simpler CREATE INDEX ... USING btree (col) option that doesn't exist on Supabase
The performance consequences of getting it wrong (silently slow queries)
Suggested page structure
A new page /stack/cipherstash/encryption/indexes (or similar), cross-linked from supabase.mdx and searchable-encryption.mdx. Core table:
Query type
Self-hosted (full EQL)
Supabase
Equality
USING btree (col) (uses opclass) orUSING hash (eql_v2.hmac_256(col))
USING hash (eql_v2.hmac_256(col)) only
Range / ORDER BY
USING btree (col)
None today — being addressed by upcoming OPE-index work
Pattern match
USING gin (eql_v2.bloom_filter(col))
Same
JSONB containment
USING gin (eql_v2.ste_vec(col))
Same
Plus a Supabase-specific Callout with the query-form requirement — bare WHERE col = … doesn't engage functional indexes. Queries must wrap the column with the same extraction function used in the index.
The OPE-index work in flight will introduce a second Supabase-specific guidance area (range/ORDER BY) that needs the same treatment.
Reference material to draw from
tests/sqlx/fixtures/bench_setup.sql in the EQL repo — currently the only canonical example of all five index types in one place.
tests/sqlx/fixtures/drop_operator_classes.sql — the fixture that simulates the Supabase environment (drops the opclasses to match what the supabase build doesn't install).
Out of scope
The OPE-index path for range queries on Supabase — separate work; once it lands, it'll need its own row in the table above.
Migration / upgrade docs from the previous index-creation pages — likely a clean rewrite is faster than porting.
Summary
The new docs site doesn't currently cover how users should create PostgreSQL indexes on encrypted columns.
CREATE INDEXappears zero times acrosscontent/— grep-confirmed. This is a regression vs. the previous docs and a real user-facing gap because the correct index-creation pattern differs between self-hosted PostgreSQL and Supabase, and the wrong path silently degrades to sequential scans even when EQL is correctly installed.Context
EQL ships in two build variants:
Full (
cipherstash-encrypt.sql) — installs custom btree and hash operator classes (encrypted_operator_class,encrypted_hash_operator_class) on theeql_v2_encryptedtype. This lets users index encrypted columns directly:CREATE INDEX ... USING btree (encrypted_col).Supabase (
cipherstash-encrypt-supabase.sql) — omits allCREATE OPERATOR CLASS/CREATE OPERATOR FAMILYstatements because they require superuser. On Supabase, the only working index path is functional indexes that wrap the column with an EQL extraction function:Queries must then be written in the matching wrapped form:
What's currently in the docs
supabase.mdxCREATE OPERATOR FAMILYis omitted as a technical setup note.searchable-encryption.mdx:134eql_v2.ore_block_u64_8_256()function forORDER BY."eql-guide.mdx:112CREATE INDEXstatements.There's nothing telling a user:
eql_v2.hmac_256(col)form on Supabase)CREATE INDEX ... USING btree (col)option that doesn't exist on SupabaseSuggested page structure
A new page
/stack/cipherstash/encryption/indexes(or similar), cross-linked fromsupabase.mdxandsearchable-encryption.mdx. Core table:USING btree (col)(uses opclass) orUSING hash (eql_v2.hmac_256(col))USING hash (eql_v2.hmac_256(col))onlyUSING btree (col)USING gin (eql_v2.bloom_filter(col))USING gin (eql_v2.ste_vec(col))Plus a Supabase-specific Callout with the query-form requirement — bare
WHERE col = …doesn't engage functional indexes. Queries must wrap the column with the same extraction function used in the index.Why this matters now
Reference material to draw from
tests/sqlx/fixtures/bench_setup.sqlin the EQL repo — currently the only canonical example of all five index types in one place.tests/sqlx/fixtures/drop_operator_classes.sql— the fixture that simulates the Supabase environment (drops the opclasses to match what the supabase build doesn't install).Out of scope