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
EQL is the surface every CipherStash integration ultimately sits on — the public.eql_v3_* column domains, the eql_v3.query_* operand domains, the operators, and the term extractors — but there is no agent skill for it. An agent working in a consuming repo reconstructs that surface second-hand today: from the CipherStash Stack skills (which document it only as far as their own integration needs), from dist/sql/cipherstash-encrypt.sql, or from the Postgres catalog.
That gap has already produced re-derivation work downstream. cipherstash/stack#777 adds a stash-postgres skill for hand-written SQL against encrypted columns, and a good deal of its content — the column-domain → query-domain naming rule, which operators each domain admits, which domains carry no query terms at all — is a hand-transcription of this repo's surface. It will drift, because nothing checks it against the bundle it describes.
The skill belongs here, next to the definitions it documents.
Scope
A SKILL.md in this repo covering:
Purpose — what EQL is and what problem it solves, and where the boundary sits between EQL (SQL-side: domains, operators, indexes) and a client (encryption, term generation, key management).
Installation — the bundle and the @cipherstash/eql package, what stash eql install applies, checking the installed version with SELECT eql_v3.version(), and upgrade/reinstall semantics — notably that the install SQL opens with DROP SCHEMA IF EXISTS eql_v3 CASCADE, which takes every functional index on an extractor with it while leaving columns and data intact.
The domain type naming system — the public.eql_v3_<type>_<index> → eql_v3.query_<type>_<index> derivation, and the irregular cases (eql_v3_json_search → eql_v3.query_json).
The full domain type list — every domain with its query domain, or an explicit "storage only, carries no query terms". Ideally generated from the bundle rather than hand-maintained, so it cannot drift.
Query forms — the operator matrix per domain (=, <>, <, <=, >, >=, @@, @>), the function twins (eql_v3.eq, eql_v3.matches, the comparison functions), JSON containment and selector extraction, and the storage-payload-vs-query-term distinction — including that the jsonb operator overloads coerce to the storage domain, so an uncast query term fails a CHECK rather than doing what the caller meant.
Indexing — functional indexes over the term extractors, opclass selection, and platform constraints.
Performance tuning — what engages an index versus what silently sequential-scans, EXPLAIN verification, and the cost of GROUP BY / DISTINCT / ORDER BY against a raw encrypted column instead of an extractor.
Cross-references
The skill should hand off rather than duplicate, in both directions.
Using EQL from an application → the CipherStash Stack skills. These ship inside the stash npm tarball and are installed into consuming repos by stash init:
stash-encryption — schema authoring, the client API, the rollout/cutover lifecycle
stash-edge — the WASM entry for Deno / Workers / Supabase Edge Functions
stash-cli — stash eql install, stash db validate
Querying through CipherStash Proxy → https://github.com/cipherstash/proxy. Worth an explicit section, because it inverts the model the rest of the skill describes: the application writes plaintext SQL and Proxy encrypts on write and decrypts on read, so there are no query terms to mint and no operand casts to get right. Proxy also reads its encryption config from eql_v2_configuration (populated by stash db push) rather than from the column's domain, which is an EQL v2 lifecycle — worth stating plainly alongside the v3 model, since the two are easy to conflate.
Conversely, the Stack skills should point here as the authoritative definition of the operator and domain surface. cipherstash/stack#777 adds that link, and cites this repo as where operator gaps and domain-level bugs belong.
Generate the reference tables, don't hand-maintain them
The domain list, the column-domain → query-domain mapping, and the operator matrix are the parts of this skill that will rot. They are also the parts that are already machine-readable in the published package, so they should be generated and then guarded by a test rather than transcribed.
Two sources ship inside @cipherstash/eql, and neither needs a database:
The CREATE OPERATOR statements in dist/sql/cipherstash-encrypt.sql — 3,025 of them in 3.0.2. Each carries LEFTARG, RIGHTARG, and FUNCTION, which is exactly one cell of the matrix.
The generated per-domain .d.ts types, whose doc comments already name the domain and its operators:
That also recovers the operator→function mapping (eql_v3.eq, eql_v3.matches, …) for free.
Why this matters beyond tidiness. The matrix in stash-postgres (cipherstash/stack#777) was built by querying pg_operator against a live EQL 3.0.2 install. That is authoritative — pg_operator is what Postgres consults to resolve col @@ $1, so it is the runtime truth, and it is the right thing to check when debugging a live "operator does not exist". But it is unrepeatable in CI without standing up Postgres with EQL installed, so nothing catches the table drifting when the version moves. A generator over either source above closes that gap with no database at all.
Worth doing here rather than downstream: the same generated artifact can back this skill's tables and be published for consumers, so stash-postgres and the other Stack skills stop maintaining a parallel hand-written copy. Publishing the operator matrix as data (JSON alongside the existing JSON Schemas) would let every consumer assert against it.
Why
EQL is the surface every CipherStash integration ultimately sits on — the
public.eql_v3_*column domains, theeql_v3.query_*operand domains, the operators, and the term extractors — but there is no agent skill for it. An agent working in a consuming repo reconstructs that surface second-hand today: from the CipherStash Stack skills (which document it only as far as their own integration needs), fromdist/sql/cipherstash-encrypt.sql, or from the Postgres catalog.That gap has already produced re-derivation work downstream. cipherstash/stack#777 adds a
stash-postgresskill for hand-written SQL against encrypted columns, and a good deal of its content — the column-domain → query-domain naming rule, which operators each domain admits, which domains carry no query terms at all — is a hand-transcription of this repo's surface. It will drift, because nothing checks it against the bundle it describes.The skill belongs here, next to the definitions it documents.
Scope
A
SKILL.mdin this repo covering:@cipherstash/eqlpackage, whatstash eql installapplies, checking the installed version withSELECT eql_v3.version(), and upgrade/reinstall semantics — notably that the install SQL opens withDROP SCHEMA IF EXISTS eql_v3 CASCADE, which takes every functional index on an extractor with it while leaving columns and data intact.public.eql_v3_<type>_<index>→eql_v3.query_<type>_<index>derivation, and the irregular cases (eql_v3_json_search→eql_v3.query_json).=,<>,<,<=,>,>=,@@,@>), the function twins (eql_v3.eq,eql_v3.matches, the comparison functions), JSON containment and selector extraction, and the storage-payload-vs-query-term distinction — including that thejsonboperator overloads coerce to the storage domain, so an uncast query term fails a CHECK rather than doing what the caller meant.EXPLAINverification, and the cost ofGROUP BY/DISTINCT/ORDER BYagainst a raw encrypted column instead of an extractor.Cross-references
The skill should hand off rather than duplicate, in both directions.
Using EQL from an application → the CipherStash Stack skills. These ship inside the
stashnpm tarball and are installed into consuming repos bystash init:stash-encryption— schema authoring, the client API, the rollout/cutover lifecyclestash-postgres— hand-written SQL overpg/postgres-js, no ORM (feat(cli): add the stash-postgres and stash-edge skills — raw-SQL predicates and the WASM entry stack#777)stash-indexing— functional indexes over the extractors, and theEXPLAINcheckliststash-drizzle,stash-supabase,stash-prisma-next,stash-dynamodb— per-integrationstash-edge— the WASM entry for Deno / Workers / Supabase Edge Functionsstash-cli—stash eql install,stash db validateQuerying through CipherStash Proxy → https://github.com/cipherstash/proxy. Worth an explicit section, because it inverts the model the rest of the skill describes: the application writes plaintext SQL and Proxy encrypts on write and decrypts on read, so there are no query terms to mint and no operand casts to get right. Proxy also reads its encryption config from
eql_v2_configuration(populated bystash db push) rather than from the column's domain, which is an EQL v2 lifecycle — worth stating plainly alongside the v3 model, since the two are easy to conflate.Conversely, the Stack skills should point here as the authoritative definition of the operator and domain surface. cipherstash/stack#777 adds that link, and cites this repo as where operator gaps and domain-level bugs belong.
Generate the reference tables, don't hand-maintain them
The domain list, the column-domain → query-domain mapping, and the operator matrix are the parts of this skill that will rot. They are also the parts that are already machine-readable in the published package, so they should be generated and then guarded by a test rather than transcribed.
Two sources ship inside
@cipherstash/eql, and neither needs a database:The
CREATE OPERATORstatements indist/sql/cipherstash-encrypt.sql— 3,025 of them in 3.0.2. Each carriesLEFTARG,RIGHTARG, andFUNCTION, which is exactly one cell of the matrix.The generated per-domain
.d.tstypes, whose doc comments already name the domain and its operators:A ~20-line parse of the first source reproduces a matrix row exactly:
That also recovers the operator→function mapping (
eql_v3.eq,eql_v3.matches, …) for free.Why this matters beyond tidiness. The matrix in
stash-postgres(cipherstash/stack#777) was built by queryingpg_operatoragainst a live EQL 3.0.2 install. That is authoritative —pg_operatoris what Postgres consults to resolvecol @@ $1, so it is the runtime truth, and it is the right thing to check when debugging a live "operator does not exist". But it is unrepeatable in CI without standing up Postgres with EQL installed, so nothing catches the table drifting when the version moves. A generator over either source above closes that gap with no database at all.Worth doing here rather than downstream: the same generated artifact can back this skill's tables and be published for consumers, so
stash-postgresand the other Stack skills stop maintaining a parallel hand-written copy. Publishing the operator matrix as data (JSON alongside the existing JSON Schemas) would let every consumer assert against it.