diff --git a/.changeset/pre.json b/.changeset/pre.json
index 12a91cce..d32644cb 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -23,8 +23,11 @@
"changesets": [
"adapter-package-split",
"adapter-split-skills",
+ "auth-binding-lockstep",
"cli-anonymous-telemetry",
"cli-eql-v3-single-bundle",
+ "drizzle-kit-eql-v3-ddl",
+ "eql-migration-command",
"eql-v3-adapter-type-robustness",
"eql-v3-bigint-domains",
"eql-v3-bundle-from-package",
@@ -37,16 +40,22 @@
"eql-v3-json-selector",
"eql-v3-json-skills",
"eql-v3-json",
+ "eql-v3-prisma-next",
"eql-v3-public-domains",
"eql-v3-rename-contains-to-matches",
"eql-v3-sole-docs",
+ "eql-v3-source-from-package",
"eql-v3-supabase-adapter",
"eql-v3-text-search",
"eql-v3-typed-client",
"eql-v3-typed-schema",
"eql-v3-wasm-inline",
"init-pins-runtime-versions",
+ "migrate-eql-v3",
+ "plan-complete-rollout-yes",
+ "prisma-example-eql-v3",
"prisma-next-0-14",
+ "prisma-next-drop-encrypted-prefix",
"prisma-next-joins-release-train",
"release-train-coupling",
"remove-legacy-drizzle-package",
@@ -59,6 +68,8 @@
"stash-cli-eql-v3-default",
"stash-cli-skill-refresh",
"stash-drizzle-skill-encrypt-query",
+ "stash-env-mint-credentials",
+ "stash-prisma-next-skill",
"stash-skills-contains-to-matches",
"stash-supabase-contains-substrings",
"supabase-encryption-error",
diff --git a/e2e/CHANGELOG.md b/e2e/CHANGELOG.md
index 21198d56..20ea6fb8 100644
--- a/e2e/CHANGELOG.md
+++ b/e2e/CHANGELOG.md
@@ -1,5 +1,22 @@
# @cipherstash/e2e
+## 0.0.3-rc.3
+
+### Patch Changes
+
+- Updated dependencies [8b2551a]
+- Updated dependencies [b8cb599]
+- Updated dependencies [0811330]
+- Updated dependencies [175eeb7]
+- Updated dependencies [3a86939]
+- Updated dependencies [b0634df]
+- Updated dependencies [4923c0a]
+- Updated dependencies [f188c7a]
+- Updated dependencies [8872d1e]
+ - @cipherstash/stack@1.0.0-rc.3
+ - stash@1.0.0-rc.3
+ - @cipherstash/wizard@1.0.0-rc.3
+
## 0.0.3-rc.2
### Patch Changes
diff --git a/e2e/package.json b/e2e/package.json
index 30073e97..72b91ba7 100644
--- a/e2e/package.json
+++ b/e2e/package.json
@@ -1,6 +1,6 @@
{
"name": "@cipherstash/e2e",
- "version": "0.0.3-rc.2",
+ "version": "0.0.3-rc.3",
"private": true,
"description": "End-to-end tests that exercise built CipherStash binaries and cross-package behaviour.",
"type": "module",
diff --git a/examples/basic/CHANGELOG.md b/examples/basic/CHANGELOG.md
index c49c345a..8496f189 100644
--- a/examples/basic/CHANGELOG.md
+++ b/examples/basic/CHANGELOG.md
@@ -1,5 +1,15 @@
# @cipherstash/basic-example
+## 1.2.14-rc.3
+
+### Patch Changes
+
+- Updated dependencies [8b2551a]
+- Updated dependencies [b8cb599]
+ - @cipherstash/stack@1.0.0-rc.3
+ - @cipherstash/stack-drizzle@1.0.0-rc.3
+ - @cipherstash/stack-supabase@1.0.0-rc.3
+
## 1.2.14-rc.2
### Patch Changes
diff --git a/examples/basic/package.json b/examples/basic/package.json
index 46b47708..734001f7 100644
--- a/examples/basic/package.json
+++ b/examples/basic/package.json
@@ -1,7 +1,7 @@
{
"name": "@cipherstash/basic-example",
"private": true,
- "version": "1.2.14-rc.2",
+ "version": "1.2.14-rc.3",
"type": "module",
"scripts": {
"start": "tsx index.ts"
diff --git a/examples/prisma/CHANGELOG.md b/examples/prisma/CHANGELOG.md
index 4614b506..a665b552 100644
--- a/examples/prisma/CHANGELOG.md
+++ b/examples/prisma/CHANGELOG.md
@@ -1,5 +1,19 @@
# @cipherstash/prisma-next-example
+## 0.1.0-rc.3
+
+### Minor Changes
+
+- a75513b: Convert the example app to EQL v3. Every column is now a concrete `public.eql_v3_*` domain authored with the per-domain constructors (`EncryptedTextSearch`, `EncryptedDoubleOrd`, `EncryptedBigIntOrd`, `EncryptedDateOrd`, `EncryptedBoolean`, `EncryptedJson`), wired through `cipherstashFromStackV3({ contractJson })`. The e2e harness runs the full v3 surface against live Postgres + ZeroKMS with no skips: the `eql*` operator vocabulary (equality/range plus `eqlMatch` free-text token search), `eqlAsc`/`eqlDesc` order-term sorting, encrypted JSON containment (`eqlJsonContains` — the v2 `cipherstashJsonb*` helpers do not exist in v3), lossless `bigint` beyond `Number.MAX_SAFE_INTEGER`, and the storage-only `eql_v3_boolean` refusal (`EncryptionOperatorError`) pinned as a feature. Migrations regenerate from the v3 contract: the initial app migration creates the `users` table against the v3 domains with zero `add_search_config` ops, and the cipherstash space carries both bundle baselines (v2 + v3).
+
+### Patch Changes
+
+- Updated dependencies [8b2551a]
+- Updated dependencies [a75513b]
+- Updated dependencies [4923c0a]
+ - @cipherstash/stack@1.0.0-rc.3
+ - @cipherstash/prisma-next@1.0.0-rc.3
+
## 0.0.6-rc.2
### Patch Changes
diff --git a/examples/prisma/package.json b/examples/prisma/package.json
index 4429e1a9..ea8f3843 100644
--- a/examples/prisma/package.json
+++ b/examples/prisma/package.json
@@ -1,7 +1,7 @@
{
"name": "@cipherstash/prisma-next-example",
"private": true,
- "version": "0.0.6-rc.2",
+ "version": "0.1.0-rc.3",
"description": "End-to-end example of @cipherstash/prisma-next: searchable application-layer encryption for Postgres with Prisma Next, using @cipherstash/stack as the SDK.",
"type": "module",
"scripts": {
diff --git a/packages/bench/CHANGELOG.md b/packages/bench/CHANGELOG.md
index d36464f8..f78d55b4 100644
--- a/packages/bench/CHANGELOG.md
+++ b/packages/bench/CHANGELOG.md
@@ -1,5 +1,14 @@
# @cipherstash/bench
+## 0.0.5-rc.3
+
+### Patch Changes
+
+- Updated dependencies [8b2551a]
+- Updated dependencies [b8cb599]
+ - @cipherstash/stack@1.0.0-rc.3
+ - @cipherstash/stack-drizzle@1.0.0-rc.3
+
## 0.0.5-rc.2
### Patch Changes
diff --git a/packages/bench/package.json b/packages/bench/package.json
index b9b961ea..4cd8be52 100644
--- a/packages/bench/package.json
+++ b/packages/bench/package.json
@@ -1,6 +1,6 @@
{
"name": "@cipherstash/bench",
- "version": "0.0.5-rc.2",
+ "version": "0.0.5-rc.3",
"private": true,
"description": "Performance / index-engagement benchmarks for stack integrations (Drizzle, encryptedSupabase, Prisma).",
"type": "module",
diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md
index cd5e4afe..c4798799 100644
--- a/packages/cli/CHANGELOG.md
+++ b/packages/cli/CHANGELOG.md
@@ -1,5 +1,209 @@
# @cipherstash/cli
+## 1.0.0-rc.3
+
+### Minor Changes
+
+- 0811330: Add `stash eql migration` — generate an EQL **v3** install migration for your ORM
+ instead of running the SQL directly against the database (`stash eql install`).
+ Migration-first is the preferred path: the install lands in your migration history
+ and ships to every environment through the ORM's own migrate step.
+
+ ```bash
+ stash eql migration --drizzle # Drizzle custom migration
+ stash eql migration --drizzle --supabase # also grants eql_v3 to anon/authenticated/service_role
+ ```
+
+ The migration carries the CLI's bundled v3 install SQL (one source of truth) plus
+ the `cs_migrations` tracking schema, so a single `drizzle-kit migrate` covers
+ everything `stash encrypt …` needs. `--supabase` appends the `eql_v3` +
+ `eql_v3_internal` role grants for PostgREST/RLS access.
+
+ `--prisma` is registered but not available yet — the Prisma Next migration
+ emitter is a follow-up (tracked in cipherstash/stack#690) that will let
+ prisma-next drop its baked install baseline. It fails with a pointer for now.
+
+- 3a86939: EQL v3 support for the encryption rollout lifecycle (#648). The `stash
+encrypt *` commands (and `@cipherstash/migrate` underneath) now resolve a
+ column's EQL version and its encrypted counterpart from the **Postgres domain
+ types** — the EQL v3 types are self-describing, so the `
_encrypted`
+ naming is a convention only, never enforced or relied upon — and follow the
+ right lifecycle, no new flags:
+
+ - **`encrypt backfill`** works on v3 columns unchanged (the engine was always
+ version-agnostic; pass an `EncryptionV3` client and real v3 envelopes land
+ in the concrete `eql_v3_*` domain column — verified live against a real
+ database, including the domain CHECK and a decrypt round-trip). The
+ manifest records the detected version, the encrypted column's name, and the
+ v3 target phase, and the command prints v3-appropriate next steps.
+ - **`encrypt cutover`** on a backfilled v3 column reports "not applicable"
+ (exit 0) with guidance: v3 has no rename cut-over — the application
+ switches to the encrypted column by name. Before backfill completes it
+ exits 1 and says to finish the backfill instead of instructing the switch.
+ On a database with no `eql_v2_configuration` table (a v3-only install) the
+ v2 path now explains that instead of surfacing a raw Postgres error.
+ - **`encrypt drop`** is version-aware: v3 runs from the `backfilled` phase,
+ **verifies live coverage** (refuses to generate the migration while any row
+ still has the plaintext set and the encrypted column NULL — the
+ `countUnencrypted` check), and drops the ORIGINAL plaintext column (there
+ is no `_plaintext` under v3); v2 behaviour is unchanged. The generated
+ v3 migration **re-verifies coverage at apply time** — it locks the table,
+ re-counts, and aborts without dropping if plaintext-only rows appeared
+ after generation. And because dropping is the one irreversible step, it
+ requires a positively asserted plaintext↔ciphertext pairing (the
+ manifest's recorded `encryptedColumn` or the naming convention): a match
+ found only by being the table's sole EQL column is refused with
+ instructions, and an ambiguous table (several EQL columns, none
+ identifiable) fails closed listing the candidates — as does `cutover`.
+ - **`encrypt status`** classifies each column from the observed domain type
+ (manifest as fallback), shows `v3` in the EQL column, and no longer raises
+ the v2-only `not-registered` / `plaintext-col-missing` drift flags for v3
+ columns. `stash status`'s quest ladder and the `stash init` agent handoff
+ prompt teach the version-appropriate next step (no more "run cutover" on
+ v3 columns).
+ - New `@cipherstash/migrate` exports: `classifyEqlDomain`,
+ `resolveEncryptedColumn`, `pickEncryptedColumn`, `listEncryptedColumns`
+ (domain-type resolution — case-exact for quoted/mixed-case table names),
+ `countEncrypted` / `countUnencrypted` (coverage counts), and manifest
+ `eqlVersion` + `encryptedColumn` fields. `EqlVersion` is numeric (`2 | 3`),
+ matching the manifest and the installer. Resolved columns carry `via:
+'hint' | 'convention' | 'sole'` so callers can tell a positively asserted
+ pairing from a by-elimination guess.
+ - Fixed: `encrypt cutover`/`encrypt drop` precondition failures now actually
+ exit 1 — the early-return guards previously skipped the exit-code path
+ entirely, so failed preconditions exited 0. (This also applies to v2
+ preconditions: scripted pipelines that relied on the erroneous exit 0 will
+ now see the documented exit 1.)
+
+ The `stash-cli` and `stash-encryption` skills and the `@cipherstash/migrate`
+ README document the two lifecycles (v2: backfill → cutover → drop;
+ v3: backfill → switch-by-name → drop).
+
+- b0634df: `stash plan --complete-rollout` is now automatable and has an honest exit code.
+ It skips the production-deploy gate, so it needs explicit consent — previously
+ that was an interactive prompt with no bypass, so a non-interactive run
+ auto-cancelled (default-no) and exited **0** without drafting a plan, leaving
+ automation to assume a plan existed.
+
+ - New `--yes` flag confirms the gate-skip without a prompt (for CI/agents).
+ - Without `--yes`, a non-interactive `--complete-rollout` run now **refuses
+ with a non-zero exit** and points at `--yes`, instead of silently succeeding.
+ - Interactive behaviour is unchanged (default-no confirm).
+
+- f188c7a: `stash env` now works: it mints deployment credentials from your device-code
+ session and prints them as env vars — no dashboard copy-paste. The command
+ creates a fresh ZeroKMS client and a member-role CipherStash access key (named
+ via `--name`; the role is pinned in the request and verified on the response —
+ the CLI deliberately cannot mint admin keys), then emits `CS_WORKSPACE_CRN`,
+ `CS_CLIENT_ID`, `CS_CLIENT_KEY`, and `CS_CLIENT_ACCESS_KEY`.
+
+ Output goes to stdout by default — and stdout is pipe-clean (progress UI is on
+ stderr), so `stash env --name x > prod.env` and pipes into secret stores are
+ safe. `--write [path]` writes a file instead (default `.env.production.local`,
+ enforced mode 0600 even when overwriting), confirming before overwriting and
+ refusing non-interactively — always _before_ anything is minted, so a refusal
+ never discards the shown-exactly-once access key. `--json` emits NDJSON; with
+ `--write` the confirmation event is deliberately secret-free. API responses
+ are schema-validated so a service change can never print `undefined` into a
+ credentials file. Creating access keys requires the admin role in the
+ workspace.
+
+ This is also the supported credential path for WASM/edge local development
+ (Supabase Edge Functions, Cloudflare Workers, Deno), where the runtime cannot
+ read the `~/.cipherstash` device profile: mint a key and feed it via
+ `supabase functions serve --env-file` or the platform's secret store.
+
+ The `STASH_EXPERIMENTAL_ENV_CMD` gate is removed.
+
+- 8872d1e: `stash init`, `stash plan`, and `stash impl` no longer crash on a Prisma Next
+ project. `SKILL_MAP` was missing a `prisma-next` entry, so the skills-install
+ and AGENTS.md-builder steps hit `SKILL_MAP[integration]` → `undefined` and threw
+ "not iterable" for any repo the CLI detected as Prisma Next. The entry is added
+ and both consumers now resolve skills through a `skillsFor()` helper that
+ degrades an unmapped integration to the base skill set instead of crashing
+ (`tsup` ships without type-checking, so the `Record` type alone
+ didn't protect the build).
+
+ Ships a new **`stash-prisma-next`** agent skill documenting the EQL v3 Prisma
+ Next surface — the domain-named encrypted column types (`EncryptedTextSearch`,
+ `EncryptedDoubleOrd`, …), `cipherstashFromStackV3` wiring, the runtime value
+ envelopes, the `eql*` query operators, and EQL installation via
+ `prisma-next migration apply`. It is installed for Prisma Next projects and
+ inlined into `AGENTS.md` for editor agents.
+
+ `stash eql install` now refuses to run in a Prisma Next project (pointing you
+ at `prisma-next migration apply`, which owns EQL installation) unless you pass
+ `--force` — closing the manual-invocation hole that `stash init --prisma-next`
+ already avoided.
+
+### Patch Changes
+
+- 8b2551a: Fix "Failed to load native binding" on project-local installs of the CLI/SDK
+ (npm). `@cipherstash/auth` was pinned at 0.41.0 while the six
+ `@cipherstash/auth-*` platform bindings declared in stack/stash/wizard's
+ optionalDependencies were pinned at 0.42.0. Because auth pins its bindings as
+ exact-version optional peer dependencies, the skew made npm nest per-consumer
+ binding copies that the hoisted `auth` package could not resolve — any command
+ or import touching auth then died at startup. All seven packages now move in
+ lockstep at 0.42.0, Dependabot is barred from bumping any of them
+ independently, and a supply-chain CI test fails on any future skew.
+- b8cb599: Fix invalid DDL from `drizzle-kit generate`/`push` for EQL v3 encrypted columns.
+ A v3 column declared its SQL type as the schema-qualified domain
+ (`public.eql_v3_text_search`), but drizzle-kit wraps a custom type's whole name
+ in a single pair of double quotes — emitting `"public.eql_v3_text_search"`, which
+ Postgres reads as one dotted identifier and rejects with `type
+"public.eql_v3_text_search" does not exist`. Generated migrations had to be
+ hand-repaired.
+
+ The v3 column now emits the **unqualified** domain (`eql_v3_text_search`), which
+ drizzle-kit renders as the valid `"eql_v3_text_search"` and which resolves via the
+ search path (the domains live in `public`). This matches how the v2
+ `encryptedType` surface already declares its type, and how drizzle-kit reads the
+ type back during a `push` introspection diff, so the two sides no longer disagree.
+ Builder recovery still yields the canonical `public.eql_v3_*` identity, so
+ operators and schema extraction are unchanged.
+
+ The bundled `stash-drizzle` skill is updated to describe the unqualified generated
+ type and the search-path requirement (hence the `stash` bump — the skill ships in
+ its tarball).
+
+- 175eeb7: The EQL **v3** install SQL is now read from the `@cipherstash/eql` package at
+ runtime instead of a copy vendored into this repo. `@cipherstash/eql` becomes a
+ runtime dependency of `stash`, and a version bump now flows straight through — no
+ re-vendor step, no drift between the pin and the shipped bundle.
+
+ This removes ~44k lines of generated plpgsql from the repository (which had made
+ GitHub classify the whole repo as plpgsql — CIP-3518) along with the
+ `gen:eql-v3-sql` vendor script and its CI drift-check.
+
+ No behaviour change: v3 installs the same one-artifact bundle (which self-adapts to
+ non-superuser environments like Supabase), and the v2 path is unchanged.
+
+- 4923c0a: **Breaking (v3 authoring surface):** the EQL v3 PSL column constructors drop
+ the `Encrypted` prefix to line up with the stack / Drizzle `types.*` catalog —
+ the `cipherstash.` namespace already disambiguates. So
+ `cipherstash.EncryptedTextSearch()` → `cipherstash.TextSearch()`,
+ `cipherstash.EncryptedDoubleOrd()` → `cipherstash.DoubleOrd()`,
+ `cipherstash.EncryptedBoolean()` → `cipherstash.Boolean()`, etc.
+
+ The v3 one-call setup function is renamed `cipherstashFromStackV3` →
+ `cipherstashFromStack` (v3 is the default), and the existing v2 setup function
+ becomes `cipherstashFromStackV2`.
+
+ The camelCase TS-authoring factory exports move in lockstep:
+ `encryptedTextSearch` → `textSearch`, `encryptedDoubleOrd` → `doubleOrd`, etc.
+ (a property test enforces the PSL and TS names agree modulo first-letter case).
+
+ Unchanged: the runtime value envelopes (`EncryptedString`, `EncryptedNumber`,
+ `EncryptedBoolean`, …), the `cipherstash.*V2` legacy column constructors, the
+ generated `contract.json` / codec ids, and the `eql*` query operators.
+
+ The `stash-prisma-next` skill is updated to the new names (skills ship in the
+ `stash` tarball).
+
+- Updated dependencies [3a86939]
+ - @cipherstash/migrate@1.0.0-rc.1
+
## 1.0.0-rc.2
### Patch Changes
diff --git a/packages/cli/package.json b/packages/cli/package.json
index bc2eaf6e..ecb82e00 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,7 +1,7 @@
{
"name": "stash",
- "version": "1.0.0-rc.2",
- "description": "CipherStash CLI \u2014 the one stash command for auth, init, encryption schema, database setup, and secrets.",
+ "version": "1.0.0-rc.3",
+ "description": "CipherStash CLI — the one stash command for auth, init, encryption schema, database setup, and secrets.",
"repository": {
"type": "git",
"url": "git+https://github.com/cipherstash/stack.git",
@@ -56,7 +56,7 @@
"posthog-node": "^5.40.0",
"zod": "^3.25.76"
},
- "//optionalDependencies": "@cipherstash/auth ships per-platform native bindings as optional peerDependencies. pnpm does not auto-install platform-matched optional peer deps, so we declare them here as optionalDependencies \u2014 pnpm then picks the binary matching the host's os/cpu (from each sub-package's own package.json) and ignores the rest. All seven names share a single catalog entry to keep them in lockstep.",
+ "//optionalDependencies": "@cipherstash/auth ships per-platform native bindings as optional peerDependencies. pnpm does not auto-install platform-matched optional peer deps, so we declare them here as optionalDependencies — pnpm then picks the binary matching the host's os/cpu (from each sub-package's own package.json) and ignores the rest. All seven names share a single catalog entry to keep them in lockstep.",
"optionalDependencies": {
"@cipherstash/auth-darwin-arm64": "catalog:repo",
"@cipherstash/auth-darwin-x64": "catalog:repo",
diff --git a/packages/migrate/CHANGELOG.md b/packages/migrate/CHANGELOG.md
index 8b991d51..ebcfb9bd 100644
--- a/packages/migrate/CHANGELOG.md
+++ b/packages/migrate/CHANGELOG.md
@@ -1,5 +1,65 @@
# @cipherstash/migrate
+## 1.0.0-rc.1
+
+### Minor Changes
+
+- 3a86939: EQL v3 support for the encryption rollout lifecycle (#648). The `stash
+encrypt *` commands (and `@cipherstash/migrate` underneath) now resolve a
+ column's EQL version and its encrypted counterpart from the **Postgres domain
+ types** — the EQL v3 types are self-describing, so the `_encrypted`
+ naming is a convention only, never enforced or relied upon — and follow the
+ right lifecycle, no new flags:
+
+ - **`encrypt backfill`** works on v3 columns unchanged (the engine was always
+ version-agnostic; pass an `EncryptionV3` client and real v3 envelopes land
+ in the concrete `eql_v3_*` domain column — verified live against a real
+ database, including the domain CHECK and a decrypt round-trip). The
+ manifest records the detected version, the encrypted column's name, and the
+ v3 target phase, and the command prints v3-appropriate next steps.
+ - **`encrypt cutover`** on a backfilled v3 column reports "not applicable"
+ (exit 0) with guidance: v3 has no rename cut-over — the application
+ switches to the encrypted column by name. Before backfill completes it
+ exits 1 and says to finish the backfill instead of instructing the switch.
+ On a database with no `eql_v2_configuration` table (a v3-only install) the
+ v2 path now explains that instead of surfacing a raw Postgres error.
+ - **`encrypt drop`** is version-aware: v3 runs from the `backfilled` phase,
+ **verifies live coverage** (refuses to generate the migration while any row
+ still has the plaintext set and the encrypted column NULL — the
+ `countUnencrypted` check), and drops the ORIGINAL plaintext column (there
+ is no `_plaintext` under v3); v2 behaviour is unchanged. The generated
+ v3 migration **re-verifies coverage at apply time** — it locks the table,
+ re-counts, and aborts without dropping if plaintext-only rows appeared
+ after generation. And because dropping is the one irreversible step, it
+ requires a positively asserted plaintext↔ciphertext pairing (the
+ manifest's recorded `encryptedColumn` or the naming convention): a match
+ found only by being the table's sole EQL column is refused with
+ instructions, and an ambiguous table (several EQL columns, none
+ identifiable) fails closed listing the candidates — as does `cutover`.
+ - **`encrypt status`** classifies each column from the observed domain type
+ (manifest as fallback), shows `v3` in the EQL column, and no longer raises
+ the v2-only `not-registered` / `plaintext-col-missing` drift flags for v3
+ columns. `stash status`'s quest ladder and the `stash init` agent handoff
+ prompt teach the version-appropriate next step (no more "run cutover" on
+ v3 columns).
+ - New `@cipherstash/migrate` exports: `classifyEqlDomain`,
+ `resolveEncryptedColumn`, `pickEncryptedColumn`, `listEncryptedColumns`
+ (domain-type resolution — case-exact for quoted/mixed-case table names),
+ `countEncrypted` / `countUnencrypted` (coverage counts), and manifest
+ `eqlVersion` + `encryptedColumn` fields. `EqlVersion` is numeric (`2 | 3`),
+ matching the manifest and the installer. Resolved columns carry `via:
+'hint' | 'convention' | 'sole'` so callers can tell a positively asserted
+ pairing from a by-elimination guess.
+ - Fixed: `encrypt cutover`/`encrypt drop` precondition failures now actually
+ exit 1 — the early-return guards previously skipped the exit-code path
+ entirely, so failed preconditions exited 0. (This also applies to v2
+ preconditions: scripted pipelines that relied on the erroneous exit 0 will
+ now see the documented exit 1.)
+
+ The `stash-cli` and `stash-encryption` skills and the `@cipherstash/migrate`
+ README document the two lifecycles (v2: backfill → cutover → drop;
+ v3: backfill → switch-by-name → drop).
+
## 1.0.0-rc.0
### Patch Changes
diff --git a/packages/migrate/package.json b/packages/migrate/package.json
index a302385c..b14343ca 100644
--- a/packages/migrate/package.json
+++ b/packages/migrate/package.json
@@ -1,6 +1,6 @@
{
"name": "@cipherstash/migrate",
- "version": "1.0.0-rc.0",
+ "version": "1.0.0-rc.1",
"description": "Plaintext-to-encrypted column migration for CipherStash: resumable backfill, per-column state, and EQL lifecycle orchestration.",
"repository": {
"type": "git",
diff --git a/packages/prisma-next/CHANGELOG.md b/packages/prisma-next/CHANGELOG.md
index 3f1a33d0..53885995 100644
--- a/packages/prisma-next/CHANGELOG.md
+++ b/packages/prisma-next/CHANGELOG.md
@@ -1,5 +1,56 @@
# @cipherstash/prisma-next
+## 1.0.0-rc.3
+
+### Minor Changes
+
+- a75513b: **Breaking:** EQL v3 columns are now authored through **concrete per-domain constructors** — the constructor you choose _is_ the capability set. The legacy boolean-option surface (`EncryptedString({ equality, freeTextSearch, orderAndRange })`) is not carried into v3.
+
+ - New per-domain constructors, one per exposed `public.eql_v3_*` domain:
+ - Text: `EncryptedText` (storage), `EncryptedTextEq`, `EncryptedTextOrd` (eq + order/range), `EncryptedTextMatch` (free-text), `EncryptedTextSearch` (eq + free-text + order/range).
+ - Scalars (Integer, Smallint, BigInt, Numeric, Real, Double, Date, Timestamp): `Encrypted` (storage), `EncryptedEq`, `EncryptedOrd`.
+ - `EncryptedBoolean` — storage-only (`public.eql_v3_boolean`); there is no boolean equality constructor.
+ - `EncryptedJson` — searchable encrypted JSONB (`public.eql_v3_json`, `ste_vec`), queried with `eqlJsonContains` (`@>` containment). Selector querying (comparing the value at a JSONPath) is tracked in #677.
+ - **Impossible capability combinations have no constructor** (e.g. text equality + free-text without order/range) — they are unrepresentable, not runtime errors.
+ - **BigInt is a first-class v3 family** (`EncryptedBigInt` / `EncryptedBigIntEq` / `EncryptedBigIntOrd`, JS `bigint` plaintext, backed by `public.eql_v3_bigint*`).
+ - Use the `*V2` constructors (`EncryptedStringV2`, `EncryptedDoubleV2`, `EncryptedBigIntV2`, `EncryptedDateV2`, `EncryptedBooleanV2`, `EncryptedJsonV2`) to keep EQL v2 columns. A client is v2 or v3 — the two runtime descriptors are never co-registered.
+ - New `@cipherstash/prisma-next/v3` entry point: `cipherstashFromStackV3({ contractJson })` builds the v3 runtime descriptor, bulk-encrypt middleware, and a stack `EncryptionV3` client from the emitted contract.
+ - Query operators use an **EQL-derived vocabulary** (`eqlEq`, `eqlNeq`, `eqlIn`, `eqlNotIn`, `eqlGt`, `eqlGte`, `eqlLt`, `eqlLte`, `eqlBetween`, `eqlNotBetween`, `eqlJsonContains`; ordering via `eqlAsc` / `eqlDesc`), lowering to the same-named `eql_v3.*` functions with operands cast to the domain's query type (`$n::eql_v3.query_`); ordering uses `eql_v3.ord_term` / `eql_v3.ord_term_ore` by the column's ordering flavour. The domains are `public.eql_v3_*`; the operator functions live in the `eql_v3` schema. (The v2 surface keeps its `cipherstash*` names.)
+ - Free-text search is **`eqlMatch`** — fuzzy bloom token matching (`eql_v3.contains`), deliberately NOT named after SQL `ILIKE`: matching is case-insensitive, order/multiplicity-insensitive, and one-sided (may false-positive). Two guards run before encryption: SQL wildcards are normalised (leading/trailing `%` stripped; interior `%` or any `_` rejected), and needles the column's match index cannot answer (empty / below the tokenizer length) are rejected via the shared `matchNeedleError` guard. There is **no negated match operator** — negating a may-false-positive bloom test would silently drop matching rows.
+ - A new baseline migration `20260601T0100_install_eql_v3_bundle` (invariant `cipherstash:install-eql-v3-bundle-v1`) installs the `public.eql_v3_*` domains and `eql_v3.*` functions from the pinned `@cipherstash/eql` release. Regenerate contracts and run migrations after changing constructors.
+ - **The v3 ORM surface is fully wired end-to-end** (proven by converting `examples/prisma`):
+ - The generated `contract.d.ts` type surface covers every v3 codec id: `CodecTypes` gains all 40 `cipherstash/eql-v3/*@1` entries (envelope outputs — `number`-castAs domains decode to the new `EncryptedNumber` — plus trait-accurate operator visibility), and `QueryOperationTypes` gains the `eql*` operator set, surfaced on v3 columns via type-level `cipherstash:v3-*` marker traits (the v2 `cipherstash*` methods never appear on v3 columns, and vice-versa). Storage-only domains (including `EncryptedBoolean`) surface no operator methods at the type level, matching the runtime gate.
+ - The v3 runtime descriptor now presents the **pack id** (`cipherstash`) with v3's own version, so `postgres({ extensions })` accepts contracts emitted by the cipherstash extension pack instead of failing with `RUNTIME.MISSING_EXTENSION_PACK`.
+ - Every v3 codec id registers a control-plane `expandNativeType` hook that strips the `public.` qualifier — `prisma-next migration plan` now renders `CREATE TABLE` columns as bare domain names (`eql_v3_bigint_ord`), matching what introspection reports, with **no `add_search_config` ops** (v3 domains carry their own index metadata). No `onFieldEvent` is registered for v3.
+ - The v3 bundle baseline migration op is reclassified `data` (it is a contract-shape-neutral self-edge; the aggregate integrity checker rejects self-edges without a data-class op), unblocking `prisma-next migration plan` / `migrate` in consuming apps.
+
+- 4923c0a: **Breaking (v3 authoring surface):** the EQL v3 PSL column constructors drop
+ the `Encrypted` prefix to line up with the stack / Drizzle `types.*` catalog —
+ the `cipherstash.` namespace already disambiguates. So
+ `cipherstash.EncryptedTextSearch()` → `cipherstash.TextSearch()`,
+ `cipherstash.EncryptedDoubleOrd()` → `cipherstash.DoubleOrd()`,
+ `cipherstash.EncryptedBoolean()` → `cipherstash.Boolean()`, etc.
+
+ The v3 one-call setup function is renamed `cipherstashFromStackV3` →
+ `cipherstashFromStack` (v3 is the default), and the existing v2 setup function
+ becomes `cipherstashFromStackV2`.
+
+ The camelCase TS-authoring factory exports move in lockstep:
+ `encryptedTextSearch` → `textSearch`, `encryptedDoubleOrd` → `doubleOrd`, etc.
+ (a property test enforces the PSL and TS names agree modulo first-letter case).
+
+ Unchanged: the runtime value envelopes (`EncryptedString`, `EncryptedNumber`,
+ `EncryptedBoolean`, …), the `cipherstash.*V2` legacy column constructors, the
+ generated `contract.json` / codec ids, and the `eql*` query operators.
+
+ The `stash-prisma-next` skill is updated to the new names (skills ship in the
+ `stash` tarball).
+
+### Patch Changes
+
+- Updated dependencies [8b2551a]
+ - @cipherstash/stack@1.0.0-rc.3
+
## 1.0.0-rc.2
### Patch Changes
diff --git a/packages/prisma-next/package.json b/packages/prisma-next/package.json
index bd5b6e5e..1fd9be97 100644
--- a/packages/prisma-next/package.json
+++ b/packages/prisma-next/package.json
@@ -1,6 +1,6 @@
{
"name": "@cipherstash/prisma-next",
- "version": "1.0.0-rc.2",
+ "version": "1.0.0-rc.3",
"license": "MIT",
"author": "CipherStash ",
"description": "CipherStash extension for Prisma Next: searchable application-layer field-level encryption for Postgres, with six encrypted column types, 17 query operators, bulk encrypt/decrypt middleware, and a baseline migration that installs the vendored EQL bundle SQL byte-for-byte.",
diff --git a/packages/stack-drizzle/CHANGELOG.md b/packages/stack-drizzle/CHANGELOG.md
index 493aca61..46e49bba 100644
--- a/packages/stack-drizzle/CHANGELOG.md
+++ b/packages/stack-drizzle/CHANGELOG.md
@@ -1,5 +1,32 @@
# @cipherstash/stack-drizzle
+## 1.0.0-rc.3
+
+### Patch Changes
+
+- b8cb599: Fix invalid DDL from `drizzle-kit generate`/`push` for EQL v3 encrypted columns.
+ A v3 column declared its SQL type as the schema-qualified domain
+ (`public.eql_v3_text_search`), but drizzle-kit wraps a custom type's whole name
+ in a single pair of double quotes — emitting `"public.eql_v3_text_search"`, which
+ Postgres reads as one dotted identifier and rejects with `type
+"public.eql_v3_text_search" does not exist`. Generated migrations had to be
+ hand-repaired.
+
+ The v3 column now emits the **unqualified** domain (`eql_v3_text_search`), which
+ drizzle-kit renders as the valid `"eql_v3_text_search"` and which resolves via the
+ search path (the domains live in `public`). This matches how the v2
+ `encryptedType` surface already declares its type, and how drizzle-kit reads the
+ type back during a `push` introspection diff, so the two sides no longer disagree.
+ Builder recovery still yields the canonical `public.eql_v3_*` identity, so
+ operators and schema extraction are unchanged.
+
+ The bundled `stash-drizzle` skill is updated to describe the unqualified generated
+ type and the search-path requirement (hence the `stash` bump — the skill ships in
+ its tarball).
+
+- Updated dependencies [8b2551a]
+ - @cipherstash/stack@1.0.0-rc.3
+
## 1.0.0-rc.2
### Patch Changes
diff --git a/packages/stack-drizzle/package.json b/packages/stack-drizzle/package.json
index 1c00bc76..b0ca4bc5 100644
--- a/packages/stack-drizzle/package.json
+++ b/packages/stack-drizzle/package.json
@@ -1,6 +1,6 @@
{
"name": "@cipherstash/stack-drizzle",
- "version": "1.0.0-rc.2",
+ "version": "1.0.0-rc.3",
"description": "CipherStash Stack Drizzle ORM integration: searchable, application-layer field-level encryption for PostgreSQL.",
"keywords": [
"encrypted",
diff --git a/packages/stack-supabase/CHANGELOG.md b/packages/stack-supabase/CHANGELOG.md
index b9d90696..87121f00 100644
--- a/packages/stack-supabase/CHANGELOG.md
+++ b/packages/stack-supabase/CHANGELOG.md
@@ -1,5 +1,12 @@
# @cipherstash/stack-supabase
+## 1.0.0-rc.3
+
+### Patch Changes
+
+- Updated dependencies [8b2551a]
+ - @cipherstash/stack@1.0.0-rc.3
+
## 1.0.0-rc.2
### Patch Changes
diff --git a/packages/stack-supabase/package.json b/packages/stack-supabase/package.json
index af9872c4..f2e2d589 100644
--- a/packages/stack-supabase/package.json
+++ b/packages/stack-supabase/package.json
@@ -1,6 +1,6 @@
{
"name": "@cipherstash/stack-supabase",
- "version": "1.0.0-rc.2",
+ "version": "1.0.0-rc.3",
"description": "CipherStash Stack Supabase integration: transparent, searchable field-level encryption for Supabase.",
"keywords": [
"encrypted",
diff --git a/packages/stack/CHANGELOG.md b/packages/stack/CHANGELOG.md
index cafcead2..b5cac25f 100644
--- a/packages/stack/CHANGELOG.md
+++ b/packages/stack/CHANGELOG.md
@@ -1,5 +1,19 @@
# @cipherstash/stack
+## 1.0.0-rc.3
+
+### Patch Changes
+
+- 8b2551a: Fix "Failed to load native binding" on project-local installs of the CLI/SDK
+ (npm). `@cipherstash/auth` was pinned at 0.41.0 while the six
+ `@cipherstash/auth-*` platform bindings declared in stack/stash/wizard's
+ optionalDependencies were pinned at 0.42.0. Because auth pins its bindings as
+ exact-version optional peer dependencies, the skew made npm nest per-consumer
+ binding copies that the hoisted `auth` package could not resolve — any command
+ or import touching auth then died at startup. All seven packages now move in
+ lockstep at 0.42.0, Dependabot is barred from bumping any of them
+ independently, and a supply-chain CI test fails on any future skew.
+
## 1.0.0-rc.2
### Minor Changes
diff --git a/packages/stack/package.json b/packages/stack/package.json
index d10cf562..db9a5ffc 100644
--- a/packages/stack/package.json
+++ b/packages/stack/package.json
@@ -1,6 +1,6 @@
{
"name": "@cipherstash/stack",
- "version": "1.0.0-rc.2",
+ "version": "1.0.0-rc.3",
"description": "CipherStash Stack for TypeScript and JavaScript",
"keywords": [
"encrypted",
diff --git a/packages/test-kit/CHANGELOG.md b/packages/test-kit/CHANGELOG.md
index a3863335..afdb8935 100644
--- a/packages/test-kit/CHANGELOG.md
+++ b/packages/test-kit/CHANGELOG.md
@@ -1,5 +1,12 @@
# @cipherstash/test-kit
+## 0.0.1-rc.3
+
+### Patch Changes
+
+- Updated dependencies [8b2551a]
+ - @cipherstash/stack@1.0.0-rc.3
+
## 0.0.1-rc.2
### Patch Changes
diff --git a/packages/test-kit/package.json b/packages/test-kit/package.json
index c0fda059..4679f5b6 100644
--- a/packages/test-kit/package.json
+++ b/packages/test-kit/package.json
@@ -1,6 +1,6 @@
{
"name": "@cipherstash/test-kit",
- "version": "0.0.1-rc.2",
+ "version": "0.0.1-rc.3",
"private": true,
"description": "Shared EQL v3 test harness: the domain catalog, the plaintext oracle, and the integration-suite driver. Consumed as TypeScript source — no build step.",
"type": "module",
diff --git a/packages/wizard/CHANGELOG.md b/packages/wizard/CHANGELOG.md
index e1d7dc28..9fa7bc2d 100644
--- a/packages/wizard/CHANGELOG.md
+++ b/packages/wizard/CHANGELOG.md
@@ -1,5 +1,19 @@
# @cipherstash/wizard
+## 1.0.0-rc.3
+
+### Patch Changes
+
+- 8b2551a: Fix "Failed to load native binding" on project-local installs of the CLI/SDK
+ (npm). `@cipherstash/auth` was pinned at 0.41.0 while the six
+ `@cipherstash/auth-*` platform bindings declared in stack/stash/wizard's
+ optionalDependencies were pinned at 0.42.0. Because auth pins its bindings as
+ exact-version optional peer dependencies, the skew made npm nest per-consumer
+ binding copies that the hoisted `auth` package could not resolve — any command
+ or import touching auth then died at startup. All seven packages now move in
+ lockstep at 0.42.0, Dependabot is barred from bumping any of them
+ independently, and a supply-chain CI test fails on any future skew.
+
## 1.0.0-rc.2
### Patch Changes
diff --git a/packages/wizard/package.json b/packages/wizard/package.json
index a44cd945..cbeadfd6 100644
--- a/packages/wizard/package.json
+++ b/packages/wizard/package.json
@@ -1,6 +1,6 @@
{
"name": "@cipherstash/wizard",
- "version": "1.0.0-rc.2",
+ "version": "1.0.0-rc.3",
"description": "AI-powered encryption setup for CipherStash. Reads your codebase, picks columns to encrypt, and wires everything up.",
"repository": {
"type": "git",