Skip to content

Add a stash-indexing skill: how to index EQL v3 encrypted columns #753

Description

@coderdan

Summary

There is currently no guidance anywhere in the shipped skills on how to index an EQL v3 encrypted column. As a result, integrations that are otherwise correct — encrypted at rest, genuinely EQL-backed search, exact round-trip — ship with no index on any encrypted predicate.

I'd like to propose a new skill covering this. Suggested name: stash-indexing (see naming note below).

Evidence

Two independent integration evals against stash@1.0.0-rc.4 / EQL v3 3.0.2, on two different surfaces (Prisma Next adapter, and raw postgres-js in a Supabase Edge Function). Both produced working, verified integrations. Neither created a single index on an encrypted column, and both assessors independently flagged the same root cause.

Grepping the skills installed by stash init across those runs — stash-encryption, stash-cli, stash-prisma-next — for create index|using gin|ord_term(|bloom_filter(|eq_term(:

  • Zero CREATE INDEX examples.
  • The only ord_term( occurrences are in an ORDER BY context (stash-encryption "On the Wire: Operators and Ordering"; stash-cli "Common issues"). Nothing connects the extractor functions to index creation.

So an agent (or a developer) reading the skills end to end has no reason to know encrypted columns can be indexed, let alone how.

These all work, including without superuser

Verified against local Supabase Postgres as postgres with rolsuper = f, on a table whose columns are public.eql_v3_integer_ord and public.eql_v3_text_search. All three succeed:

-- ordering / range, OPE-backed (_ord, text_search)
CREATE INDEX ON transactions (eql_v3.ord_term(amount));

-- free-text match (trigram bloom)
CREATE INDEX ON transactions USING gin ((eql_v3_internal.bloom_filter(description::jsonb)));

-- equality
CREATE INDEX ON transactions (eql_v3.eq_term(description));

The non-superuser part matters: the surrounding docs correctly warn that the ORE operator class needs superuser and is unavailable on managed Postgres, and I suspect that warning is being over-generalised into "you can't index encrypted columns on Supabase." These three forms are all available on a managed, non-superuser role.

Proposed scope

  • The three index forms above, each tied to the domain families it applies to (*_ord / text_searchord_term; Match/Searchbloom_filter GIN; equality-capable domains → eq_term).
  • Explicitly state that all three install without superuser, and how this differs from the ORE opclass restriction — that distinction is the confusing part today.
  • Which domains have no index option (storage-only), so the absence is a deliberate answer rather than a gap.
  • How this interacts with the ORM integrations: Drizzle and Prisma Next emit the operators for you, but neither emits DDL for these indexes, so it's the developer's job either way.
  • Ideally, a note in the rollout lifecycle about when to create them (after backfill, before switching reads).

Naming

I'd lean stash-indexing over stash-postgres / stash-postgres-indexing:

  • The gap is cross-cutting, not raw-SQL-specific — it applies equally to Drizzle, Prisma Next, Supabase, and hand-written SQL. stash-postgres would imply the no-ORM surface.
  • There is a genuinely separate gap for the raw-SQL / non-ORM surface (binding forms for postgres-js / pg, the WASM entry, Deno specifics). That deserves its own skill — I'll open a second issue for it rather than conflate the two here.

Happy to be overruled on the name if stash-postgres is preferred for discoverability.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions