Version Packages (rc)#729
Merged
Merged
Conversation
github-actions
Bot
force-pushed
the
changeset-release/main
branch
8 times, most recently
from
July 21, 2026 09:32
f1e0d0b to
7c322ee
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
from
July 21, 2026 12:07
7c322ee to
4bb5120
Compare
coderdan
approved these changes
Jul 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
mainis currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, runchangeset pre exitonmain.Releases
@cipherstash/prisma-next@1.0.0-rc.4
Minor Changes
2e6f032: Make
@cipherstash/prisma-nextEQL v3 only. The EQL v2 surface is removedentirely — install path, authoring constructors, runtime codecs, and the v2
subpath exports.
Why: the v2 and v3 baselines were chained — the v3 migration edge started
from the v2 baseline's
tostate and the head ref required both invariants — sothe only path to head ran the v2 install first. The v2 bundle's install fails on
managed Postgres (e.g. Supabase) where the connecting role is not a
superuser, which made the adapter unusable there even for v3-only apps.
Installing only EQL v3 (which applies fine as a non-superuser) fixes this.
Breaking — install path: the EQL v2 baseline migration
(
20260601T0000_install_eql_bundle) is removed, and the contract now models nostorage (the retired
eql_v2_configurationtable is gone). The v3 baseline(
20260601T0100_install_eql_v3_bundle) is re-rooted as the sole invariant-onlygenesis edge (
from: null); the head ref requires onlycipherstash:install-eql-v3-bundle-v1.prisma-next migration applynowinstalls EQL v3 exclusively and works on Supabase as a non-superuser.
Breaking — API: the EQL v2 authoring/runtime surface is removed:
cipherstashFromStackV2,deriveStackSchemas, andcreateCipherstashSdk(from
./stack) — usecipherstashFromStack(v3).encrypted*V2TS column factories and thecipherstash.Encrypted*V2PSLconstructors (from
./column-types) — use the v3 domain factories/constructors(
text/textSearch/bigIntOrd/… andcipherstash.TextSearch()etc.).createCipherstashRuntimeDescriptor, thecipherstash*query operators/helpers, and the
EncryptedDoubleenvelope (from./runtime)— use the v3 runtime (
createCipherstashV3RuntimeDescriptor,bulkEncryptMiddlewareV3, theeql*operators,EncryptedNumber). Theversion-neutral envelopes (
EncryptedString/BigInt/Boolean/Date/Json)and
decryptAllare unchanged../middlewareand./migrationsubpath exports are removed (the v2bulk-encrypt middleware and call-classes). Use
bulkEncryptMiddlewareV3from./runtime/./v3.Apps still on the v2 surface must move to the v3 constructors and regenerate
their contract (
prisma-next contract emit); there is no supported EQL v2 pathin this package anymore.
Also: the "bulk-encrypt middleware not wired" diagnostic is now raised on the
v3 write path. Encoding an unencrypted value with an SDK that has no
bulkEncryptMiddlewareV3(sdk)registered against it fails fast withRUNTIME.ENCODE_FAILEDand a copy-pasteable wiring snippet, instead of surfacingas an opaque pg-level serialise error. (The guard existed on the v2 codec; the v3
codec had never wired it up.)
cf2c57c: Upgrade Stack to
@cipherstash/protect-ffi0.30 and EQL 3.0.2.Prisma Next includes a versioned EQL 3.0.2 upgrade migration, so databases
that have already recorded the original EQL v3 baseline still install the new
domains and functions.
Encrypted JSON now uses the
public.eql_v3_json_searchstorage domain andeql_v3.query_jsonquery domain. Drizzle selector equality uses exact,GIN-indexable value-selector containment, while selector range comparisons use
a ciphertext-free path selector plus string/number query term. Prisma Next gains
the equivalent
eqlJsonPathEq,eqlJsonPathNeq,eqlJsonPathGt,eqlJsonPathGte,eqlJsonPathLt, andeqlJsonPathLteoperators. SelectorSelector-based
ORDER BYis available asops.selector(column, path).asc()/desc()in Drizzleand
eqlJsonPathAsc(column, path)/eqlJsonPathDesc(column, path)in PrismaNext; both lower to
ORDER BY eql_v3.ord_termover the selected entry.If you call
encryptQuerywith an explicitqueryType, note thatsteVecTermnow produces a scalar JSON ordering term. It no longer meansstructural containment; use the recommended
searchableJsonquery type withan object or array for containment, or
steVecValueSelectorwith{ path, value }for exact equality at a path.The FFI now rejects free-text needles shorter than the configured n-gram size
at the core query-encryption boundary, including callers that bypass adapter
guards.
This EQL release changes the SteVec storage format. Existing EQL v3 encrypted
JSON rows must be re-encrypted before they can be queried with the new domain.
Legacy EQL v2
searchableJson()schemas are rejected during client setupbecause the old selector envelope can no longer be emitted; migrate them to the
v3
types.Jsondomain.EQL 3.0.2 requires typed query-domain operands for encrypted free-text and JSON
operators. PostgREST cannot express those casts, so Supabase v3 fails fast for
matches(), encryptedcontains(), andselectorEq()/selectorNe()insteadof placing a decryptable storage envelope in a GET query string that the new
SQL surface will reject. Use the Drizzle or Prisma Next adapter, or a carefully
scoped direct SQL/RPC path.
Patch Changes
@cipherstash/stack@1.0.0-rc.4
Minor Changes
cf2c57c: Upgrade Stack to
@cipherstash/protect-ffi0.30 and EQL 3.0.2.Prisma Next includes a versioned EQL 3.0.2 upgrade migration, so databases
that have already recorded the original EQL v3 baseline still install the new
domains and functions.
Encrypted JSON now uses the
public.eql_v3_json_searchstorage domain andeql_v3.query_jsonquery domain. Drizzle selector equality uses exact,GIN-indexable value-selector containment, while selector range comparisons use
a ciphertext-free path selector plus string/number query term. Prisma Next gains
the equivalent
eqlJsonPathEq,eqlJsonPathNeq,eqlJsonPathGt,eqlJsonPathGte,eqlJsonPathLt, andeqlJsonPathLteoperators. SelectorSelector-based
ORDER BYis available asops.selector(column, path).asc()/desc()in Drizzleand
eqlJsonPathAsc(column, path)/eqlJsonPathDesc(column, path)in PrismaNext; both lower to
ORDER BY eql_v3.ord_termover the selected entry.If you call
encryptQuerywith an explicitqueryType, note thatsteVecTermnow produces a scalar JSON ordering term. It no longer meansstructural containment; use the recommended
searchableJsonquery type withan object or array for containment, or
steVecValueSelectorwith{ path, value }for exact equality at a path.The FFI now rejects free-text needles shorter than the configured n-gram size
at the core query-encryption boundary, including callers that bypass adapter
guards.
This EQL release changes the SteVec storage format. Existing EQL v3 encrypted
JSON rows must be re-encrypted before they can be queried with the new domain.
Legacy EQL v2
searchableJson()schemas are rejected during client setupbecause the old selector envelope can no longer be emitted; migrate them to the
v3
types.Jsondomain.EQL 3.0.2 requires typed query-domain operands for encrypted free-text and JSON
operators. PostgREST cannot express those casts, so Supabase v3 fails fast for
matches(), encryptedcontains(), andselectorEq()/selectorNe()insteadof placing a decryptable storage envelope in a GET query string that the new
SQL surface will reject. Use the Drizzle or Prisma Next adapter, or a carefully
scoped direct SQL/RPC path.
508f1d5: Breaking (
@cipherstash/stack/wasm-inline): every fallible method now returns aResult—{ data } | { failure }— instead of throwing. AndbulkEncrypt/bulkDecryptare added, so a list of encrypted rows costs one ZeroKMS round trip instead of one per row.Result alignment
encrypt,decrypt,encryptQueryandencryptQueryBulkpreviously threw on failure, and returned bare values on success. They now return{ data } | { failure }, withfailure.typedrawn fromEncryptionErrorTypes(EncryptionErrorfor encrypt-side operations,DecryptionErrorfor decrypt-side) andfailure.codecarrying the FFI error code where there is one.This is the contract the native entry has always honoured, and the one
AGENTS.mdstates outright: "Operations return{ data }or{ failure }. Preserve this shape and errortypevalues inEncryptionErrorTypes." The WASM entry never followed it. That was drift rather than a design decision — nothing about WASM prevents it (@byteslice/resultis already bundled intodist/wasm-inline.js), and it meant edge code had to be written in a different shape from every other surface, with failures that were easy to miss.Fixed now because it is a breaking change and 1.0.0 has not shipped:
@cipherstash/stack@latestis still0.19.0, so this surface has only ever been published under therctag. After GA it would have had to wait for a major.isEncryptedis unchanged — a pure predicate with nothing to fail at, exactly as on the native entry.Bulk operations
The WASM entry previously exposed no bulk operations at all, so rendering an N-row list on Deno, Cloudflare Workers, or Supabase Edge Functions meant N sequential ZeroKMS calls. Combined with the WASM cold start, that made list endpoints impractical on the edge.
Both are index-aligned with their input, and
null/undefinedentries yieldnullat the same index without reaching ZeroKMS (an all-null batch makes no call at all). Because each entry names its own table and column, a singlebulkEncryptcan cover several columns across many rows — which is what makes the saving real, since a single-column batch would still cost one round trip per column.bulkDecryptbuilds on the fallible FFI primitive, so when items fail thefailure.messagenames every failing index with its reason, rather than surfacing the first and discarding the rest.The model helpers (
encryptModel/decryptModeland their bulk forms) remain Node-only: the WASM entry has no single-model operation to build them on, so those need their own port.@cipherstash/stack-drizzle@1.0.0-rc.4
Minor Changes
cf2c57c: Upgrade Stack to
@cipherstash/protect-ffi0.30 and EQL 3.0.2.Prisma Next includes a versioned EQL 3.0.2 upgrade migration, so databases
that have already recorded the original EQL v3 baseline still install the new
domains and functions.
Encrypted JSON now uses the
public.eql_v3_json_searchstorage domain andeql_v3.query_jsonquery domain. Drizzle selector equality uses exact,GIN-indexable value-selector containment, while selector range comparisons use
a ciphertext-free path selector plus string/number query term. Prisma Next gains
the equivalent
eqlJsonPathEq,eqlJsonPathNeq,eqlJsonPathGt,eqlJsonPathGte,eqlJsonPathLt, andeqlJsonPathLteoperators. SelectorSelector-based
ORDER BYis available asops.selector(column, path).asc()/desc()in Drizzleand
eqlJsonPathAsc(column, path)/eqlJsonPathDesc(column, path)in PrismaNext; both lower to
ORDER BY eql_v3.ord_termover the selected entry.If you call
encryptQuerywith an explicitqueryType, note thatsteVecTermnow produces a scalar JSON ordering term. It no longer meansstructural containment; use the recommended
searchableJsonquery type withan object or array for containment, or
steVecValueSelectorwith{ path, value }for exact equality at a path.The FFI now rejects free-text needles shorter than the configured n-gram size
at the core query-encryption boundary, including callers that bypass adapter
guards.
This EQL release changes the SteVec storage format. Existing EQL v3 encrypted
JSON rows must be re-encrypted before they can be queried with the new domain.
Legacy EQL v2
searchableJson()schemas are rejected during client setupbecause the old selector envelope can no longer be emitted; migrate them to the
v3
types.Jsondomain.EQL 3.0.2 requires typed query-domain operands for encrypted free-text and JSON
operators. PostgREST cannot express those casts, so Supabase v3 fails fast for
matches(), encryptedcontains(), andselectorEq()/selectorNe()insteadof placing a decryptable storage envelope in a GET query string that the new
SQL surface will reject. Use the Drizzle or Prisma Next adapter, or a carefully
scoped direct SQL/RPC path.
Patch Changes
stash@1.0.0-rc.4
Patch Changes
98156ac: Fix the Codex handoff installing zero skills — and losing
AGENTS.mdand.cipherstash/with them — when.codex/is not writable.Codex sandboxes deny writes under
.codex/.installSkillscreated its destination with an unguardedmkdirSync, sitting directly above a per-skill copy loop that was guarded — so the failure threw past that fallback and past the caller, aborting the whole handoff step. Because the skills install runs first, nothing after it ran either: noAGENTS.md, no.cipherstash/context.json, no.cipherstash/setup-prompt.md. All five Codex runs of the rc.3 skilltester matrix landed here, and it was identified in that report as the primary driver of the Claude→Codex quality gap.The fix, hardened by a follow-up review of the first cut:
installSkillsnever throws, and reports what happened. It returns{ copied, failed }instead of a flat list, so callers can tell "unwritable destination" from "stripped build" from "partial copy" without re-deriving it — every filesystem failure degrades to a warning plus afailedentry..codex/skills/— all of them under a sandbox, or a subset after a partial failure — has its body inlined intoAGENTS.mdvia the samedoctrine-plus-skillspath the editor-agent handoff uses. The launch prompt points at wherever each skill actually ended up, including both locations after a partial copy. A stripped build that ships no skills staysdoctrine-onlyand says nothing.dist/commands/init/doctrine, but the compiled resolver probes ancestor directories of the chunk indist/bin/— so every published build silently wrote the minimalAGENTS.mdstub instead of the doctrine (and the inline fallback would have inlined nothing). It now lands atdist/doctrine, like the skills bundle.buildAgentsMdBodyalso honoursdoctrine-plus-skillseven when the doctrine fragment is missing, so inlined skills are never dropped with it.context.jsongains aninlinedSkillsfield, andsetup-prompt.mddistinguishes installed / inlined / failed skills instead of mislabelling an unwritable destination as a "stripped build". The Claude handoff now warns when skills exist but could not be installed, and the AGENTS.md handoff records what it inlined.AGENTS.mdupsert (which refuses malformed sentinel pairs) and the bundled-file reads degrade to warnings instead of aborting the step before.cipherstash/is written.@cipherstash/wizardcarries its own copy ofinstallSkillswith the same unguardedmkdirSyncabove the same guarded copy loop. It targets.claude/skillsrather than.codex/skills, so the Codex sandbox case does not apply, but an unwritable destination crashed it identically — now guarded the same way, with a confirmed-then-failed install recorded in the wizard changelog instead of vanishing with the terminal output.0e2ce93: Fix
stash implandstash inithanging on CI runners that allocate a TTY.Four prompts decided whether to run interactively without going through the
shared TTY helper, so on a CI runner with an allocated TTY they rendered a clack
prompt and blocked forever on
/dev/tty— a silent hang with no error and notimeout:
stash implgated on an inlineprocess.env.CI !== 'true', which onlyrecognised the exact lowercase spelling. Runners that set
CI=1orCI=TRUEblocked on the plan-summary confirmation or the agent-target picker.
stash init's offer to chain intostash plan, and its Proxy-vs-SDKquestion, gated on
process.stdout.isTTYand did not consultCIat all —so they hung on any CI runner with a TTY, whatever the spelling. Gating on
stdout was also the wrong stream: a redirected stdin still hangs a prompt.
stash impl --continue-without-planconfirmed the flag with a second promptthat was not gated at all, so a CI run with no plan on disk blocked there even
though the flag had already granted consent. The flag is now taken as consent
in non-interactive runs and only re-confirmed interactively.
All four now use the shared
isInteractive()helper (stdin is a TTY andCIis not set to
1/truein any case), matchingstash plan. Non-interactiveruns take the path they always should have:
stash initskips the chain offerand prints the
plan --targethint, the Proxy-vs-SDK question defaults toSDK-only, and
stash implproceeds without prompting.c8726cd:
stash init --drizzlenow installs EQL v3 instead of v2.The Drizzle init flow pinned
--eql-version 2, becausestash eql install --drizzle(the only migration-generating install path at the time) wasv2-only. That made
stash init --drizzlethe single flow that provisioned a v2database — a bare
stash eql install, and init for every other integration,already defaulted to v3. It also contradicted the
stash-drizzleskill initcopies into the same project, which documents the v3
@cipherstash/stack-drizzle/v3surface (
types.*domains,EncryptionV3) and would have the user's agentauthor v3 code against a v2 database.
Init's Drizzle flow now routes through
stash eql migration --drizzle, so itstays migration-first (the install lands in your Drizzle migration history and
ships to every environment via
drizzle-kit migrate) while emitting v3 SQL.The generated migration also carries the
cs_migrationstracking schema, so onedrizzle-kit migratecovers everythingstash encrypt …needs. Ifdrizzle-kitisn't installed or configured, init now reports EQL as not installed and points
at
stash eql migration --drizzlerather than aborting the run.The v2 Drizzle path remains available for existing deployments via an explicit
stash eql install --drizzle --eql-version 2; that command's error message nowpoints at the v3 alternative instead of only suggesting
--eql-version 2.04f5a13:
stash initnow scaffolds an EQL v3 encryption client, matching the EQL v3database it installs.
The placeholder client (
DRIZZLE_PLACEHOLDER/GENERIC_PLACEHOLDER) and theintrospection-driven client generator previously emitted EQL v2 authoring
patterns —
Encryption({ schemas }),encryptedColumn(...).equality().freeTextSearch(),and
encryptedType<T>('x', { equality: true }). Since init installs a v3database, this handed the customer's coding agent v2 guidance against a v3
schema (follow-up to stash init --drizzle installs EQL v2 instead of v3 #732 / fix(cli): stash init --drizzle installs EQL v3, not v2 #705).
Scaffolds now teach the v3 surface:
EncryptionV3from@cipherstash/stack/v3,the concrete-domain
types.*factories (types.TextSearch,types.IntegerOrd,types.Text,types.Json, …), and the@cipherstash/stack-drizzle/v3entry(
extractEncryptionSchemaV3) for Drizzle. TheencryptionClientexport shapeand the empty-schema "no schemas yet" error path are unchanged.
46dde37: Fix two defects in the Drizzle migration generator used by
stash eql install --drizzle(EQL v2):--nameis now validated and no longer reaches a shell. The migration name was interpolated into a shell command string, so a name containing shell metacharacters (e.g.--name 'x; rm -rf ~') was executed.--nameis now restricted to letters, numbers, dashes, and underscores, and drizzle-kit is invoked with an argv array instead of a shell string.--outis now actually passed to drizzle-kit. The flag was used to search for the generated migration but never handed todrizzle-kit generate, so any project whosedrizzle.config.tswrites migrations outsidedrizzle/had the file written in one place and searched for in another, failing with "migration file not found".pnpm dlx/npx <pkg>/bunx), which could fetch a different drizzle-kit major into a temp store and resolve a differentdrizzle.config.ts/schema than the project's. It now uses the project-local form (pnpm exec/npx --no-install), so it resolves the project's own drizzle-kit and config and fails loudly if drizzle-kit isn't installed rather than surprise-downloading it. The "run your migrations" hint matches. This aligns v2 with the v3 generator's behaviour.stash eql migration --drizzle(EQL v3) already had all three fixes and is unchanged.cf2c57c: Upgrade Stack to
@cipherstash/protect-ffi0.30 and EQL 3.0.2.Prisma Next includes a versioned EQL 3.0.2 upgrade migration, so databases
that have already recorded the original EQL v3 baseline still install the new
domains and functions.
Encrypted JSON now uses the
public.eql_v3_json_searchstorage domain andeql_v3.query_jsonquery domain. Drizzle selector equality uses exact,GIN-indexable value-selector containment, while selector range comparisons use
a ciphertext-free path selector plus string/number query term. Prisma Next gains
the equivalent
eqlJsonPathEq,eqlJsonPathNeq,eqlJsonPathGt,eqlJsonPathGte,eqlJsonPathLt, andeqlJsonPathLteoperators. SelectorSelector-based
ORDER BYis available asops.selector(column, path).asc()/desc()in Drizzleand
eqlJsonPathAsc(column, path)/eqlJsonPathDesc(column, path)in PrismaNext; both lower to
ORDER BY eql_v3.ord_termover the selected entry.If you call
encryptQuerywith an explicitqueryType, note thatsteVecTermnow produces a scalar JSON ordering term. It no longer meansstructural containment; use the recommended
searchableJsonquery type withan object or array for containment, or
steVecValueSelectorwith{ path, value }for exact equality at a path.The FFI now rejects free-text needles shorter than the configured n-gram size
at the core query-encryption boundary, including callers that bypass adapter
guards.
This EQL release changes the SteVec storage format. Existing EQL v3 encrypted
JSON rows must be re-encrypted before they can be queried with the new domain.
Legacy EQL v2
searchableJson()schemas are rejected during client setupbecause the old selector envelope can no longer be emitted; migrate them to the
v3
types.Jsondomain.EQL 3.0.2 requires typed query-domain operands for encrypted free-text and JSON
operators. PostgREST cannot express those casts, so Supabase v3 fails fast for
matches(), encryptedcontains(), andselectorEq()/selectorNe()insteadof placing a decryptable storage envelope in a GET query string that the new
SQL surface will reject. Use the Drizzle or Prisma Next adapter, or a carefully
scoped direct SQL/RPC path.
524903c: Correct stale EQL v3 guidance in the bundled agent skills.
@cipherstash/migrateand thestash encrypt *commands gained EQL v3 support(Make @cipherstash/migrate (and
stash encrypt *) compatible with EQL v3 #648, now closed), but the shipped skills still told readers therollout tooling was v2-only. Since these skills are copied into customer repos, the
stale text steered users away from v3 and toward workarounds they no longer need.
stash-drizzle,stash-supabase— replaced the "v3 not supported end-to-end"callouts with an accurate EQL version note: the tooling auto-detects a column's
generation from its Postgres domain type, and the two lifecycles differ at the end.
v3 is
backfill → switch the app to the encrypted column by name → dropwith nocut-over rename; v2 keeps the
stash encrypt cutoverrename plus config promotion.stash-supabase— removed the "Interim path until Make @cipherstash/migrate (andstash encrypt *) compatible with EQL v3 #648: the v2 encrypted twin"section; a v2 twin is no longer needed to get CLI-managed backfill.
stash-drizzle,stash-supabase— the drop step now documents thatstash encrypt droptargets the original column under v3 (there is no<col>_plaintext, since nothing was renamed) and<col>_plaintextunder v2.stash-cli— corrected the documentedEQLInstallerdefault:eqlVersiondefaults to
3, not2, matching the--eql-versionCLI default. Also rewordedthe v2 cut-over known-gap note, which cited EQL v3: make it the default, and stop the CLI recommending
db push(a v2/Proxy-only step) #585 as open trackingwhen it was resolved by making v3 the default.
2e6f032: Update the bundled
stash-prisma-nextskill for the EQL v3-only@cipherstash/prisma-next: drop the stale references to the removed EQL v2surface (
cipherstashFromStackV2, thecipherstash*operators, the "legacy v2"subpath note) so the guidance copied into customer repos matches the package.
508f1d5: Breaking (
@cipherstash/stack/wasm-inline): every fallible method now returns aResult—{ data } | { failure }— instead of throwing. AndbulkEncrypt/bulkDecryptare added, so a list of encrypted rows costs one ZeroKMS round trip instead of one per row.Result alignment
encrypt,decrypt,encryptQueryandencryptQueryBulkpreviously threw on failure, and returned bare values on success. They now return{ data } | { failure }, withfailure.typedrawn fromEncryptionErrorTypes(EncryptionErrorfor encrypt-side operations,DecryptionErrorfor decrypt-side) andfailure.codecarrying the FFI error code where there is one.This is the contract the native entry has always honoured, and the one
AGENTS.mdstates outright: "Operations return{ data }or{ failure }. Preserve this shape and errortypevalues inEncryptionErrorTypes." The WASM entry never followed it. That was drift rather than a design decision — nothing about WASM prevents it (@byteslice/resultis already bundled intodist/wasm-inline.js), and it meant edge code had to be written in a different shape from every other surface, with failures that were easy to miss.Fixed now because it is a breaking change and 1.0.0 has not shipped:
@cipherstash/stack@latestis still0.19.0, so this surface has only ever been published under therctag. After GA it would have had to wait for a major.isEncryptedis unchanged — a pure predicate with nothing to fail at, exactly as on the native entry.Bulk operations
The WASM entry previously exposed no bulk operations at all, so rendering an N-row list on Deno, Cloudflare Workers, or Supabase Edge Functions meant N sequential ZeroKMS calls. Combined with the WASM cold start, that made list endpoints impractical on the edge.
Both are index-aligned with their input, and
null/undefinedentries yieldnullat the same index without reaching ZeroKMS (an all-null batch makes no call at all). Because each entry names its own table and column, a singlebulkEncryptcan cover several columns across many rows — which is what makes the saving real, since a single-column batch would still cost one round trip per column.bulkDecryptbuilds on the fallible FFI primitive, so when items fail thefailure.messagenames every failing index with its reason, rather than surfacing the first and discarding the rest.The model helpers (
encryptModel/decryptModeland their bulk forms) remain Node-only: the WASM entry has no single-model operation to build them on, so those need their own port.@cipherstash/stack-supabase@1.0.0-rc.4
Patch Changes
cf2c57c: Upgrade Stack to
@cipherstash/protect-ffi0.30 and EQL 3.0.2.Prisma Next includes a versioned EQL 3.0.2 upgrade migration, so databases
that have already recorded the original EQL v3 baseline still install the new
domains and functions.
Encrypted JSON now uses the
public.eql_v3_json_searchstorage domain andeql_v3.query_jsonquery domain. Drizzle selector equality uses exact,GIN-indexable value-selector containment, while selector range comparisons use
a ciphertext-free path selector plus string/number query term. Prisma Next gains
the equivalent
eqlJsonPathEq,eqlJsonPathNeq,eqlJsonPathGt,eqlJsonPathGte,eqlJsonPathLt, andeqlJsonPathLteoperators. SelectorSelector-based
ORDER BYis available asops.selector(column, path).asc()/desc()in Drizzleand
eqlJsonPathAsc(column, path)/eqlJsonPathDesc(column, path)in PrismaNext; both lower to
ORDER BY eql_v3.ord_termover the selected entry.If you call
encryptQuerywith an explicitqueryType, note thatsteVecTermnow produces a scalar JSON ordering term. It no longer meansstructural containment; use the recommended
searchableJsonquery type withan object or array for containment, or
steVecValueSelectorwith{ path, value }for exact equality at a path.The FFI now rejects free-text needles shorter than the configured n-gram size
at the core query-encryption boundary, including callers that bypass adapter
guards.
This EQL release changes the SteVec storage format. Existing EQL v3 encrypted
JSON rows must be re-encrypted before they can be queried with the new domain.
Legacy EQL v2
searchableJson()schemas are rejected during client setupbecause the old selector envelope can no longer be emitted; migrate them to the
v3
types.Jsondomain.EQL 3.0.2 requires typed query-domain operands for encrypted free-text and JSON
operators. PostgREST cannot express those casts, so Supabase v3 fails fast for
matches(), encryptedcontains(), andselectorEq()/selectorNe()insteadof placing a decryptable storage envelope in a GET query string that the new
SQL surface will reject. Use the Drizzle or Prisma Next adapter, or a carefully
scoped direct SQL/RPC path.
Updated dependencies [cf2c57c]
Updated dependencies [508f1d5]
@cipherstash/wizard@1.0.0-rc.4
Patch Changes
98156ac: Fix the Codex handoff installing zero skills — and losing
AGENTS.mdand.cipherstash/with them — when.codex/is not writable.Codex sandboxes deny writes under
.codex/.installSkillscreated its destination with an unguardedmkdirSync, sitting directly above a per-skill copy loop that was guarded — so the failure threw past that fallback and past the caller, aborting the whole handoff step. Because the skills install runs first, nothing after it ran either: noAGENTS.md, no.cipherstash/context.json, no.cipherstash/setup-prompt.md. All five Codex runs of the rc.3 skilltester matrix landed here, and it was identified in that report as the primary driver of the Claude→Codex quality gap.The fix, hardened by a follow-up review of the first cut:
installSkillsnever throws, and reports what happened. It returns{ copied, failed }instead of a flat list, so callers can tell "unwritable destination" from "stripped build" from "partial copy" without re-deriving it — every filesystem failure degrades to a warning plus afailedentry..codex/skills/— all of them under a sandbox, or a subset after a partial failure — has its body inlined intoAGENTS.mdvia the samedoctrine-plus-skillspath the editor-agent handoff uses. The launch prompt points at wherever each skill actually ended up, including both locations after a partial copy. A stripped build that ships no skills staysdoctrine-onlyand says nothing.dist/commands/init/doctrine, but the compiled resolver probes ancestor directories of the chunk indist/bin/— so every published build silently wrote the minimalAGENTS.mdstub instead of the doctrine (and the inline fallback would have inlined nothing). It now lands atdist/doctrine, like the skills bundle.buildAgentsMdBodyalso honoursdoctrine-plus-skillseven when the doctrine fragment is missing, so inlined skills are never dropped with it.context.jsongains aninlinedSkillsfield, andsetup-prompt.mddistinguishes installed / inlined / failed skills instead of mislabelling an unwritable destination as a "stripped build". The Claude handoff now warns when skills exist but could not be installed, and the AGENTS.md handoff records what it inlined.AGENTS.mdupsert (which refuses malformed sentinel pairs) and the bundled-file reads degrade to warnings instead of aborting the step before.cipherstash/is written.@cipherstash/wizardcarries its own copy ofinstallSkillswith the same unguardedmkdirSyncabove the same guarded copy loop. It targets.claude/skillsrather than.codex/skills, so the Codex sandbox case does not apply, but an unwritable destination crashed it identically — now guarded the same way, with a confirmed-then-failed install recorded in the wizard changelog instead of vanishing with the terminal output.@cipherstash/e2e@0.0.3-rc.4
Patch Changes
@cipherstash/basic-example@1.2.14-rc.4
Patch Changes
@cipherstash/prisma-next-example@0.1.0-rc.4
Patch Changes
@cipherstash/bench@0.0.5-rc.4
Patch Changes
@cipherstash/test-kit@0.0.1-rc.4
Patch Changes