Skip to content

Adopt EQL V3 self-configuring domain types in @cipherstash/stack schema #534

Description

@coderdan

Summary

Adopt EQL V3's self-configuring domain types in the @cipherstash/stack schema builder, consuming the new @cipherstash/eql package (published from the EQL repo — see that repo's companion issue).

EQL V3 replaces the per-column index-configuration model with self-configuring Postgres domain types (int4_eq, text_match, text_search, …). The type name encodes the searchable-encryption metadata, so:

  1. the types are Postgres Domains, which ORMs can read as jsonb; and
  2. the column is inherently self-configured — the searchable-encryption metadata no longer has to be configured client-side, because the type name specifies it (e.g. int4_eq always carries an HMAC equality term).

Current state

The going-forward builder is @cipherstash/stack/schema (packages/stack/src/schema/index.ts). It builds an EncryptConfig {v:1} with per-column cast_as + indexes: {ore, unique, match, ste_vec}, validates it, and passes it to protect-ffi at newClient(...) time (packages/stack/src/encryption/index.ts). It already has the seam for this migration: an eqlCastAsEnum (text/int/small_int/big_int/real/double/boolean/date/jsonb) and a toEqlCastAs() SDK→EQL mapper.

There is no EQL V3 footprint in the repo today — everything is EQL v2 (eql_v2 schema, eql_v2_encrypted composite type, eql_v2_configuration state machine, vendored eql-2.3.1 bundle).

Scope note. This issue targets the primary @cipherstash/stack/schema builder. The legacy standalone @cipherstash/schema (packages/schema) — used by @cipherstash/protect, now in maintenance mode — is intentionally out of scope here; migrate it separately if desired.

Goal

Migrate @cipherstash/stack/schema to depend on @cipherstash/eql and represent/emit V3 domain types instead of the index-config model, so that choosing a column's capabilities selects a self-configuring domain type rather than hand-assembling an indexes object.

Scope (primary)

  • Add @cipherstash/eql as a dependency and type column payloads with its generated types.
  • Map the builder API → V3 domain types, e.g.:
    • number + equality + range → int8_ord_ore (or width-appropriate int4_*)
    • string + match → text_match
    • string + equality + match + range → text_search
    • storage-only → the bare domain (int4, text, bool, …)
  • Decide whether the public API exposes the domain types directly or keeps the capability-builder (.equality() / .orderAndRange() / .freeTextSearch()) and infers the domain type from the chosen capabilities (eqlCastAsEnum / toEqlCastAs is the existing seam).
  • Replace the indexes object / EncryptConfig {v:1} emission accordingly.

Knock-on: docs / README (explicitly in scope)

  • README.md (top-level quick-start + "Searchable encryption"), packages/stack/README.md + CHANGELOG.md.
  • AGENTS.md, .cursorrules, docs/plans/encryption-migrations.md.
  • Agent skills: skills/stash-encryption, stash-drizzle, stash-dynamodb, stash-supabase, stash-cli (SKILL.md).
  • Examples: examples/basic, examples/prisma, examples/supabase-worker.
  • Add a changeset (repo uses Changesets, access: restricted).

Downstream impact (flagged — likely separate follow-up issues)

The schema builder is the entry point, but switching to self-configuring types ripples through the stack. Capturing these so they aren't lost:

  • protect-ffi owns cast_as-driven encoding and must accept/encode V3 domain types — coordinate a lockstep version bump (note the 7-day minimumReleaseAge cooldown excludes @cipherstash/protect-ffi*).
  • FFI initencryptConfigSchema hard-locks v: z.literal(1); packages/stack/src/wasm-inline.ts normalizeCastAs() rewrites cast_as via toEqlCastAs before the WASM newClient.
  • Proxy / DB config state machinepackages/cli db/push.ts (toEqlConfigpublic.eql_v2_configuration, pending/activate dance) and @cipherstash/migrate src/eql.ts (eql_v2.* wrappers). Self-configuring types may remove or reshape this.
  • Drizzlepackages/drizzle/src/pg/* hardcodes the eql_v2_encrypted type name and eql_v2.* operators (eq/gt/order_by/jsonb_path_query_first/…) and the composite-literal codec.
  • prisma-next — vendored eql-2.3.1 bundle (src/migration/eql-install.generated.ts), cipherstash/<type>@1 codec ids, and EQL_V2_* constants.
  • DynamoDBprotect-dynamodb / stack/src/dynamodb branch on cast_as === 'json' + indexes.ste_vec.

Acceptance criteria

  • @cipherstash/stack/schema depends on @cipherstash/eql and represents columns as V3 domain types (self-configuring) rather than the indexes config object.
  • Docs, README, examples, and skills are updated to the V3 type model.
  • A changeset is added; downstream-impact items above are filed as follow-up issues.

Dependency

Blocked on @cipherstash/eql being published from the EQL repo: cipherstash/encrypt-query-language#331

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions