From 9f5a6b70ad757dcc5a1572925d270b38c3c31d8e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2026 05:25:42 +0000 Subject: [PATCH 1/3] Version Packages --- .changeset/claude-review-oidc-guidance.md | 9 - .changeset/cli-install-sql-digest.md | 34 -- .../doctor-optional-absent-is-not-a-pass.md | 10 - .changeset/eql-3-0-5-migration.md | 102 ----- .changeset/eql-3-0-6-migration.md | 56 --- .changeset/eql-exact-runtime-pin.md | 41 -- .changeset/eql-repoint-manifests-to-stack.md | 5 - .changeset/lucky-poems-repeat.md | 24 -- .changeset/nextjs-cts-refusal-diagnostics.md | 33 -- .changeset/prisma-skill-eql-306.md | 7 - .changeset/prisma-skill-upgrade-replan.md | 16 - .changeset/protect-ffi-auth-error-code.md | 16 - .changeset/protect-ffi-client-0-42-3.md | 17 - .../protect-ffi-eql-bindings-in-tree.md | 35 -- .changeset/protect-ffi-jsonwebtoken-cve.md | 5 - .changeset/safe-eql-reinstall.md | 20 - .../skills-eql-source-and-index-cascade.md | 37 -- .changeset/stack-prisma-upgrade-from-1-0-0.md | 16 - .changeset/stash-postgres-signed-range.md | 8 - .changeset/supabase-docs-wasm-inline-entry.md | 184 -------- .changeset/supabase-skill-eql-305.md | 11 - .changeset/usage-limit-refusal-guidance.md | 22 - ...wasm-inline-client-key-not-browser-safe.md | 25 -- .changeset/wasm-inline-type-identity.md | 18 - e2e/CHANGELOG.md | 22 + e2e/package.json | 2 +- examples/basic/CHANGELOG.md | 10 + examples/basic/package.json | 2 +- examples/prisma/CHANGELOG.md | 14 + examples/prisma/package.json | 2 +- packages/bench/CHANGELOG.md | 10 + packages/bench/package.json | 2 +- packages/cli/CHANGELOG.md | 397 ++++++++++++++++++ packages/cli/package.json | 2 +- packages/eql/Cargo.lock | 2 +- packages/eql/crates/eql-bindings/Cargo.toml | 2 +- .../eql-bindings/sql/cipherstash-encrypt.sql | 6 +- .../eql-bindings/sql/release-manifest.json | 4 +- packages/eql/packages/eql/CHANGELOG.md | 9 +- packages/eql/packages/eql/package.json | 2 +- .../packages/eql/sql/cipherstash-encrypt.sql | 6 +- .../packages/eql/sql/release-manifest.json | 4 +- .../eql/src/generated/release-manifest.ts | 4 +- packages/nextjs/CHANGELOG.md | 34 ++ packages/nextjs/package.json | 2 +- packages/protect-ffi/CHANGELOG.md | 73 +++- packages/protect-ffi/Cargo.lock | 10 +- .../integration-tests/CHANGELOG.md | 11 + .../integration-tests/package.json | 2 +- packages/protect-ffi/package.json | 2 +- .../platforms/darwin-arm64/CHANGELOG.md | 2 + .../platforms/darwin-arm64/package.json | 2 +- .../platforms/darwin-x64/CHANGELOG.md | 2 + .../platforms/darwin-x64/package.json | 2 +- .../platforms/linux-arm64-gnu/CHANGELOG.md | 2 + .../platforms/linux-arm64-gnu/package.json | 2 +- .../platforms/linux-x64-gnu/CHANGELOG.md | 2 + .../platforms/linux-x64-gnu/package.json | 2 +- .../platforms/linux-x64-musl/CHANGELOG.md | 2 + .../platforms/linux-x64-musl/package.json | 2 +- .../platforms/win32-x64-msvc/CHANGELOG.md | 2 + .../platforms/win32-x64-msvc/package.json | 2 +- packages/stack-drizzle/CHANGELOG.md | 9 + packages/stack-drizzle/package.json | 2 +- packages/stack-prisma/CHANGELOG.md | 221 +++++++++- packages/stack-prisma/package.json | 2 +- packages/stack-supabase/CHANGELOG.md | 222 ++++++++++ packages/stack-supabase/package.json | 2 +- packages/stack/CHANGELOG.md | 69 ++- packages/stack/package.json | 2 +- packages/test-kit/CHANGELOG.md | 9 + packages/test-kit/package.json | 2 +- packages/wizard/CHANGELOG.md | 187 +++++++++ packages/wizard/package.json | 2 +- 74 files changed, 1338 insertions(+), 802 deletions(-) delete mode 100644 .changeset/claude-review-oidc-guidance.md delete mode 100644 .changeset/cli-install-sql-digest.md delete mode 100644 .changeset/doctor-optional-absent-is-not-a-pass.md delete mode 100644 .changeset/eql-3-0-5-migration.md delete mode 100644 .changeset/eql-3-0-6-migration.md delete mode 100644 .changeset/eql-exact-runtime-pin.md delete mode 100644 .changeset/eql-repoint-manifests-to-stack.md delete mode 100644 .changeset/lucky-poems-repeat.md delete mode 100644 .changeset/nextjs-cts-refusal-diagnostics.md delete mode 100644 .changeset/prisma-skill-eql-306.md delete mode 100644 .changeset/prisma-skill-upgrade-replan.md delete mode 100644 .changeset/protect-ffi-auth-error-code.md delete mode 100644 .changeset/protect-ffi-client-0-42-3.md delete mode 100644 .changeset/protect-ffi-eql-bindings-in-tree.md delete mode 100644 .changeset/protect-ffi-jsonwebtoken-cve.md delete mode 100644 .changeset/safe-eql-reinstall.md delete mode 100644 .changeset/skills-eql-source-and-index-cascade.md delete mode 100644 .changeset/stack-prisma-upgrade-from-1-0-0.md delete mode 100644 .changeset/stash-postgres-signed-range.md delete mode 100644 .changeset/supabase-docs-wasm-inline-entry.md delete mode 100644 .changeset/supabase-skill-eql-305.md delete mode 100644 .changeset/usage-limit-refusal-guidance.md delete mode 100644 .changeset/wasm-inline-client-key-not-browser-safe.md delete mode 100644 .changeset/wasm-inline-type-identity.md create mode 100644 packages/protect-ffi/integration-tests/CHANGELOG.md diff --git a/.changeset/claude-review-oidc-guidance.md b/.changeset/claude-review-oidc-guidance.md deleted file mode 100644 index 83cedd1b1..000000000 --- a/.changeset/claude-review-oidc-guidance.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'stash': patch -'@cipherstash/wizard': patch ---- - -Clarify the bundled supply-chain guidance for non-publishing workload identity -federation. OIDC holders are now classified separately from registry -publishers, with each exchange and its repository permissions reviewed -explicitly. diff --git a/.changeset/cli-install-sql-digest.md b/.changeset/cli-install-sql-digest.md deleted file mode 100644 index 197c7f958..000000000 --- a/.changeset/cli-install-sql-digest.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -'stash': patch ---- - -Verify the EQL install SQL against its release digest before running it. - -`stash eql install` reads the EQL v3 bundle from the resolved -`@cipherstash/eql` in your `node_modules` and executes it against your -database. That read was a bare `readFileSync` — nothing checked that the bytes -on disk were the bundle the resolved release actually ships. A corrupt, -partially-updated, or tampered package installed silently: the database ended -up carrying SQL the version it reports does not define, and the CLI printed -"EQL extensions installed." - -The CLI now hashes the bundle and compares it to `installSqlSha256` from the -release manifest that ships alongside it, and **refuses** on a mismatch. The -error names the expected digest, the actual digest, the resolved file path and -the EQL version, so the remedy is visible rather than inferred. Verification -happens before any database connection is opened, so a refusal means nothing -was attempted — not that something was rolled back. - -The check covers all three paths that read the bundle: `stash eql install`, -the SQL embedded by `stash eql migration --drizzle` / `--supabase`, and the -expected-surface baseline `stash eql verify` compares your database against. -`@cipherstash/stack-prisma` has verified against this same digest since its v3 -migrations landed; this brings the CLI in line. - -No healthy install is affected — the SQL and its manifest are produced by the -same build of `@cipherstash/eql`, so a mismatch only ever means a broken -dependency tree. - -`skills/stash-cli` documents the new pre-flight alongside the existing -post-install surface check, so an agent reading it does not report a digest -refusal as a failed install. diff --git a/.changeset/doctor-optional-absent-is-not-a-pass.md b/.changeset/doctor-optional-absent-is-not-a-pass.md deleted file mode 100644 index 43af18459..000000000 --- a/.changeset/doctor-optional-absent-is-not-a-pass.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'stash': patch ---- - -`stash doctor` no longer reports "All checks passed." when `@cipherstash/stack` -is absent. The package is an optional peer, so running `doctor` before `stash -init` skips the encryption check entirely — the row already said so, but the -outro claimed a pass for a check that never ran. It now ends with "stash doctor -could not run every check.", the same line an unprobeable install gets, and -still exits 0: an absent optional package is recoverable, not a failure. diff --git a/.changeset/eql-3-0-5-migration.md b/.changeset/eql-3-0-5-migration.md deleted file mode 100644 index 3abe4aa89..000000000 --- a/.changeset/eql-3-0-5-migration.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -'@cipherstash/stack-prisma': minor ---- - -Move the bundled EQL v3 migrations to **eql-3.0.5**, which renames the SQL -function `eql_v3.ste_vec_contains` to `eql_v3.jsonb_document_contains`. - -**The blast radius is narrower than a renamed public function suggests.** The -`@>` / `<@` operators on `public.eql_v3_json_search` behave exactly as before, -and so do the two function-form entry points that exist for platforms without -operator support — `eql_v3.jsonb_contains(jsonb, jsonb)` and -`eql_v3.jsonb_contained_by(jsonb, jsonb)` are byte-identical to 3.0.4. Those -are what a PostgREST caller invokes, so PostgREST callers on the documented -surface are **not** affected. The renamed function is the typed implementation -those operators dispatch into. - -**And the old name still works.** eql-3.0.5 ships `eql_v3.ste_vec_contains` as -a deprecated delegating alias for both overloads, so hand-written SQL naming it -— an application query, a view, an RLS policy, or a per-function -`GRANT EXECUTE ON FUNCTION eql_v3.ste_vec_contains(…)` — keeps resolving. The -typed overload stays inlinable, so a function-form query through the alias -still matches the same functional GIN index. Migrate to -`jsonb_document_contains` when convenient; nothing forces it at upgrade time. - -**Separately — and true of every EQL upgrade, not just this one:** the install -bundle opens with `DROP SCHEMA IF EXISTS eql_v3 CASCADE`, so applying it drops -every object in `eql_v3` / `eql_v3_internal` and everything that depended on -them. **Encrypted data and column types are not affected** — the storage -domains are `public.eql_v3_*`, deliberately outside both dropped schemas, and -their CHECK functions are re-created rather than dropped. What does not survive -is everything else pointing into the schema, which is two actions, neither of -them to do with the rename: - -1. **Re-run your grant script.** Every grant on every `eql_v3` / - `eql_v3_internal` object is gone. The schema-wide form EQL documents — - `GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA eql_v3 TO app_role` — picks up - both the new name and the alias on its own. -2. **Recreate your functional indexes, then `ANALYZE`.** Indexes over - `eql_v3.eq_term(…)` / `ord_term` / `match_term` / `to_ste_vec_query(…)` - depend on the dropped schema and go with it. Nothing errors afterwards: - encrypted predicates keep working and silently fall back to sequential - scans. A migration runner will not redo an already-applied migration, so - this has to be a *new* one. The `stash-indexing` skill documents the - mechanism ("These indexes do not survive an EQL reinstall or upgrade") and - the `EXPLAIN` check that confirms recovery; capturing and restoring them - automatically is tracked in - [cipherstash/stack#918](https://github.com/cipherstash/stack/issues/918). - -Any RLS policy, view, or constraint that calls an `eql_v3` function is dropped -by the same CASCADE and needs recreating too. **The rename itself needs no -action — the alias makes it non-breaking.** - -Two artefacts carry the new invariant: - -- A new upgrade edge, `20260814T0000_upgrade_eql_v3_3_0_5`, carrying the - invariant `cipherstash:upgrade-eql-v3-bundle-3.0.5-v1`. Databases already - running an earlier bundle re-install through this edge on the next - `prisma-next migration plan` followed by `prisma-next migrate`, exactly as - they did for 3.0.2 and 3.0.4. **`migrate` alone is not enough** — the seed - phase that copies a new migration package into your repo runs only from - `migration plan`, so without it the 3.0.5 directory never reaches disk and - `migrate` is a silent no-op that leaves the database on the older bundle. -- The baseline install migration `20260601T0100_install_eql_v3_bundle`, which - gains a no-SQL carrier op for the new invariant. The baseline itself bakes - eql-3.0.6, not 3.0.5: it was re-emitted again for 3.0.6 before any release - carried the 3.0.5 bytes (see the 3.0.6 entry). Fresh databases land on 3.0.6 - from the single all-additive genesis edge, keeping `db init` (additive-only - policy) working. - -**Action required.** The baseline's bytes — and so its `migrationHash` — have -changed. This is one change, shared with the 3.0.6 entry: npm never shipped -the 3.0.5 baseline on its own. If your project already has a -`migrations/cipherstash/` directory generated against -`@cipherstash/stack-prisma@1.0.0`, `@1.1.0` or `@1.1.1`, delete that directory -and re-run `prisma-next migration plan` (or `migrate`); the 1.1.0 Prisma Next -0.17 upgrade re-anchored the same artefacts, so a space vendored against any -of them is stale here. The seed phase regenerates -it byte-identical to the shipped artefacts. Your database keeps its markers, so -already-applied invariants are not re-run — the only new work is the 3.0.5 and -3.0.6 upgrade edges. - -If you skip the delete, nothing warns you: a vendored baseline is stale but -internally intact, so it passes every integrity check. On an existing database -the upgrade still applies correctly; on a **fresh** one, `db init` refuses with -`Operation cipherstash.upgrade-eql-v3-bundle-3.0.5 has class "data" which is -not allowed by policy.` — an error that names neither the directory nor the -remedy. See "Upgrading from 1.0.0 or 1.1.x" in the package README. - -**Why the baseline was re-emitted rather than left frozen.** These artefacts are -content-addressed and normally append-only: an EQL bump ships as a new upgrade -directory and published directories are never rewritten. That rule cannot be -followed here without a second `from: null` genesis edge, because no upgrade -edge can ever be walked by `db init` — every upgrade edge is a self-edge, and -the integrity checker requires a self-edge to carry a `data`-class op, which -`db init`'s additive-only policy refuses. A fresh database must therefore -collect every head-ref invariant from the genesis edge it walks. The -append-only alternative would duplicate the full ~2.6 MB bundle into a new -genesis edge on every EQL release, permanently; re-emitting was taken instead -while 1.0.0 was two weeks old with negligible adoption, and is a decision to be -re-argued on adoption numbers rather than repeated by default. This re-emit -never reached npm: 1.1.0 and 1.1.1 shipped the eql-3.0.4 baseline, and the -3.0.6 re-emit replaced these bytes before a release carried them. diff --git a/.changeset/eql-3-0-6-migration.md b/.changeset/eql-3-0-6-migration.md deleted file mode 100644 index e356d3226..000000000 --- a/.changeset/eql-3-0-6-migration.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -'@cipherstash/stack-prisma': minor ---- - -Move the bundled EQL v3 migrations to **eql-3.0.6**. This release changes only -the version stamp: `eql_v3.version()` returns `'3.0.6'` and the `eql_v3` schema -comment reads `'3.0.6'`. No function, operator, domain or index behaviour -changes. - -Two artefacts carry the new bundle: - -- A new upgrade edge, `20261002T0000_upgrade_eql_v3_3_0_6`, carrying the - invariant `cipherstash:upgrade-eql-v3-bundle-3.0.6-v1`. Existing databases - re-install through it on the next `prisma-next migration plan` followed by - `prisma-next migrate`. **`migrate` alone is not enough**: only - `migration plan` copies the new directory into your repo. -- The baseline install migration `20260601T0100_install_eql_v3_bundle`, which - now bakes 3.0.6 and gains a fifth op, a no-SQL carrier for the new - invariant, so fresh databases land on 3.0.6 from the single all-additive - genesis edge and `db init` keeps working. - -**Upgrading a 1.0.0 or 1.1.x database installs the EQL bundle twice.** Those -releases shipped eql-3.0.4, so `migrate` walks the 3.0.5 upgrade edge and then -the 3.0.6 one, re-installing the ~2.6 MB bundle once for each in the same run. -That costs time, not correctness: the database ends on 3.0.6 with every -invariant recorded. The 3.0.5 edge is kept rather than folded into this one -because the frozen-history guard treats every committed edge as published, and -a database built from `main` may already have walked it and recorded its -invariant. As with every EQL upgrade, each re-install opens with -`DROP SCHEMA IF EXISTS eql_v3 CASCADE`: re-run your grant script, and recreate -functional indexes and anything else depending on `eql_v3` (see the 3.0.5 -entry). - -**Action required: the one in the 3.0.5 entry, once.** npm has only ever -shipped the eql-3.0.4 baseline (1.0.0 on 30 July, 1.1.0 on 19 August, 1.1.1 on -20 August), so this release changes the published baseline once, not twice. If -your project has a `migrations/cipherstash/` directory generated against any -earlier version, delete it and re-run `prisma-next migration plan`. Your -database keeps its markers, so already-applied invariants are not re-run. If -you skip the delete, a fresh `db init` refuses with -`Operation cipherstash.upgrade-eql-v3-bundle-3.0.5 has class "data" which is -not allowed by policy.` — see "Upgrading from 1.0.0 or 1.1.x" in the package -README. - -**Why the baseline was re-emitted rather than given a second genesis edge.** -In this repository the baseline was already re-emitted once, for 3.0.5, and -that decision said the trade must be re-argued on adoption numbers at the next -bump. This is that bump, and the trade came out the same way. -`@cipherstash/stack-prisma` had 144 npm downloads in September 2026 (52 in the -last week), fewer than the ~253 monthly downloads at which the 3.0.5 re-emit -was judged a small, knowable blast radius. The 3.0.5 re-emit never reached -npm, so what this one rewrites is unpublished. And since 3.0.6 changes only -the version stamp, the append-only alternative, a second `from: null` genesis -edge, would add another permanent ~2.6 MB copy of the bundle for no change in -behaviour. Once this package has real adoption, the second genesis edge is the -right shape. diff --git a/.changeset/eql-exact-runtime-pin.md b/.changeset/eql-exact-runtime-pin.md deleted file mode 100644 index 00b4eef9f..000000000 --- a/.changeset/eql-exact-runtime-pin.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -'@cipherstash/stack-prisma': patch -'stash': patch ---- - -Pin the packed `@cipherstash/eql` dependency to an exact version, closing a -route by which an installed EQL bundle could drift ahead of the code built -against it. - -Both packages declared `"@cipherstash/eql": "workspace:^"` under -`dependencies`. In this workspace that resolves in-tree either way, so nothing -in development or CI could see a difference — but the two specifiers do not -pack the same. pnpm rewrites the protocol when it builds the tarball a customer -actually installs: - - "workspace:^" packs as "^3.0.5" - "workspace:*" packs as "3.0.5" - -The caret is the problem. `@cipherstash/eql` is still published from -`cipherstash/encrypt-query-language` until the publisher repoint, so a 3.0.x can -reach npm without passing through this repository at all — and `^3.0.5` accepts -it. A customer installing `stash` or `@cipherstash/stack-prisma` would then get -SQL that STORES and queries encrypted payloads at one version, while -`@cipherstash/stack`'s v3 domain types (which EMIT those payloads) and -`stack-prisma`'s baked migrations stayed frozen at the version this repo built -and tested against. The two halves of EQL are released in lockstep precisely -because that skew does not fail at install or in CI — it fails in a database. - -`workspace:*` is the only form that closes it. A literal `"3.0.5"` would be an -exact pin too, but it is a registry pin: `pnpm run lint:eql-pins` rejects it, -because resolving EQL from a registry rather than from this repo is the same -drift one layer up. - -No API, behaviour or SQL changes. What changes is the dependency range in the -published tarballs, and only in the narrowing direction — the version resolved -today is the version that was already being resolved. Nothing needs to be done -on upgrade. - -`@cipherstash/stack` declares the same dependency under `devDependencies` and -is deliberately left alone: pnpm rewrites that range too, but no consumer of the -package ever resolves it. diff --git a/.changeset/eql-repoint-manifests-to-stack.md b/.changeset/eql-repoint-manifests-to-stack.md deleted file mode 100644 index 903621653..000000000 --- a/.changeset/eql-repoint-manifests-to-stack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@cipherstash/eql': patch ---- - -Point `repository`, `repository.directory`, and `bugs.url` at `cipherstash/stack` instead of the archived `cipherstash/encrypt-query-language`. Purely metadata — no behaviour change — but required before npm's trusted publishing (already repointed at `cipherstash/stack`) can accept a release: npm rejects a publish whose manifest `repository.url` doesn't match the publishing repository. diff --git a/.changeset/lucky-poems-repeat.md b/.changeset/lucky-poems-repeat.md deleted file mode 100644 index b7f49eaf4..000000000 --- a/.changeset/lucky-poems-repeat.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -'@cipherstash/stack-supabase': patch ---- - -Correct the runtime story in the TSDoc that ships as `.d.ts`. - -Three claims a user sees on hover were wrong: - -- `makeEncryptedSupabase` said "Declare your schemas and it runs anywhere; omit - them and we discover them for you, which needs a database connection and is - therefore Node-only." Declaring `schemas` does skip introspection entirely — - no Postgres connection, no `pg`, no `databaseUrl` — but it does not make the - default entry edge-capable. **The entry point decides where the wrapper runs; - `schemas` decides only whether Postgres is involved.** -- The default entry's doc named `@cipherstash/protect-ffi` as the Node-API - binary loaded on import. It is the one package in that graph that - deliberately does not load on import; the module-evaluation-time load belongs - to `@cipherstash/auth`. -- `./wasm-inline`'s doc called introspection "half of what made the default - entry Node-only". The engine is what makes it Node-only, and its emitted - bundle also carries an `import("pg")` specifier a bundler resolves at build - time. Introspection is a separate axis. - -Documentation only — no runtime behaviour changes. diff --git a/.changeset/nextjs-cts-refusal-diagnostics.md b/.changeset/nextjs-cts-refusal-diagnostics.md deleted file mode 100644 index 004cbb71d..000000000 --- a/.changeset/nextjs-cts-refusal-diagnostics.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -'@cipherstash/nextjs': minor ---- - -Report what the CipherStash token service actually said when it refuses a token. - -`getCtsToken()` reported a non-2xx response as `Failed to fetch CTS token: ` and -nothing else. It read `statusText`, which is the empty string over HTTP/2 — so -the message ended at the colon — and it discarded the response body, taking any -refusal code with it. A billing refusal was indistinguishable from a bad token, -and the accompanying log said "contact support", which is the wrong advice for -an organisation that needs to upgrade a plan. - -The failure now names the status and quotes what the service returned, and the -refusal code is surfaced on a new optional `authCode` field of -`GetCtsTokenResponse` — `USAGE_LIMIT_EXCEEDED` for an organisation over its -allowance, `ORG_NOT_PROVISIONED` for one not registered with the usage system. -Both are terminal: retrying cannot clear either. Unknown `402` codes are -declined so a future payment-required response does not inherit the wrong -classification. - -The body is read as text exactly once and then parsed defensively, never with -`response.json()`. The two shapes are not the same shape: a `402` is JSON, while -every other failure from this endpoint is `text/plain` (a `401` is the bare -string `Authorization failed: InvalidToken`), and `.json()` on one of those -throws a `SyntaxError` that replaces the real failure with a parse error. A -response that is not a recognisable CipherStash refusal — a gateway or WAF -answering in front of the service — still reports its status and body rather -than being reported as a billing problem it is not. - -This package does not depend on `@cipherstash/stack`, so it carries the code -rather than a copy of that package's remedy text — look the remedy up from -`authCode` if you need to render one. diff --git a/.changeset/prisma-skill-eql-306.md b/.changeset/prisma-skill-eql-306.md deleted file mode 100644 index 3be370993..000000000 --- a/.changeset/prisma-skill-eql-306.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'stash': patch ---- - -`skills/stash-prisma`: the stale-`migrations/cipherstash/` table now describes -eql-3.0.6. The baseline bakes 3.0.6, 1.1.x shipped 3.0.4 as 1.0.0 did, and an -existing database upgrades through both the 3.0.5 and 3.0.6 edges. diff --git a/.changeset/prisma-skill-upgrade-replan.md b/.changeset/prisma-skill-upgrade-replan.md deleted file mode 100644 index 19d93f919..000000000 --- a/.changeset/prisma-skill-upgrade-replan.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'stash': patch ---- - -`skills/stash-prisma` now documents the re-plan step that follows an -`@cipherstash/stack-prisma` upgrade: `rm -rf migrations/cipherstash && npx -prisma-next migration plan`, why only `migration plan` vendors new migration -packages, and the exact `db init` refusal a stale vendored directory produces on -a fresh database (`Operation cipherstash.upgrade-eql-v3-bundle-3.0.5 has class -"data" which is not allowed by policy.`). - -The package README already carried this; the skill did not — and the skill is -what ships inside the `stash` tarball and gets copied into a user's -`.claude/skills/`, so an agent driving the upgrade hit the refusal with no route -out of it. `packages/stack-prisma/test/v3/stale-vendored-space.test.ts` now pins -both files to the planner's real message so they cannot drift apart again. diff --git a/.changeset/protect-ffi-auth-error-code.md b/.changeset/protect-ffi-auth-error-code.md deleted file mode 100644 index 4dd3439af..000000000 --- a/.changeset/protect-ffi-auth-error-code.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'@cipherstash/protect-ffi': minor ---- - -Carry `stack-auth` diagnostics across the JavaScript boundary. Errors that -originate in `stack-auth` now expose `authCode`, `help`, and `url` on both the -native and WASM bindings, alongside protect-ffi's existing `code` field. - -The boundary remains deliberately thin: `Error::Auth` and `Error::ZeroKMS` are -transparent miette diagnostics, so stack-auth continues to own the message, -instructions, and destination URL. Protect-ffi only serializes those fields and -reads the stable auth code from the typed `AuthError`; it does not classify the -message or maintain its own remedy taxonomy. - -`getAuthErrorCode(err)` reads the new field and `ProtectAuthErrorCode` types it. -The auth taxonomy is separate from protect-ffi's closed `ProtectErrorCode` set. diff --git a/.changeset/protect-ffi-client-0-42-3.md b/.changeset/protect-ffi-client-0-42-3.md deleted file mode 100644 index 3584a789d..000000000 --- a/.changeset/protect-ffi-client-0-42-3.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -'@cipherstash/protect-ffi': patch ---- - -Move the CipherStash client crates to `0.42.3` — `cipherstash-client`, -`cts-common`, `stack-auth` and `stack-profile`, which release in lockstep. - -This is the release that raises the usage-denial taxonomy. `stack-auth` gained -typed `UsageLimitExceeded` / `OrgNotProvisioned` errors with a `help` and a -`url` on each, a shared classifier for a `402` from any credential-issuance -path, and a 60-second sticky cache so a refused organisation stops re-issuing -the same doomed request at its own request rate. Together they are what makes -`authCode` on a failure report a billing refusal as one, rather than as a -generic server error a retry loop will hammer. - -It also carries a ZeroKMS change requiring `org_id` on every token. The client -side decodes claims without requiring it, so this is transparent here. diff --git a/.changeset/protect-ffi-eql-bindings-in-tree.md b/.changeset/protect-ffi-eql-bindings-in-tree.md deleted file mode 100644 index 894015531..000000000 --- a/.changeset/protect-ffi-eql-bindings-in-tree.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -'@cipherstash/protect-ffi': patch ---- - -Compile `eql-bindings` from this repository rather than from crates.io. - -The native binding pinned `eql-bindings = "=3.0.2"` from the registry. It now -resolves by path from `packages/eql/crates/eql-bindings`, which ships at 3.0.5 -alongside the `@cipherstash/eql` SQL bundle. - -**No behaviour change.** `eql-bindings` is the Rust half of EQL — it EMITS the -encrypted payloads that the SQL half STORES and queries — and its Rust source is -byte-identical across 3.0.2, 3.0.4 and 3.0.5 (`src/`, `bindings/` and `schema/` -compared directly). What 3.0.3 through 3.0.5 changed was SQL, carried on the -shared lockstep version number. So the payloads this binding produces are the -same bytes before and after; what moves is the version stamped on the crate -compiled into `index.node`, from 3.0.2 to 3.0.5. - -**Why it is worth a release anyway.** A registry pin let the two halves of EQL -drift apart silently. Nothing asserted they agreed: a mismatched pair compiles, -passes every suite, and fails in a database — because the failure is a payload -the installed SQL cannot read, which no unit test holds both sides of. Resolving -from the tree makes the skew unrepresentable: the emitter and the SQL are now -the same commit, and `pnpm run lint:eql-pins` fails any change that reintroduces -a registry pin on either. - -The flip was taken while it was a no-op deliberately. Waiting for the first -release where the two halves genuinely diverge would have turned a provenance -change into a behaviour change that had to be argued under credentialed test. - -Verified without credentials: `cargo build -p protect-ffi` clean, the crate test -suite green (310 passed) with `cargo fmt --check` clean, and a -`wasm32-unknown-unknown` build clean — the last of those being the target where a -cross-workspace path dependency would break first, since the EQL workspace never -otherwise builds for wasm32. diff --git a/.changeset/protect-ffi-jsonwebtoken-cve.md b/.changeset/protect-ffi-jsonwebtoken-cve.md deleted file mode 100644 index 8693f8e97..000000000 --- a/.changeset/protect-ffi-jsonwebtoken-cve.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@cipherstash/protect-ffi': patch ---- - -Bump `cipherstash-client`, `cts-common`, `stack-auth`, and `stack-profile` to 0.42.2, which moves the transitive `jsonwebtoken` dependency from 9.3.1 to 10.4.0, resolving [CVE-2026-25537](https://github.com/advisories/GHSA-h395-gr6q-cpjc) (a JWT claim-validation type-confusion bug that could allow bypassing `nbf`/`exp` checks). No API changes. diff --git a/.changeset/safe-eql-reinstall.md b/.changeset/safe-eql-reinstall.md deleted file mode 100644 index 8f604fa59..000000000 --- a/.changeset/safe-eql-reinstall.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -"stash": minor ---- - -Preserve encrypted data and reconstruct functional indexes when reinstalling EQL v3, while refusing unsupported external dependencies before mutation. - -`stash eql install` and `stash eql upgrade` now capture dependent functional -indexes before replacing the disposable EQL schemas, then restore and verify -their definitions, clustering, replica-identity role, comments, explicit -statistics targets, and health in the same transaction. A -reconstruction failure rolls the replacement back. -PostgreSQL derives index ownership from the table owner, so reinstall verifies -the resulting owner and rolls back on a mismatch rather than independently -restoring ownership. -Unsupported dependencies—including views, policies, constraints, and -partitioned indexes—are named and refused before mutation. - -Reinstall remains a maintenance-window operation: its advisory lock serializes -`stash` lifecycle commands, not unrelated database DDL. Generated EQL migrations -contain the raw bundle and do not include these reinstall protections. diff --git a/.changeset/skills-eql-source-and-index-cascade.md b/.changeset/skills-eql-source-and-index-cascade.md deleted file mode 100644 index 6b7c75930..000000000 --- a/.changeset/skills-eql-source-and-index-cascade.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -'stash': patch ---- - -Correct two things the bundled agent skills were telling customers wrongly -about EQL. - -**`skills/stash-postgres` pointed at the wrong repository.** EQL's source now -lives in `cipherstash/stack` under `packages/eql/`, and that is where operator -gaps and domain-level bugs are filed; only *publishing* still happens from -`cipherstash/encrypt-query-language`, which the skill continues to say. The -skill also cited "the EQL skill" as a source of truth that "ships from -`encrypt-query-language` alongside the bundle" — no such skill ships from -either repository, so the reference is gone and the remaining three sources -(the generated types, the install SQL, and `SELECT eql_v3.version()`) are -renumbered. - -**And it claimed the CLI pins an exact `@cipherstash/eql` version, "so a -database is only ever on one bundle."** Neither half holds: the CLI depends on -the workspace package rather than a pinned literal, and a database is on -whatever bundle was last applied to it — the Prisma Next adapter installs and -upgrades the bundle through its own migrations without involving the CLI at -all. Replaced with the guarantee that does hold: one `stash` release carries -one resolved bundle, and the database is the authority on which bundle it has. - -**`skills/stash-prisma` hands out the functional-index recipe without saying an -EQL upgrade destroys it.** Installing a bundle begins with `DROP SCHEMA IF -EXISTS eql_v3 CASCADE`, which cascade-drops every index over an `eql_v3.*` -extractor — the PSL expression indexes Prisma Next 0.17 introduced and any -`rawSql` index DDL alike; queries keep working and silently sequential-scan. -Because an applied migration is never replayed, recovery is a NEW one: a PSL -expression index has to change its `name:` (the physical name carries a content -hash of the expression, so re-declaring the same one plans no work), and a -`rawSql` recovery op needs a new `id`. Said where the recipe is given, pointing -at `stash-indexing` for the mechanism and at -[cipherstash/stack#918](https://github.com/cipherstash/stack/issues/918) for -capturing and restoring them automatically. diff --git a/.changeset/stack-prisma-upgrade-from-1-0-0.md b/.changeset/stack-prisma-upgrade-from-1-0-0.md deleted file mode 100644 index 8b7dc2f5e..000000000 --- a/.changeset/stack-prisma-upgrade-from-1-0-0.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'@cipherstash/stack-prisma': patch ---- - -Document the upgrade from 1.0.0 or 1.1.x in the package README: why -`migrations/cipherstash/` must be deleted and regenerated, what each Prisma Next -command does if it is not, and the exact `db init` refusal -(`Operation cipherstash.upgrade-eql-v3-bundle-3.0.5 has class "data" which is -not allowed by policy.`) that a stale vendored directory produces on a fresh -database. - -The behaviour worth knowing regardless of version: only `prisma-next migration -plan` copies new migration packages into your repo. Running `migrate` or -`db init` after upgrading this package without planning first silently leaves -the database on the older EQL bundle — a stale vendored directory is internally -intact, so it passes every integrity check and nothing reports a problem. diff --git a/.changeset/stash-postgres-signed-range.md b/.changeset/stash-postgres-signed-range.md deleted file mode 100644 index 2f9a6539f..000000000 --- a/.changeset/stash-postgres-signed-range.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'stash': patch ---- - -Document how to express an absolute-value bucket over an EQL v3 ordered -column by decomposing it into positive and negative signed ranges. The -`stash-postgres` recipe encrypts all four bounds, shows the reversed negative -inequalities, and states the required bound validation. diff --git a/.changeset/supabase-docs-wasm-inline-entry.md b/.changeset/supabase-docs-wasm-inline-entry.md deleted file mode 100644 index c494f6652..000000000 --- a/.changeset/supabase-docs-wasm-inline-entry.md +++ /dev/null @@ -1,184 +0,0 @@ ---- -'@cipherstash/stack-supabase': patch -'stash': patch -'@cipherstash/wizard': patch ---- - -Stop telling customers the Supabase wrapper cannot run in a Worker. - -`@cipherstash/stack-supabase` has shipped two entry points since #912. The -package root introspects your database and runs on Node; the `wasm-inline` -entry carries the WASM engine, takes declared `schemas` instead of -introspecting, and runs on Deno, Supabase Edge Functions and Cloudflare -Workers. Introspection was the only thing that needed a Postgres socket, and -that entry does not do it. - -Two shipping documents were never updated and still described the state before -that change: - -- `packages/stack-supabase/README.md` said "the factory cannot run in an edge - Worker or the browser" and did not mention the `wasm-inline` entry anywhere - in the file. This is the npm package page. -- `skills/stash-supabase/SKILL.md` said the same thing in its setup section. - The skill ships inside the `stash` tarball, and `stash init` copies it into - the customer's own repository, where their coding agent reads it as - instruction. The one correct mention of the edge entry was in a callout near - the top that the setup steps never pointed at, so a reader following the - setup never learned the second entry existed. - -The population this misled hardest is the one that needs the edge entry most: -server code on Lovable, v0, Bolt and Replit runs on an edge runtime, which is -exactly the case `wasm-inline` was built for and exactly the case these -documents called impossible. - -Both files now describe both entries. The README gains an "Edge runtimes" -section with the call shape; the skill gains a fifth setup step with the same, -and the introspection paragraph now scopes its restriction to the native entry -and points there. Both name the four ways the edge entry differs: `schemas` is -required, `config` is required, `databaseUrl` is refused, and -`.withLockContext()` / `.audit()` throw rather than silently dropping an -identity claim (#797). - -The **browser** half of the old sentence was correct and is kept, with the -reason now given: the WASM client requires a workspace `clientKey` on every -authentication path, so a browser build would ship the key with it (#804). - -Four claims that were wrong in the same neighbourhood are corrected while we -are here, three of them pre-dating this change: - -- **`skills/stash-managed-platforms/SKILL.md` shipped a snippet that does not - compile.** It authored the `schemas` object from `@cipherstash/stack/wasm-inline` - and handed it to `encryptedSupabase` from `@cipherstash/stack-supabase/wasm-inline`. - The adapter types `schemas` from `@cipherstash/stack/eql/v3`, and the two - entries ship independent declarations of the column classes whose private - `columnName` field TypeScript compares nominally — so `tsc` rejects it while - the code runs perfectly, which is why nobody noticed. The schema import now - comes from `eql/v3`, and a new guard - (`scripts/__tests__/skills-supabase-edge-schema-entry.test.mjs`) fails if any - shipped document pairs the two again. -- **`skills/stash-edge/SKILL.md` is what produced that snippet.** Its "Schema - Modules Do Not Cross Entries" section told edge projects to author schemas - from `@cipherstash/stack/wasm-inline` with no carve-out. The rule is really - "author against the entry whose *client type* consumes the schema": a raw - `Encryption` client from `wasm-inline` wants `wasm-inline` tables, but the - Supabase adapter wants `eql/v3` tables on both of its entries, WASM engine or - not. The section now says so, and the "The Supabase adapter has its own edge - entry" note points at it. -- **`skills/stash-supabase/SKILL.md` described the wrong failure mode for a - missing declaration.** Omitting `schemas` on the edge entry cannot produce a - no-column client — it is non-optional on the type and throws at construction — - and an undeclared *table* throws rather than passing through unencrypted. The - hazard is an undeclared **column** on a declared table, which is treated as - plaintext; the bullet now says that, along with the one thing that limits it - (a plaintext write to an `eql_v3_*` column fails the domain CHECK, though a - NULL still passes) and the fact that the native entry's warning about - unverified declarations is gated on the introspector and so never fires - there. Reads get no equivalent backstop, and the bullet now says so: the - `select('*')` refusal looks like one, but a query awaited with no - `.select()` at all takes the raw-`*` branch in `query-builder.ts` and - returns every column undecrypted. -- **"Undeclared tables behave exactly as with no `schemas` at all" was false on - the native entry too.** Introspection is gated on a resolved database URL, - not on the absence of `schemas`, and an ambient `DATABASE_URL` is - deliberately ignored once tables are declared. The statement holds only when - `databaseUrl` is passed *alongside* `schemas`, which is what it now says. - -`config` is corrected everywhere that called all four `CS_*` values mandatory: -the README, `skills/stash-supabase/SKILL.md`, and — in `skills/stash-edge/SKILL.md` -— its frontmatter description, its Credentials section, its troubleshooting -advice, and the native-vs-WASM comparison table. That skill already contradicted -itself, since its own `config.authStrategy` example passes two values, not four. -The same sentence in the `EncryptedSupabaseWasmOptions` doc comment -(`packages/stack-supabase/src/wasm-inline.ts`) is fixed too, comment-only. Only -`clientId` and `clientKey` are always required. Beyond them the config is a union — the -access-key path adds `workspaceCrn` + `accessKey`, and the strategy path takes -a pre-built `config.authStrategy` and makes `workspaceCrn` optional, because a -built strategy already carries the CRN. `OidcFederationStrategy` is re-exported -from `@cipherstash/stack/wasm-inline`, so authenticating as the end user works -on the edge; what does not work is binding data to that user, which stays -called out in its own `.withLockContext()` bullet. - -Tests now anchor the corrected claims against the code rather than against -prose. `packages/stack-supabase/__tests__/supabase-wasm-config.test-d.ts` asserts -at the type level that the edge `config` accepts both the access-key arm and a -strategy-only arm without `workspaceCrn`, and rejects `clientId` + `clientKey` -alone. `supabase-declared-mode.test.ts` gains a case pinning the real hazard: an -undeclared column on a declared table reaches PostgREST as plaintext on insert, -update and filter, and is absent from the decrypt call. - -Review found four more, one of them a change to a published type: - -- **`databaseUrl` was only refused for callers who wrote the options inline.** - `EncryptedSupabaseWasmOptions` left the field out, and omission is policed by - excess-property checking, which fires on fresh object literals alone. An - options object assembled as a `const` and passed by variable — which is what - a Node-to-edge port actually holds — type-checked clean and reached the - construction-time throw instead, from documents saying the type checker - enforced it. The field is now declared `databaseUrl?: never`, mirroring - `WasmClientConfig.eqlVersion?: never` in `@cipherstash/stack`, which exists - for the identical reason one package along. The runtime throw stays as the - backstop for plain JS. New type tests cover the inline and by-variable - shapes on both call forms, plus a positive control that the same options - object still compiles once `databaseUrl` is dropped. -- **The `select('*')` correction had landed in only one of its two shipped - copies.** `skills/stash-supabase/SKILL.md` carried it; - `skills/stash-managed-platforms/SKILL.md`, edited in the same change, still - framed declared mode as giving things up "loudly rather than silently" over a - bullet naming the refusal — precisely the inference the correction exists to - kill. That skill is read as instruction by an agent on Lovable, v0, Bolt or - Replit, and the failure it mispromised is silent: raw EQL payloads returned - as `data`, no error. The wording is carried across, and a new guard - (`scripts/__tests__/skills-select-star-not-a-read-backstop.test.mjs`) fails - if any shipped document states the refusal without the caveat in the same - section. Two copies of one fact drift the moment one of them is edited, and - no reviewer diff shows the copy nobody touched. -- **"Everything after construction is the same wrapper" was false in the first - way a reader hits it.** Both `packages/stack-supabase/README.md` and - `skills/stash-supabase/SKILL.md` said `from()`, the filters and the response - shape are identical across the two entries — the README saying so twenty-five - lines under a paragraph telling the same reader `select('*')` just works. The - edge entry is always in declared mode, where `select('*')` is refused and - `from()` on an undeclared table throws. Both sentences now name the two - exceptions, so the quick-start snippet the section tells you to port no - longer arrives with a promise it breaks. -- **The ambient-`DATABASE_URL` warning cannot fire on the edge entry.** - `skills/stash-managed-platforms/SKILL.md` said an ambient `DATABASE_URL` is - ignored when `schemas` are passed, "with a warning that the declaration is - unverified" — in a section whose subject is `wasm-inline`. Both the ambient - read and the warning are gated on the introspector, which that build does not - have, so nothing there ever tells you a declaration is incomplete. Now scoped - to Node, with the edge case stated. The sentence immediately above it had the - same fault and is fixed with it: the ⚠️ callout on undeclared columns offered - "pass `databaseUrl` so introspection fills the gaps" as the remedy, inside a - section about the entry that refuses `databaseUrl` — and it is the remedy a - Lovable or Replit agent, which has only the edge entry, would have reached - for. It now says introspection is unavailable there and what to do instead. - -`@cipherstash/wizard` is bumped alongside `stash` because `skills/` ships in -both tarballs — `packages/wizard/tsup.config.ts` copies it into `dist/skills` -and `package.json` lists that under `files`. Without the bump the published -wizard keeps shipping the old text until some unrelated change moves its -version. - -One more wording correction, of the kind #952 fixed in this package's `.d.ts`: -`packages/stack-supabase/README.md` and `skills/stash-supabase/SKILL.md` both -derived "runs on Node only" from introspection — "introspection needs a direct -Postgres connection, **so** … this entry runs on Node only". Introspection is -not the cause. The entry binds the native engine, so it is Node-only whether or -not you declare `schemas`; a reader who took the stated cause at face value -would conclude that declaring tables makes the root entry edge-capable, which is -the exact wrong turn the `wasm-inline` entry exists to prevent. Both sentences -now attribute the restriction to the engine and say that declaring `schemas` -does not move it. Both files are enrolled in #952's -`scripts/__tests__/supabase-runtime-claims.test.mjs`, which is what its own -comment said to do once this branch stopped rewriting the same lines — the -README and this skill are the two copies that reach a customer, and the guard -now fails if either grows the claim back. `skills/stash-managed-platforms/SKILL.md` -stays out, with the reason written down: its causal claims are correct, but -rewording the unqualified "Worker" in its frontmatter `description` changes -what the skill matches on, which is not a rider on a documentation fix. - -No runtime behaviour changes. The one non-documentation change is the -`databaseUrl?: never` field on `EncryptedSupabaseWasmOptions`, which is -type-level: it rejects at compile time a call that already threw at -construction. diff --git a/.changeset/supabase-skill-eql-305.md b/.changeset/supabase-skill-eql-305.md deleted file mode 100644 index 8f65699f7..000000000 --- a/.changeset/supabase-skill-eql-305.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'stash': patch ---- - -Update the bundled agent skills for eql-3.0.5. `skills/stash-supabase` -re-states the PostgREST query-domain limitations against 3.0.5 (unchanged in -substance — the typed `eql_v3.query_*` operand requirement still stands), and -`skills/stash-postgres` drops one of the two places it claimed the CLI pins -`@cipherstash/eql` to an exact version — a claim that stopped being true when -EQL moved in-tree. The second copy goes in the same release, with the rest of -that skill's EQL source and issue pointers. diff --git a/.changeset/usage-limit-refusal-guidance.md b/.changeset/usage-limit-refusal-guidance.md deleted file mode 100644 index 31eb32531..000000000 --- a/.changeset/usage-limit-refusal-guidance.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -'@cipherstash/stack': minor -'stash': minor ---- - -Surface CipherStash token-service refusals as typed diagnostics. - -`@cipherstash/stack` operation and initialization failures now carry -`authCode`, `help`, and `url` from stack-auth. The message remains stack-auth's -original diagnostic message; Stack does not copy or rewrite its instructions. -Callers can branch on `USAGE_LIMIT_EXCEEDED` or `ORG_NOT_PROVISIONED`, render -`help`, and link to `url`. - -`LockContext.identify()` also recognizes those two codes on a genuine CTS -`402`, while declining malformed or unknown responses. Legacy valid JSON -responses without `cs_code` retain the historical usage-limit classification. - -`stash auth login` and `stash env` now consume `@cipherstash/auth` 0.44.0's -typed failures. They print the upstream diagnostic guidance, preserve its URL, -avoid suggesting another login for terminal account refusals, and expose -terminal codes on the JSON stream. The JSON error envelope gains an optional -`hint` for the upstream guidance. diff --git a/.changeset/wasm-inline-client-key-not-browser-safe.md b/.changeset/wasm-inline-client-key-not-browser-safe.md deleted file mode 100644 index 995bc5937..000000000 --- a/.changeset/wasm-inline-client-key-not-browser-safe.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -'@cipherstash/stack': patch -'stash': patch ---- - -Document that `@cipherstash/stack/wasm-inline` is server-side only, and pin the -reason against the core. - -`WasmClientConfig` requires `clientId` and `clientKey` on every auth arm, -including the `authStrategy` (OIDC federation) arm. That read like an -over-declaration the SDK could relax — if federation alone sufficed, a browser -could hold a client without a workspace secret. It cannot. The core requires -both fields regardless of strategy, and loads `clientKey` as encryption key -material *before* it ever calls the auth strategy. Since `clientKey` is a -workspace secret, no configuration of this entry belongs in a browser bundle — -which is why this entry has no `browser` export condition, and will not get one -until the core changes. - -No behaviour change. The types and runtime are unchanged; what changes is that -the constraint is now stated where callers meet it — `WasmClientConfig`, the -`stash-edge` skill, the `stash-encryption` entry-point table, and, where this -entry had been described as browser-capable, the `stash-supabase` skill and -the `supabase-worker` example — and enforced by contract tests that run -against the real WASM core instead of the mocks and stubs the rest of the wasm -suite uses. diff --git a/.changeset/wasm-inline-type-identity.md b/.changeset/wasm-inline-type-identity.md deleted file mode 100644 index 06ec075e6..000000000 --- a/.changeset/wasm-inline-type-identity.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -'@cipherstash/stack': patch -'@cipherstash/stack-supabase': patch -'stash': patch ---- - -Fix: a schema authored with `encryptedTable`/`types` from -`@cipherstash/stack/wasm-inline` was a compile error wherever an EQL v3 table was -expected — `encryptedSupabase`'s `schemas`, the Drizzle helpers, Prisma Next, the -native `Encryption` — and native-authored tables were rejected by the WASM -`Encryption`, with `Types have separate declarations of a private property -'columnName'`. The two entries shipped separately-emitted copies of every column -class, and TypeScript compares classes with private members by declaration -origin. The runtime was never affected, which made `as any` the tempting fix. - -Every entry now resolves one declaration, so one schema module can be shared -between a Node server and an Edge Function in either direction. `./wasm-inline` -keeps its ESM-only shape. diff --git a/e2e/CHANGELOG.md b/e2e/CHANGELOG.md index 099fe996c..46c0f1698 100644 --- a/e2e/CHANGELOG.md +++ b/e2e/CHANGELOG.md @@ -1,5 +1,27 @@ # @cipherstash/e2e +## 0.0.6 + +### Patch Changes + +- Updated dependencies [3311039] +- Updated dependencies [801868d] +- Updated dependencies [ac46a5a] +- Updated dependencies [4422d5c] +- Updated dependencies [78ab894] +- Updated dependencies [ad033df] +- Updated dependencies [f7488e9] +- Updated dependencies [4422d5c] +- Updated dependencies [24f7b2c] +- Updated dependencies [e36f1a0] +- Updated dependencies [ba37039] +- Updated dependencies [b04ea2f] +- Updated dependencies [518abfd] +- Updated dependencies [c1bf387] + - stash@1.2.0 + - @cipherstash/wizard@1.2.0 + - @cipherstash/stack@1.2.0 + ## 0.0.5 ### Patch Changes diff --git a/e2e/package.json b/e2e/package.json index 03d3bf485..d8717521e 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "@cipherstash/e2e", - "version": "0.0.5", + "version": "0.0.6", "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 0f978233b..1d0e08a1c 100644 --- a/examples/basic/CHANGELOG.md +++ b/examples/basic/CHANGELOG.md @@ -1,5 +1,15 @@ # @cipherstash/basic-example +## 1.2.17 + +### Patch Changes + +- Updated dependencies [b04ea2f] +- Updated dependencies [518abfd] +- Updated dependencies [c1bf387] + - @cipherstash/stack@1.2.0 + - @cipherstash/stack-drizzle@1.2.0 + ## 1.2.16 ### Patch Changes diff --git a/examples/basic/package.json b/examples/basic/package.json index f2975b41f..38056359b 100644 --- a/examples/basic/package.json +++ b/examples/basic/package.json @@ -1,7 +1,7 @@ { "name": "@cipherstash/basic-example", "private": true, - "version": "1.2.16", + "version": "1.2.17", "type": "module", "scripts": { "start": "tsx index.ts", diff --git a/examples/prisma/CHANGELOG.md b/examples/prisma/CHANGELOG.md index e1f8a9ca6..5495f573d 100644 --- a/examples/prisma/CHANGELOG.md +++ b/examples/prisma/CHANGELOG.md @@ -1,5 +1,19 @@ # @cipherstash/prisma-next-example +## 0.1.3 + +### Patch Changes + +- Updated dependencies [ba37039] +- Updated dependencies [78ab894] +- Updated dependencies [4422d5c] +- Updated dependencies [c604028] +- Updated dependencies [b04ea2f] +- Updated dependencies [518abfd] +- Updated dependencies [c1bf387] + - @cipherstash/stack-prisma@1.2.0 + - @cipherstash/stack@1.2.0 + ## 0.1.2 ### Patch Changes diff --git a/examples/prisma/package.json b/examples/prisma/package.json index 53e28833b..6c0f96e9a 100644 --- a/examples/prisma/package.json +++ b/examples/prisma/package.json @@ -1,7 +1,7 @@ { "name": "@cipherstash/prisma-example", "private": true, - "version": "0.1.2", + "version": "0.1.3", "description": "End-to-end example of @cipherstash/stack-prisma: 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 87d5b7a4b..cc0de5723 100644 --- a/packages/bench/CHANGELOG.md +++ b/packages/bench/CHANGELOG.md @@ -1,5 +1,15 @@ # @cipherstash/bench +## 0.0.8 + +### Patch Changes + +- Updated dependencies [b04ea2f] +- Updated dependencies [518abfd] +- Updated dependencies [c1bf387] + - @cipherstash/stack@1.2.0 + - @cipherstash/stack-drizzle@1.2.0 + ## 0.0.7 ### Patch Changes diff --git a/packages/bench/package.json b/packages/bench/package.json index 56068c2ef..809e21efa 100644 --- a/packages/bench/package.json +++ b/packages/bench/package.json @@ -1,6 +1,6 @@ { "name": "@cipherstash/bench", - "version": "0.0.7", + "version": "0.0.8", "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 1401bdda2..5c4b2ad9d 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,402 @@ # stash +## 1.2.0 + +### Minor Changes + +- f7488e9: Preserve encrypted data and reconstruct functional indexes when reinstalling EQL v3, while refusing unsupported external dependencies before mutation. + + `stash eql install` and `stash eql upgrade` now capture dependent functional + indexes before replacing the disposable EQL schemas, then restore and verify + their definitions, clustering, replica-identity role, comments, explicit + statistics targets, and health in the same transaction. A + reconstruction failure rolls the replacement back. + PostgreSQL derives index ownership from the table owner, so reinstall verifies + the resulting owner and rolls back on a mismatch rather than independently + restoring ownership. + Unsupported dependencies—including views, policies, constraints, and + partitioned indexes—are named and refused before mutation. + + Reinstall remains a maintenance-window operation: its advisory lock serializes + `stash` lifecycle commands, not unrelated database DDL. Generated EQL migrations + contain the raw bundle and do not include these reinstall protections. + +- b04ea2f: Surface CipherStash token-service refusals as typed diagnostics. + + `@cipherstash/stack` operation and initialization failures now carry + `authCode`, `help`, and `url` from stack-auth. The message remains stack-auth's + original diagnostic message; Stack does not copy or rewrite its instructions. + Callers can branch on `USAGE_LIMIT_EXCEEDED` or `ORG_NOT_PROVISIONED`, render + `help`, and link to `url`. + + `LockContext.identify()` also recognizes those two codes on a genuine CTS + `402`, while declining malformed or unknown responses. Legacy valid JSON + responses without `cs_code` retain the historical usage-limit classification. + + `stash auth login` and `stash env` now consume `@cipherstash/auth` 0.44.0's + typed failures. They print the upstream diagnostic guidance, preserve its URL, + avoid suggesting another login for terminal account refusals, and expose + terminal codes on the JSON stream. The JSON error envelope gains an optional + `hint` for the upstream guidance. + +### Patch Changes + +- 3311039: Clarify the bundled supply-chain guidance for non-publishing workload identity + federation. OIDC holders are now classified separately from registry + publishers, with each exchange and its repository permissions reviewed + explicitly. +- 801868d: Verify the EQL install SQL against its release digest before running it. + + `stash eql install` reads the EQL v3 bundle from the resolved + `@cipherstash/eql` in your `node_modules` and executes it against your + database. That read was a bare `readFileSync` — nothing checked that the bytes + on disk were the bundle the resolved release actually ships. A corrupt, + partially-updated, or tampered package installed silently: the database ended + up carrying SQL the version it reports does not define, and the CLI printed + "EQL extensions installed." + + The CLI now hashes the bundle and compares it to `installSqlSha256` from the + release manifest that ships alongside it, and **refuses** on a mismatch. The + error names the expected digest, the actual digest, the resolved file path and + the EQL version, so the remedy is visible rather than inferred. Verification + happens before any database connection is opened, so a refusal means nothing + was attempted — not that something was rolled back. + + The check covers all three paths that read the bundle: `stash eql install`, + the SQL embedded by `stash eql migration --drizzle` / `--supabase`, and the + expected-surface baseline `stash eql verify` compares your database against. + `@cipherstash/stack-prisma` has verified against this same digest since its v3 + migrations landed; this brings the CLI in line. + + No healthy install is affected — the SQL and its manifest are produced by the + same build of `@cipherstash/eql`, so a mismatch only ever means a broken + dependency tree. + + `skills/stash-cli` documents the new pre-flight alongside the existing + post-install surface check, so an agent reading it does not report a digest + refusal as a failed install. + +- ac46a5a: `stash doctor` no longer reports "All checks passed." when `@cipherstash/stack` + is absent. The package is an optional peer, so running `doctor` before `stash +init` skips the encryption check entirely — the row already said so, but the + outro claimed a pass for a check that never ran. It now ends with "stash doctor + could not run every check.", the same line an unprobeable install gets, and + still exits 0: an absent optional package is recoverable, not a failure. +- 4422d5c: Pin the packed `@cipherstash/eql` dependency to an exact version, closing a + route by which an installed EQL bundle could drift ahead of the code built + against it. + + Both packages declared `"@cipherstash/eql": "workspace:^"` under + `dependencies`. In this workspace that resolves in-tree either way, so nothing + in development or CI could see a difference — but the two specifiers do not + pack the same. pnpm rewrites the protocol when it builds the tarball a customer + actually installs: + + "workspace:^" packs as "^3.0.5" + "workspace:*" packs as "3.0.5" + + The caret is the problem. `@cipherstash/eql` is still published from + `cipherstash/encrypt-query-language` until the publisher repoint, so a 3.0.x can + reach npm without passing through this repository at all — and `^3.0.5` accepts + it. A customer installing `stash` or `@cipherstash/stack-prisma` would then get + SQL that STORES and queries encrypted payloads at one version, while + `@cipherstash/stack`'s v3 domain types (which EMIT those payloads) and + `stack-prisma`'s baked migrations stayed frozen at the version this repo built + and tested against. The two halves of EQL are released in lockstep precisely + because that skew does not fail at install or in CI — it fails in a database. + + `workspace:*` is the only form that closes it. A literal `"3.0.5"` would be an + exact pin too, but it is a registry pin: `pnpm run lint:eql-pins` rejects it, + because resolving EQL from a registry rather than from this repo is the same + drift one layer up. + + No API, behaviour or SQL changes. What changes is the dependency range in the + published tarballs, and only in the narrowing direction — the version resolved + today is the version that was already being resolved. Nothing needs to be done + on upgrade. + + `@cipherstash/stack` declares the same dependency under `devDependencies` and + is deliberately left alone: pnpm rewrites that range too, but no consumer of the + package ever resolves it. + +- 78ab894: `skills/stash-prisma`: the stale-`migrations/cipherstash/` table now describes + eql-3.0.6. The baseline bakes 3.0.6, 1.1.x shipped 3.0.4 as 1.0.0 did, and an + existing database upgrades through both the 3.0.5 and 3.0.6 edges. +- ad033df: `skills/stash-prisma` now documents the re-plan step that follows an + `@cipherstash/stack-prisma` upgrade: `rm -rf migrations/cipherstash && npx +prisma-next migration plan`, why only `migration plan` vendors new migration + packages, and the exact `db init` refusal a stale vendored directory produces on + a fresh database (`Operation cipherstash.upgrade-eql-v3-bundle-3.0.5 has class +"data" which is not allowed by policy.`). + + The package README already carried this; the skill did not — and the skill is + what ships inside the `stash` tarball and gets copied into a user's + `.claude/skills/`, so an agent driving the upgrade hit the refusal with no route + out of it. `packages/stack-prisma/test/v3/stale-vendored-space.test.ts` now pins + both files to the planner's real message so they cannot drift apart again. + +- 4422d5c: Correct two things the bundled agent skills were telling customers wrongly + about EQL. + + **`skills/stash-postgres` pointed at the wrong repository.** EQL's source now + lives in `cipherstash/stack` under `packages/eql/`, and that is where operator + gaps and domain-level bugs are filed; only _publishing_ still happens from + `cipherstash/encrypt-query-language`, which the skill continues to say. The + skill also cited "the EQL skill" as a source of truth that "ships from + `encrypt-query-language` alongside the bundle" — no such skill ships from + either repository, so the reference is gone and the remaining three sources + (the generated types, the install SQL, and `SELECT eql_v3.version()`) are + renumbered. + + **And it claimed the CLI pins an exact `@cipherstash/eql` version, "so a + database is only ever on one bundle."** Neither half holds: the CLI depends on + the workspace package rather than a pinned literal, and a database is on + whatever bundle was last applied to it — the Prisma Next adapter installs and + upgrades the bundle through its own migrations without involving the CLI at + all. Replaced with the guarantee that does hold: one `stash` release carries + one resolved bundle, and the database is the authority on which bundle it has. + + **`skills/stash-prisma` hands out the functional-index recipe without saying an + EQL upgrade destroys it.** Installing a bundle begins with `DROP SCHEMA IF +EXISTS eql_v3 CASCADE`, which cascade-drops every index over an `eql_v3.*` + extractor — the PSL expression indexes Prisma Next 0.17 introduced and any + `rawSql` index DDL alike; queries keep working and silently sequential-scan. + Because an applied migration is never replayed, recovery is a NEW one: a PSL + expression index has to change its `name:` (the physical name carries a content + hash of the expression, so re-declaring the same one plans no work), and a + `rawSql` recovery op needs a new `id`. Said where the recipe is given, pointing + at `stash-indexing` for the mechanism and at + [cipherstash/stack#918](https://github.com/cipherstash/stack/issues/918) for + capturing and restoring them automatically. + +- 24f7b2c: Document how to express an absolute-value bucket over an EQL v3 ordered + column by decomposing it into positive and negative signed ranges. The + `stash-postgres` recipe encrypts all four bounds, shows the reversed negative + inequalities, and states the required bound validation. +- e36f1a0: Stop telling customers the Supabase wrapper cannot run in a Worker. + + `@cipherstash/stack-supabase` has shipped two entry points since #912. The + package root introspects your database and runs on Node; the `wasm-inline` + entry carries the WASM engine, takes declared `schemas` instead of + introspecting, and runs on Deno, Supabase Edge Functions and Cloudflare + Workers. Introspection was the only thing that needed a Postgres socket, and + that entry does not do it. + + Two shipping documents were never updated and still described the state before + that change: + + - `packages/stack-supabase/README.md` said "the factory cannot run in an edge + Worker or the browser" and did not mention the `wasm-inline` entry anywhere + in the file. This is the npm package page. + - `skills/stash-supabase/SKILL.md` said the same thing in its setup section. + The skill ships inside the `stash` tarball, and `stash init` copies it into + the customer's own repository, where their coding agent reads it as + instruction. The one correct mention of the edge entry was in a callout near + the top that the setup steps never pointed at, so a reader following the + setup never learned the second entry existed. + + The population this misled hardest is the one that needs the edge entry most: + server code on Lovable, v0, Bolt and Replit runs on an edge runtime, which is + exactly the case `wasm-inline` was built for and exactly the case these + documents called impossible. + + Both files now describe both entries. The README gains an "Edge runtimes" + section with the call shape; the skill gains a fifth setup step with the same, + and the introspection paragraph now scopes its restriction to the native entry + and points there. Both name the four ways the edge entry differs: `schemas` is + required, `config` is required, `databaseUrl` is refused, and + `.withLockContext()` / `.audit()` throw rather than silently dropping an + identity claim (#797). + + The **browser** half of the old sentence was correct and is kept, with the + reason now given: the WASM client requires a workspace `clientKey` on every + authentication path, so a browser build would ship the key with it (#804). + + Four claims that were wrong in the same neighbourhood are corrected while we + are here, three of them pre-dating this change: + + - **`skills/stash-managed-platforms/SKILL.md` shipped a snippet that does not + compile.** It authored the `schemas` object from `@cipherstash/stack/wasm-inline` + and handed it to `encryptedSupabase` from `@cipherstash/stack-supabase/wasm-inline`. + The adapter types `schemas` from `@cipherstash/stack/eql/v3`, and the two + entries ship independent declarations of the column classes whose private + `columnName` field TypeScript compares nominally — so `tsc` rejects it while + the code runs perfectly, which is why nobody noticed. The schema import now + comes from `eql/v3`, and a new guard + (`scripts/__tests__/skills-supabase-edge-schema-entry.test.mjs`) fails if any + shipped document pairs the two again. + - **`skills/stash-edge/SKILL.md` is what produced that snippet.** Its "Schema + Modules Do Not Cross Entries" section told edge projects to author schemas + from `@cipherstash/stack/wasm-inline` with no carve-out. The rule is really + "author against the entry whose _client type_ consumes the schema": a raw + `Encryption` client from `wasm-inline` wants `wasm-inline` tables, but the + Supabase adapter wants `eql/v3` tables on both of its entries, WASM engine or + not. The section now says so, and the "The Supabase adapter has its own edge + entry" note points at it. + - **`skills/stash-supabase/SKILL.md` described the wrong failure mode for a + missing declaration.** Omitting `schemas` on the edge entry cannot produce a + no-column client — it is non-optional on the type and throws at construction — + and an undeclared _table_ throws rather than passing through unencrypted. The + hazard is an undeclared **column** on a declared table, which is treated as + plaintext; the bullet now says that, along with the one thing that limits it + (a plaintext write to an `eql_v3_*` column fails the domain CHECK, though a + NULL still passes) and the fact that the native entry's warning about + unverified declarations is gated on the introspector and so never fires + there. Reads get no equivalent backstop, and the bullet now says so: the + `select('*')` refusal looks like one, but a query awaited with no + `.select()` at all takes the raw-`*` branch in `query-builder.ts` and + returns every column undecrypted. + - **"Undeclared tables behave exactly as with no `schemas` at all" was false on + the native entry too.** Introspection is gated on a resolved database URL, + not on the absence of `schemas`, and an ambient `DATABASE_URL` is + deliberately ignored once tables are declared. The statement holds only when + `databaseUrl` is passed _alongside_ `schemas`, which is what it now says. + + `config` is corrected everywhere that called all four `CS_*` values mandatory: + the README, `skills/stash-supabase/SKILL.md`, and — in `skills/stash-edge/SKILL.md` + — its frontmatter description, its Credentials section, its troubleshooting + advice, and the native-vs-WASM comparison table. That skill already contradicted + itself, since its own `config.authStrategy` example passes two values, not four. + The same sentence in the `EncryptedSupabaseWasmOptions` doc comment + (`packages/stack-supabase/src/wasm-inline.ts`) is fixed too, comment-only. Only + `clientId` and `clientKey` are always required. Beyond them the config is a union — the + access-key path adds `workspaceCrn` + `accessKey`, and the strategy path takes + a pre-built `config.authStrategy` and makes `workspaceCrn` optional, because a + built strategy already carries the CRN. `OidcFederationStrategy` is re-exported + from `@cipherstash/stack/wasm-inline`, so authenticating as the end user works + on the edge; what does not work is binding data to that user, which stays + called out in its own `.withLockContext()` bullet. + + Tests now anchor the corrected claims against the code rather than against + prose. `packages/stack-supabase/__tests__/supabase-wasm-config.test-d.ts` asserts + at the type level that the edge `config` accepts both the access-key arm and a + strategy-only arm without `workspaceCrn`, and rejects `clientId` + `clientKey` + alone. `supabase-declared-mode.test.ts` gains a case pinning the real hazard: an + undeclared column on a declared table reaches PostgREST as plaintext on insert, + update and filter, and is absent from the decrypt call. + + Review found four more, one of them a change to a published type: + + - **`databaseUrl` was only refused for callers who wrote the options inline.** + `EncryptedSupabaseWasmOptions` left the field out, and omission is policed by + excess-property checking, which fires on fresh object literals alone. An + options object assembled as a `const` and passed by variable — which is what + a Node-to-edge port actually holds — type-checked clean and reached the + construction-time throw instead, from documents saying the type checker + enforced it. The field is now declared `databaseUrl?: never`, mirroring + `WasmClientConfig.eqlVersion?: never` in `@cipherstash/stack`, which exists + for the identical reason one package along. The runtime throw stays as the + backstop for plain JS. New type tests cover the inline and by-variable + shapes on both call forms, plus a positive control that the same options + object still compiles once `databaseUrl` is dropped. + - **The `select('*')` correction had landed in only one of its two shipped + copies.** `skills/stash-supabase/SKILL.md` carried it; + `skills/stash-managed-platforms/SKILL.md`, edited in the same change, still + framed declared mode as giving things up "loudly rather than silently" over a + bullet naming the refusal — precisely the inference the correction exists to + kill. That skill is read as instruction by an agent on Lovable, v0, Bolt or + Replit, and the failure it mispromised is silent: raw EQL payloads returned + as `data`, no error. The wording is carried across, and a new guard + (`scripts/__tests__/skills-select-star-not-a-read-backstop.test.mjs`) fails + if any shipped document states the refusal without the caveat in the same + section. Two copies of one fact drift the moment one of them is edited, and + no reviewer diff shows the copy nobody touched. + - **"Everything after construction is the same wrapper" was false in the first + way a reader hits it.** Both `packages/stack-supabase/README.md` and + `skills/stash-supabase/SKILL.md` said `from()`, the filters and the response + shape are identical across the two entries — the README saying so twenty-five + lines under a paragraph telling the same reader `select('*')` just works. The + edge entry is always in declared mode, where `select('*')` is refused and + `from()` on an undeclared table throws. Both sentences now name the two + exceptions, so the quick-start snippet the section tells you to port no + longer arrives with a promise it breaks. + - **The ambient-`DATABASE_URL` warning cannot fire on the edge entry.** + `skills/stash-managed-platforms/SKILL.md` said an ambient `DATABASE_URL` is + ignored when `schemas` are passed, "with a warning that the declaration is + unverified" — in a section whose subject is `wasm-inline`. Both the ambient + read and the warning are gated on the introspector, which that build does not + have, so nothing there ever tells you a declaration is incomplete. Now scoped + to Node, with the edge case stated. The sentence immediately above it had the + same fault and is fixed with it: the ⚠️ callout on undeclared columns offered + "pass `databaseUrl` so introspection fills the gaps" as the remedy, inside a + section about the entry that refuses `databaseUrl` — and it is the remedy a + Lovable or Replit agent, which has only the edge entry, would have reached + for. It now says introspection is unavailable there and what to do instead. + + `@cipherstash/wizard` is bumped alongside `stash` because `skills/` ships in + both tarballs — `packages/wizard/tsup.config.ts` copies it into `dist/skills` + and `package.json` lists that under `files`. Without the bump the published + wizard keeps shipping the old text until some unrelated change moves its + version. + + One more wording correction, of the kind #952 fixed in this package's `.d.ts`: + `packages/stack-supabase/README.md` and `skills/stash-supabase/SKILL.md` both + derived "runs on Node only" from introspection — "introspection needs a direct + Postgres connection, **so** … this entry runs on Node only". Introspection is + not the cause. The entry binds the native engine, so it is Node-only whether or + not you declare `schemas`; a reader who took the stated cause at face value + would conclude that declaring tables makes the root entry edge-capable, which is + the exact wrong turn the `wasm-inline` entry exists to prevent. Both sentences + now attribute the restriction to the engine and say that declaring `schemas` + does not move it. Both files are enrolled in #952's + `scripts/__tests__/supabase-runtime-claims.test.mjs`, which is what its own + comment said to do once this branch stopped rewriting the same lines — the + README and this skill are the two copies that reach a customer, and the guard + now fails if either grows the claim back. `skills/stash-managed-platforms/SKILL.md` + stays out, with the reason written down: its causal claims are correct, but + rewording the unqualified "Worker" in its frontmatter `description` changes + what the skill matches on, which is not a rider on a documentation fix. + + No runtime behaviour changes. The one non-documentation change is the + `databaseUrl?: never` field on `EncryptedSupabaseWasmOptions`, which is + type-level: it rejects at compile time a call that already threw at + construction. + +- ba37039: Update the bundled agent skills for eql-3.0.5. `skills/stash-supabase` + re-states the PostgREST query-domain limitations against 3.0.5 (unchanged in + substance — the typed `eql_v3.query_*` operand requirement still stands), and + `skills/stash-postgres` drops one of the two places it claimed the CLI pins + `@cipherstash/eql` to an exact version — a claim that stopped being true when + EQL moved in-tree. The second copy goes in the same release, with the rest of + that skill's EQL source and issue pointers. +- 518abfd: Document that `@cipherstash/stack/wasm-inline` is server-side only, and pin the + reason against the core. + + `WasmClientConfig` requires `clientId` and `clientKey` on every auth arm, + including the `authStrategy` (OIDC federation) arm. That read like an + over-declaration the SDK could relax — if federation alone sufficed, a browser + could hold a client without a workspace secret. It cannot. The core requires + both fields regardless of strategy, and loads `clientKey` as encryption key + material _before_ it ever calls the auth strategy. Since `clientKey` is a + workspace secret, no configuration of this entry belongs in a browser bundle — + which is why this entry has no `browser` export condition, and will not get one + until the core changes. + + No behaviour change. The types and runtime are unchanged; what changes is that + the constraint is now stated where callers meet it — `WasmClientConfig`, the + `stash-edge` skill, the `stash-encryption` entry-point table, and, where this + entry had been described as browser-capable, the `stash-supabase` skill and + the `supabase-worker` example — and enforced by contract tests that run + against the real WASM core instead of the mocks and stubs the rest of the wasm + suite uses. + +- c1bf387: Fix: a schema authored with `encryptedTable`/`types` from + `@cipherstash/stack/wasm-inline` was a compile error wherever an EQL v3 table was + expected — `encryptedSupabase`'s `schemas`, the Drizzle helpers, Prisma Next, the + native `Encryption` — and native-authored tables were rejected by the WASM + `Encryption`, with `Types have separate declarations of a private property +'columnName'`. The two entries shipped separately-emitted copies of every column + class, and TypeScript compares classes with private members by declaration + origin. The runtime was never affected, which made `as any` the tempting fix. + + Every entry now resolves one declaration, so one schema module can be shared + between a Node server and an Edge Function in either direction. `./wasm-inline` + keeps its ESM-only shape. + +- Updated dependencies [e52d331] + - @cipherstash/eql@3.0.6 + - @cipherstash/migrate@1.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 881f69626..6ee7a3cda 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "stash", - "version": "1.1.1", + "version": "1.2.0", "description": "CipherStash CLI — the one stash command for auth, init, encryption schema, database setup, and secrets.", "repository": { "type": "git", diff --git a/packages/eql/Cargo.lock b/packages/eql/Cargo.lock index 06fafe2d3..f80f2bab7 100644 --- a/packages/eql/Cargo.lock +++ b/packages/eql/Cargo.lock @@ -1129,7 +1129,7 @@ dependencies = [ [[package]] name = "eql-bindings" -version = "3.0.5" +version = "3.0.6" dependencies = [ "eql-domains", "schemars", diff --git a/packages/eql/crates/eql-bindings/Cargo.toml b/packages/eql/crates/eql-bindings/Cargo.toml index 5c443d5cc..dea9d5db2 100644 --- a/packages/eql/crates/eql-bindings/Cargo.toml +++ b/packages/eql/crates/eql-bindings/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "eql-bindings" -version = "3.0.5" +version = "3.0.6" edition = "2021" description = "Canonical wire types for EQL payloads — single source of truth for Rust, TypeScript (ts-rs), and JSON Schema (schemars)." # crates.io metadata. `license` is REQUIRED by crates.io — publish fails without diff --git a/packages/eql/crates/eql-bindings/sql/cipherstash-encrypt.sql b/packages/eql/crates/eql-bindings/sql/cipherstash-encrypt.sql index 88c460300..4b40deee5 100644 --- a/packages/eql/crates/eql-bindings/sql/cipherstash-encrypt.sql +++ b/packages/eql/crates/eql-bindings/sql/cipherstash-encrypt.sql @@ -59469,7 +59469,7 @@ DROP FUNCTION IF EXISTS eql_v3.version(); --! @brief EQL version reporting (self-contained eql_v3 surface) --! --! This file is auto-generated from src/v3/version.template during build. ---! The 3.0.5 placeholder is replaced with the actual release +--! The 3.0.6 placeholder is replaced with the actual release --! version (bare semver, e.g. "3.0.0") supplied via `mise run build --version`, --! or "DEV" for development builds. @@ -59488,14 +59488,14 @@ CREATE FUNCTION eql_v3.version() RETURNS text IMMUTABLE STRICT PARALLEL SAFE AS $$ - SELECT '3.0.5'; + SELECT '3.0.6'; $$ LANGUAGE SQL; --! @brief Schema-level version marker for obj_description() discoverability --! --! Mirrors eql_v3.version() as a comment on the schema so the installed --! version can also be read via obj_description('eql_v3'::regnamespace). -COMMENT ON SCHEMA eql_v3 IS '3.0.5'; +COMMENT ON SCHEMA eql_v3 IS '3.0.6'; --! @file pin_search_path_v3.sql --! @brief Post-install: pin search_path on every eql_v3.* function. --! diff --git a/packages/eql/crates/eql-bindings/sql/release-manifest.json b/packages/eql/crates/eql-bindings/sql/release-manifest.json index f0203fcc7..cd386a01c 100644 --- a/packages/eql/crates/eql-bindings/sql/release-manifest.json +++ b/packages/eql/crates/eql-bindings/sql/release-manifest.json @@ -1,6 +1,6 @@ { - "eqlVersion": "3.0.5", + "eqlVersion": "3.0.6", "schemaVersion": 3, - "installSqlSha256": "accde0030b8f356af616175640635f67661d51aa900624b7fb0fb059e8115048", + "installSqlSha256": "9b6dab789248f911707f7c1c468c5d455987b47aac4a30fc421bc813073fffb1", "uninstallSqlSha256": "b1b5131b8175c5d04da9ada108d25c81c5772b15fad79a6c419ebb32d18c60a9" } diff --git a/packages/eql/packages/eql/CHANGELOG.md b/packages/eql/packages/eql/CHANGELOG.md index 1942b9676..6f0c535d2 100644 --- a/packages/eql/packages/eql/CHANGELOG.md +++ b/packages/eql/packages/eql/CHANGELOG.md @@ -1,5 +1,11 @@ # @cipherstash/eql +## 3.0.6 + +### Patch Changes + +- e52d331: Point `repository`, `repository.directory`, and `bugs.url` at `cipherstash/stack` instead of the archived `cipherstash/encrypt-query-language`. Purely metadata — no behaviour change — but required before npm's trusted publishing (already repointed at `cipherstash/stack`) can accept a release: npm rejects a publish whose manifest `repository.url` doesn't match the publishing repository. + ## 3.0.5 ### Patch Changes @@ -20,7 +26,7 @@ pressure. See [U-001](../../docs/upgrading/v3.0.5.md#u-001-the-containment-implementation-is-renamed). **The thing in this release that does have consequences is not the rename.** - The installer opens with `DROP SCHEMA IF EXISTS eql_v3 CASCADE`, so *every* EQL + The installer opens with `DROP SCHEMA IF EXISTS eql_v3 CASCADE`, so _every_ EQL install drops grants, functional indexes and dependent views — true of every release, not just this one, and now the only item here with operational weight. See [U-002](../../docs/upgrading/v3.0.5.md#u-002-grants-and-dependent-objects-do-not-survive-an-install). @@ -49,6 +55,7 @@ re-synced to that release so the bundle here is byte-identical to the published one — `installSqlSha256: accde0030…`. The generated entry below is upstream's own changeset for the same release. + - 4c2bb92: **`eql_v3.ste_vec_contains` is renamed to `eql_v3.jsonb_document_contains`.** This consolidates the last `ste_vec_*`-named public object into the `jsonb_*` family, matching the earlier renames of the SteVec entry/query surface (`jsonb_entry`, diff --git a/packages/eql/packages/eql/package.json b/packages/eql/packages/eql/package.json index 36db8bd81..ae87ef23b 100644 --- a/packages/eql/packages/eql/package.json +++ b/packages/eql/packages/eql/package.json @@ -1,6 +1,6 @@ { "name": "@cipherstash/eql", - "version": "3.0.5", + "version": "3.0.6", "description": "Canonical EQL v3 wire types, JSON schemas, and SQL bundle.", "keywords": [ "eql", diff --git a/packages/eql/packages/eql/sql/cipherstash-encrypt.sql b/packages/eql/packages/eql/sql/cipherstash-encrypt.sql index 88c460300..4b40deee5 100644 --- a/packages/eql/packages/eql/sql/cipherstash-encrypt.sql +++ b/packages/eql/packages/eql/sql/cipherstash-encrypt.sql @@ -59469,7 +59469,7 @@ DROP FUNCTION IF EXISTS eql_v3.version(); --! @brief EQL version reporting (self-contained eql_v3 surface) --! --! This file is auto-generated from src/v3/version.template during build. ---! The 3.0.5 placeholder is replaced with the actual release +--! The 3.0.6 placeholder is replaced with the actual release --! version (bare semver, e.g. "3.0.0") supplied via `mise run build --version`, --! or "DEV" for development builds. @@ -59488,14 +59488,14 @@ CREATE FUNCTION eql_v3.version() RETURNS text IMMUTABLE STRICT PARALLEL SAFE AS $$ - SELECT '3.0.5'; + SELECT '3.0.6'; $$ LANGUAGE SQL; --! @brief Schema-level version marker for obj_description() discoverability --! --! Mirrors eql_v3.version() as a comment on the schema so the installed --! version can also be read via obj_description('eql_v3'::regnamespace). -COMMENT ON SCHEMA eql_v3 IS '3.0.5'; +COMMENT ON SCHEMA eql_v3 IS '3.0.6'; --! @file pin_search_path_v3.sql --! @brief Post-install: pin search_path on every eql_v3.* function. --! diff --git a/packages/eql/packages/eql/sql/release-manifest.json b/packages/eql/packages/eql/sql/release-manifest.json index f0203fcc7..cd386a01c 100644 --- a/packages/eql/packages/eql/sql/release-manifest.json +++ b/packages/eql/packages/eql/sql/release-manifest.json @@ -1,6 +1,6 @@ { - "eqlVersion": "3.0.5", + "eqlVersion": "3.0.6", "schemaVersion": 3, - "installSqlSha256": "accde0030b8f356af616175640635f67661d51aa900624b7fb0fb059e8115048", + "installSqlSha256": "9b6dab789248f911707f7c1c468c5d455987b47aac4a30fc421bc813073fffb1", "uninstallSqlSha256": "b1b5131b8175c5d04da9ada108d25c81c5772b15fad79a6c419ebb32d18c60a9" } diff --git a/packages/eql/packages/eql/src/generated/release-manifest.ts b/packages/eql/packages/eql/src/generated/release-manifest.ts index 21c403c1c..30e8cd835 100644 --- a/packages/eql/packages/eql/src/generated/release-manifest.ts +++ b/packages/eql/packages/eql/src/generated/release-manifest.ts @@ -1,6 +1,6 @@ export const releaseManifest = { - eqlVersion: '3.0.5', + eqlVersion: '3.0.6', schemaVersion: 3, - installSqlSha256: 'accde0030b8f356af616175640635f67661d51aa900624b7fb0fb059e8115048', + installSqlSha256: '9b6dab789248f911707f7c1c468c5d455987b47aac4a30fc421bc813073fffb1', uninstallSqlSha256: 'b1b5131b8175c5d04da9ada108d25c81c5772b15fad79a6c419ebb32d18c60a9', } as const diff --git a/packages/nextjs/CHANGELOG.md b/packages/nextjs/CHANGELOG.md index cf588367b..028fd5306 100644 --- a/packages/nextjs/CHANGELOG.md +++ b/packages/nextjs/CHANGELOG.md @@ -1,5 +1,39 @@ # @cipherstash/nextjs +## 4.2.0 + +### Minor Changes + +- d3efdbd: Report what the CipherStash token service actually said when it refuses a token. + + `getCtsToken()` reported a non-2xx response as `Failed to fetch CTS token: ` and + nothing else. It read `statusText`, which is the empty string over HTTP/2 — so + the message ended at the colon — and it discarded the response body, taking any + refusal code with it. A billing refusal was indistinguishable from a bad token, + and the accompanying log said "contact support", which is the wrong advice for + an organisation that needs to upgrade a plan. + + The failure now names the status and quotes what the service returned, and the + refusal code is surfaced on a new optional `authCode` field of + `GetCtsTokenResponse` — `USAGE_LIMIT_EXCEEDED` for an organisation over its + allowance, `ORG_NOT_PROVISIONED` for one not registered with the usage system. + Both are terminal: retrying cannot clear either. Unknown `402` codes are + declined so a future payment-required response does not inherit the wrong + classification. + + The body is read as text exactly once and then parsed defensively, never with + `response.json()`. The two shapes are not the same shape: a `402` is JSON, while + every other failure from this endpoint is `text/plain` (a `401` is the bare + string `Authorization failed: InvalidToken`), and `.json()` on one of those + throws a `SyntaxError` that replaces the real failure with a parse error. A + response that is not a recognisable CipherStash refusal — a gateway or WAF + answering in front of the service — still reports its status and body rather + than being reported as a billing problem it is not. + + This package does not depend on `@cipherstash/stack`, so it carries the code + rather than a copy of that package's remedy text — look the remedy up from + `authCode` if you need to render one. + ## 4.1.2 ### Patch Changes diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 948e606ad..6f6b2529c 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@cipherstash/nextjs", - "version": "4.1.2", + "version": "4.2.0", "description": "Nextjs package for use with @cipherstash/stack", "keywords": [ "encrypted", diff --git a/packages/protect-ffi/CHANGELOG.md b/packages/protect-ffi/CHANGELOG.md index 9129d0cc5..5cfe7a32e 100644 --- a/packages/protect-ffi/CHANGELOG.md +++ b/packages/protect-ffi/CHANGELOG.md @@ -1,5 +1,72 @@ # Changelog +## 0.33.0 + +### Minor Changes + +- 8839d5a: Carry `stack-auth` diagnostics across the JavaScript boundary. Errors that + originate in `stack-auth` now expose `authCode`, `help`, and `url` on both the + native and WASM bindings, alongside protect-ffi's existing `code` field. + + The boundary remains deliberately thin: `Error::Auth` and `Error::ZeroKMS` are + transparent miette diagnostics, so stack-auth continues to own the message, + instructions, and destination URL. Protect-ffi only serializes those fields and + reads the stable auth code from the typed `AuthError`; it does not classify the + message or maintain its own remedy taxonomy. + + `getAuthErrorCode(err)` reads the new field and `ProtectAuthErrorCode` types it. + The auth taxonomy is separate from protect-ffi's closed `ProtectErrorCode` set. + +### Patch Changes + +- 8839d5a: Move the CipherStash client crates to `0.42.3` — `cipherstash-client`, + `cts-common`, `stack-auth` and `stack-profile`, which release in lockstep. + + This is the release that raises the usage-denial taxonomy. `stack-auth` gained + typed `UsageLimitExceeded` / `OrgNotProvisioned` errors with a `help` and a + `url` on each, a shared classifier for a `402` from any credential-issuance + path, and a 60-second sticky cache so a refused organisation stops re-issuing + the same doomed request at its own request rate. Together they are what makes + `authCode` on a failure report a billing refusal as one, rather than as a + generic server error a retry loop will hammer. + + It also carries a ZeroKMS change requiring `org_id` on every token. The client + side decodes claims without requiring it, so this is transparent here. + +- 4422d5c: Compile `eql-bindings` from this repository rather than from crates.io. + + The native binding pinned `eql-bindings = "=3.0.2"` from the registry. It now + resolves by path from `packages/eql/crates/eql-bindings`, which ships at 3.0.5 + alongside the `@cipherstash/eql` SQL bundle. + + **No behaviour change.** `eql-bindings` is the Rust half of EQL — it EMITS the + encrypted payloads that the SQL half STORES and queries — and its Rust source is + byte-identical across 3.0.2, 3.0.4 and 3.0.5 (`src/`, `bindings/` and `schema/` + compared directly). What 3.0.3 through 3.0.5 changed was SQL, carried on the + shared lockstep version number. So the payloads this binding produces are the + same bytes before and after; what moves is the version stamped on the crate + compiled into `index.node`, from 3.0.2 to 3.0.5. + + **Why it is worth a release anyway.** A registry pin let the two halves of EQL + drift apart silently. Nothing asserted they agreed: a mismatched pair compiles, + passes every suite, and fails in a database — because the failure is a payload + the installed SQL cannot read, which no unit test holds both sides of. Resolving + from the tree makes the skew unrepresentable: the emitter and the SQL are now + the same commit, and `pnpm run lint:eql-pins` fails any change that reintroduces + a registry pin on either. + + The flip was taken while it was a no-op deliberately. Waiting for the first + release where the two halves genuinely diverge would have turned a provenance + change into a behaviour change that had to be argued under credentialed test. + + Verified without credentials: `cargo build -p protect-ffi` clean, the crate test + suite green (310 passed) with `cargo fmt --check` clean, and a + `wasm32-unknown-unknown` build clean — the last of those being the target where a + cross-workspace path dependency would break first, since the EQL workspace never + otherwise builds for wasm32. + +- Bump `cipherstash-client`, `cts-common`, `stack-auth`, and `stack-profile` to 0.42.2, which moves the transitive `jsonwebtoken` dependency from 9.3.1 to 10.4.0, resolving [CVE-2026-25537](https://github.com/advisories/GHSA-h395-gr6q-cpjc) (a JWT claim-validation type-confusion bug that could allow bypassing `nbf`/`exp` checks). No API changes. + ## 0.32.0 ### Minor Changes @@ -122,7 +189,7 @@ clientKey: expected a hex-encoded key`. Re-encode as hex, or read the key - **A key an options object doesn't declare is now an error, not a silent drop.** Every options struct rejects unrecognised fields, naming the - offender — `` unknown field `clientId` `` — instead of discarding them on the + offender — ``unknown field `clientId` `` — instead of discarding them on the way in. A misspelling, a stale key, or a value in the wrong place fails loudly. ([#144]) @@ -156,8 +223,8 @@ clientKey: expected a hex-encoded key`. Re-encode as hex, or read the key `Object.defineProperty({enumerable: false})` is dropped. Neon has always been `JSON.stringify`, which is own-enumerable too. - **A misspelled _required_ field now reports it as missing, not unknown.** - `encrypt(client, {plaintext, column, tabel: 'users'})` says `` missing -field `table` `` and never names `tabel`; it used to say both. Serde's + `encrypt(client, {plaintext, column, tabel: 'users'})` says ``missing +field `table` `` and never names `tabel`; it used to say both. Serde's flatten path buffers the map and reports at its closing brace, which also drops the `expected one of ...` list from every rejection. Neon-only — the wasm path had no error to lose. diff --git a/packages/protect-ffi/Cargo.lock b/packages/protect-ffi/Cargo.lock index 9ea0210c7..dc409fec6 100644 --- a/packages/protect-ffi/Cargo.lock +++ b/packages/protect-ffi/Cargo.lock @@ -1058,7 +1058,7 @@ dependencies = [ [[package]] name = "eql-bindings" -version = "3.0.5" +version = "3.0.6" dependencies = [ "schemars", "serde", @@ -1079,7 +1079,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3024,7 +3024,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3369,7 +3369,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4400,7 +4400,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/packages/protect-ffi/integration-tests/CHANGELOG.md b/packages/protect-ffi/integration-tests/CHANGELOG.md new file mode 100644 index 000000000..7f2deaa1c --- /dev/null +++ b/packages/protect-ffi/integration-tests/CHANGELOG.md @@ -0,0 +1,11 @@ +# @cipherstash/ffi-integration-tests + +## 1.0.1 + +### Patch Changes + +- Updated dependencies [8839d5a] +- Updated dependencies [8839d5a] +- Updated dependencies [4422d5c] +- Updated dependencies + - @cipherstash/protect-ffi@0.33.0 diff --git a/packages/protect-ffi/integration-tests/package.json b/packages/protect-ffi/integration-tests/package.json index 8ece03acd..97620d469 100644 --- a/packages/protect-ffi/integration-tests/package.json +++ b/packages/protect-ffi/integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@cipherstash/ffi-integration-tests", - "version": "1.0.0", + "version": "1.0.1", "private": true, "description": "Live integration coverage for @cipherstash/protect-ffi — real ZeroKMS, real Postgres, EQL v2 and v3", "license": "MIT", diff --git a/packages/protect-ffi/package.json b/packages/protect-ffi/package.json index 0edef5ffa..7c6dccfa2 100644 --- a/packages/protect-ffi/package.json +++ b/packages/protect-ffi/package.json @@ -1,6 +1,6 @@ { "name": "@cipherstash/protect-ffi", - "version": "0.32.0", + "version": "0.33.0", "repository": { "type": "git", "url": "git+https://github.com/cipherstash/stack.git" diff --git a/packages/protect-ffi/platforms/darwin-arm64/CHANGELOG.md b/packages/protect-ffi/platforms/darwin-arm64/CHANGELOG.md index f29e8e2a3..807ce0ec6 100644 --- a/packages/protect-ffi/platforms/darwin-arm64/CHANGELOG.md +++ b/packages/protect-ffi/platforms/darwin-arm64/CHANGELOG.md @@ -1,5 +1,7 @@ # @cipherstash/protect-ffi-darwin-arm64 +## 0.33.0 + ## 0.32.0 ### Patch Changes diff --git a/packages/protect-ffi/platforms/darwin-arm64/package.json b/packages/protect-ffi/platforms/darwin-arm64/package.json index 155b6f6bd..859494a49 100644 --- a/packages/protect-ffi/platforms/darwin-arm64/package.json +++ b/packages/protect-ffi/platforms/darwin-arm64/package.json @@ -1,7 +1,7 @@ { "name": "@cipherstash/protect-ffi-darwin-arm64", "description": "Prebuilt binary package for `@cipherstash/protect-ffi` on `darwin-arm64`.", - "version": "0.32.0", + "version": "0.33.0", "repository": { "type": "git", "url": "git+https://github.com/cipherstash/stack.git", diff --git a/packages/protect-ffi/platforms/darwin-x64/CHANGELOG.md b/packages/protect-ffi/platforms/darwin-x64/CHANGELOG.md index 8cc1976ff..f7067ef63 100644 --- a/packages/protect-ffi/platforms/darwin-x64/CHANGELOG.md +++ b/packages/protect-ffi/platforms/darwin-x64/CHANGELOG.md @@ -1,5 +1,7 @@ # @cipherstash/protect-ffi-darwin-x64 +## 0.33.0 + ## 0.32.0 ### Patch Changes diff --git a/packages/protect-ffi/platforms/darwin-x64/package.json b/packages/protect-ffi/platforms/darwin-x64/package.json index b0e369e85..49ee91d06 100644 --- a/packages/protect-ffi/platforms/darwin-x64/package.json +++ b/packages/protect-ffi/platforms/darwin-x64/package.json @@ -1,7 +1,7 @@ { "name": "@cipherstash/protect-ffi-darwin-x64", "description": "Prebuilt binary package for `@cipherstash/protect-ffi` on `darwin-x64`.", - "version": "0.32.0", + "version": "0.33.0", "repository": { "type": "git", "url": "git+https://github.com/cipherstash/stack.git", diff --git a/packages/protect-ffi/platforms/linux-arm64-gnu/CHANGELOG.md b/packages/protect-ffi/platforms/linux-arm64-gnu/CHANGELOG.md index 75edf5eb6..8a79eab40 100644 --- a/packages/protect-ffi/platforms/linux-arm64-gnu/CHANGELOG.md +++ b/packages/protect-ffi/platforms/linux-arm64-gnu/CHANGELOG.md @@ -1,5 +1,7 @@ # @cipherstash/protect-ffi-linux-arm64-gnu +## 0.33.0 + ## 0.32.0 ### Patch Changes diff --git a/packages/protect-ffi/platforms/linux-arm64-gnu/package.json b/packages/protect-ffi/platforms/linux-arm64-gnu/package.json index dbbb71e8d..3c2a9f10c 100644 --- a/packages/protect-ffi/platforms/linux-arm64-gnu/package.json +++ b/packages/protect-ffi/platforms/linux-arm64-gnu/package.json @@ -1,7 +1,7 @@ { "name": "@cipherstash/protect-ffi-linux-arm64-gnu", "description": "Prebuilt binary package for `@cipherstash/protect-ffi` on `linux-arm64-gnu`.", - "version": "0.32.0", + "version": "0.33.0", "repository": { "type": "git", "url": "git+https://github.com/cipherstash/stack.git", diff --git a/packages/protect-ffi/platforms/linux-x64-gnu/CHANGELOG.md b/packages/protect-ffi/platforms/linux-x64-gnu/CHANGELOG.md index 031fa09e5..ccaad566a 100644 --- a/packages/protect-ffi/platforms/linux-x64-gnu/CHANGELOG.md +++ b/packages/protect-ffi/platforms/linux-x64-gnu/CHANGELOG.md @@ -1,5 +1,7 @@ # @cipherstash/protect-ffi-linux-x64-gnu +## 0.33.0 + ## 0.32.0 ### Patch Changes diff --git a/packages/protect-ffi/platforms/linux-x64-gnu/package.json b/packages/protect-ffi/platforms/linux-x64-gnu/package.json index 18cafb70e..231d1de2e 100644 --- a/packages/protect-ffi/platforms/linux-x64-gnu/package.json +++ b/packages/protect-ffi/platforms/linux-x64-gnu/package.json @@ -1,7 +1,7 @@ { "name": "@cipherstash/protect-ffi-linux-x64-gnu", "description": "Prebuilt binary package for `@cipherstash/protect-ffi` on `linux-x64-gnu`.", - "version": "0.32.0", + "version": "0.33.0", "repository": { "type": "git", "url": "git+https://github.com/cipherstash/stack.git", diff --git a/packages/protect-ffi/platforms/linux-x64-musl/CHANGELOG.md b/packages/protect-ffi/platforms/linux-x64-musl/CHANGELOG.md index fa4ce6b3a..8bacbdb5c 100644 --- a/packages/protect-ffi/platforms/linux-x64-musl/CHANGELOG.md +++ b/packages/protect-ffi/platforms/linux-x64-musl/CHANGELOG.md @@ -1,5 +1,7 @@ # @cipherstash/protect-ffi-linux-x64-musl +## 0.33.0 + ## 0.32.0 ### Patch Changes diff --git a/packages/protect-ffi/platforms/linux-x64-musl/package.json b/packages/protect-ffi/platforms/linux-x64-musl/package.json index 5885c934b..90ebc3350 100644 --- a/packages/protect-ffi/platforms/linux-x64-musl/package.json +++ b/packages/protect-ffi/platforms/linux-x64-musl/package.json @@ -1,7 +1,7 @@ { "name": "@cipherstash/protect-ffi-linux-x64-musl", "description": "Prebuilt binary package for `@cipherstash/protect-ffi` on `linux-x64-musl`.", - "version": "0.32.0", + "version": "0.33.0", "repository": { "type": "git", "url": "git+https://github.com/cipherstash/stack.git", diff --git a/packages/protect-ffi/platforms/win32-x64-msvc/CHANGELOG.md b/packages/protect-ffi/platforms/win32-x64-msvc/CHANGELOG.md index d7bc53160..86a2feda4 100644 --- a/packages/protect-ffi/platforms/win32-x64-msvc/CHANGELOG.md +++ b/packages/protect-ffi/platforms/win32-x64-msvc/CHANGELOG.md @@ -1,5 +1,7 @@ # @cipherstash/protect-ffi-win32-x64-msvc +## 0.33.0 + ## 0.32.0 ### Patch Changes diff --git a/packages/protect-ffi/platforms/win32-x64-msvc/package.json b/packages/protect-ffi/platforms/win32-x64-msvc/package.json index 6a33f28d8..430e4e9e2 100644 --- a/packages/protect-ffi/platforms/win32-x64-msvc/package.json +++ b/packages/protect-ffi/platforms/win32-x64-msvc/package.json @@ -1,7 +1,7 @@ { "name": "@cipherstash/protect-ffi-win32-x64-msvc", "description": "Prebuilt binary package for `@cipherstash/protect-ffi` on `win32-x64-msvc`.", - "version": "0.32.0", + "version": "0.33.0", "repository": { "type": "git", "url": "git+https://github.com/cipherstash/stack.git", diff --git a/packages/stack-drizzle/CHANGELOG.md b/packages/stack-drizzle/CHANGELOG.md index 62ccaa572..27cfa01a8 100644 --- a/packages/stack-drizzle/CHANGELOG.md +++ b/packages/stack-drizzle/CHANGELOG.md @@ -1,5 +1,14 @@ # @cipherstash/stack-drizzle +## 1.2.0 + +### Patch Changes + +- Updated dependencies [b04ea2f] +- Updated dependencies [518abfd] +- Updated dependencies [c1bf387] + - @cipherstash/stack@1.2.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/stack-drizzle/package.json b/packages/stack-drizzle/package.json index 738969ef2..0ba93aa84 100644 --- a/packages/stack-drizzle/package.json +++ b/packages/stack-drizzle/package.json @@ -1,6 +1,6 @@ { "name": "@cipherstash/stack-drizzle", - "version": "1.1.1", + "version": "1.2.0", "description": "CipherStash Stack Drizzle ORM integration: searchable, application-layer field-level encryption for PostgreSQL.", "keywords": [ "encrypted", diff --git a/packages/stack-prisma/CHANGELOG.md b/packages/stack-prisma/CHANGELOG.md index deb7af322..6cc9f2d9e 100644 --- a/packages/stack-prisma/CHANGELOG.md +++ b/packages/stack-prisma/CHANGELOG.md @@ -1,5 +1,220 @@ # @cipherstash/stack-prisma +## 1.2.0 + +### Minor Changes + +- ba37039: Move the bundled EQL v3 migrations to **eql-3.0.5**, which renames the SQL + function `eql_v3.ste_vec_contains` to `eql_v3.jsonb_document_contains`. + + **The blast radius is narrower than a renamed public function suggests.** The + `@>` / `<@` operators on `public.eql_v3_json_search` behave exactly as before, + and so do the two function-form entry points that exist for platforms without + operator support — `eql_v3.jsonb_contains(jsonb, jsonb)` and + `eql_v3.jsonb_contained_by(jsonb, jsonb)` are byte-identical to 3.0.4. Those + are what a PostgREST caller invokes, so PostgREST callers on the documented + surface are **not** affected. The renamed function is the typed implementation + those operators dispatch into. + + **And the old name still works.** eql-3.0.5 ships `eql_v3.ste_vec_contains` as + a deprecated delegating alias for both overloads, so hand-written SQL naming it + — an application query, a view, an RLS policy, or a per-function + `GRANT EXECUTE ON FUNCTION eql_v3.ste_vec_contains(…)` — keeps resolving. The + typed overload stays inlinable, so a function-form query through the alias + still matches the same functional GIN index. Migrate to + `jsonb_document_contains` when convenient; nothing forces it at upgrade time. + + **Separately — and true of every EQL upgrade, not just this one:** the install + bundle opens with `DROP SCHEMA IF EXISTS eql_v3 CASCADE`, so applying it drops + every object in `eql_v3` / `eql_v3_internal` and everything that depended on + them. **Encrypted data and column types are not affected** — the storage + domains are `public.eql_v3_*`, deliberately outside both dropped schemas, and + their CHECK functions are re-created rather than dropped. What does not survive + is everything else pointing into the schema, which is two actions, neither of + them to do with the rename: + + 1. **Re-run your grant script.** Every grant on every `eql_v3` / + `eql_v3_internal` object is gone. The schema-wide form EQL documents — + `GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA eql_v3 TO app_role` — picks up + both the new name and the alias on its own. + 2. **Recreate your functional indexes, then `ANALYZE`.** Indexes over + `eql_v3.eq_term(…)` / `ord_term` / `match_term` / `to_ste_vec_query(…)` + depend on the dropped schema and go with it. Nothing errors afterwards: + encrypted predicates keep working and silently fall back to sequential + scans. A migration runner will not redo an already-applied migration, so + this has to be a _new_ one. The `stash-indexing` skill documents the + mechanism ("These indexes do not survive an EQL reinstall or upgrade") and + the `EXPLAIN` check that confirms recovery; capturing and restoring them + automatically is tracked in + [cipherstash/stack#918](https://github.com/cipherstash/stack/issues/918). + + Any RLS policy, view, or constraint that calls an `eql_v3` function is dropped + by the same CASCADE and needs recreating too. **The rename itself needs no + action — the alias makes it non-breaking.** + + Two artefacts carry the new invariant: + + - A new upgrade edge, `20260814T0000_upgrade_eql_v3_3_0_5`, carrying the + invariant `cipherstash:upgrade-eql-v3-bundle-3.0.5-v1`. Databases already + running an earlier bundle re-install through this edge on the next + `prisma-next migration plan` followed by `prisma-next migrate`, exactly as + they did for 3.0.2 and 3.0.4. **`migrate` alone is not enough** — the seed + phase that copies a new migration package into your repo runs only from + `migration plan`, so without it the 3.0.5 directory never reaches disk and + `migrate` is a silent no-op that leaves the database on the older bundle. + - The baseline install migration `20260601T0100_install_eql_v3_bundle`, which + gains a no-SQL carrier op for the new invariant. The baseline itself bakes + eql-3.0.6, not 3.0.5: it was re-emitted again for 3.0.6 before any release + carried the 3.0.5 bytes (see the 3.0.6 entry). Fresh databases land on 3.0.6 + from the single all-additive genesis edge, keeping `db init` (additive-only + policy) working. + + **Action required.** The baseline's bytes — and so its `migrationHash` — have + changed. This is one change, shared with the 3.0.6 entry: npm never shipped + the 3.0.5 baseline on its own. If your project already has a + `migrations/cipherstash/` directory generated against + `@cipherstash/stack-prisma@1.0.0`, `@1.1.0` or `@1.1.1`, delete that directory + and re-run `prisma-next migration plan` (or `migrate`); the 1.1.0 Prisma Next + 0.17 upgrade re-anchored the same artefacts, so a space vendored against any + of them is stale here. The seed phase regenerates + it byte-identical to the shipped artefacts. Your database keeps its markers, so + already-applied invariants are not re-run — the only new work is the 3.0.5 and + 3.0.6 upgrade edges. + + If you skip the delete, nothing warns you: a vendored baseline is stale but + internally intact, so it passes every integrity check. On an existing database + the upgrade still applies correctly; on a **fresh** one, `db init` refuses with + `Operation cipherstash.upgrade-eql-v3-bundle-3.0.5 has class "data" which is +not allowed by policy.` — an error that names neither the directory nor the + remedy. See "Upgrading from 1.0.0 or 1.1.x" in the package README. + + **Why the baseline was re-emitted rather than left frozen.** These artefacts are + content-addressed and normally append-only: an EQL bump ships as a new upgrade + directory and published directories are never rewritten. That rule cannot be + followed here without a second `from: null` genesis edge, because no upgrade + edge can ever be walked by `db init` — every upgrade edge is a self-edge, and + the integrity checker requires a self-edge to carry a `data`-class op, which + `db init`'s additive-only policy refuses. A fresh database must therefore + collect every head-ref invariant from the genesis edge it walks. The + append-only alternative would duplicate the full ~2.6 MB bundle into a new + genesis edge on every EQL release, permanently; re-emitting was taken instead + while 1.0.0 was two weeks old with negligible adoption, and is a decision to be + re-argued on adoption numbers rather than repeated by default. This re-emit + never reached npm: 1.1.0 and 1.1.1 shipped the eql-3.0.4 baseline, and the + 3.0.6 re-emit replaced these bytes before a release carried them. + +- 78ab894: Move the bundled EQL v3 migrations to **eql-3.0.6**. This release changes only + the version stamp: `eql_v3.version()` returns `'3.0.6'` and the `eql_v3` schema + comment reads `'3.0.6'`. No function, operator, domain or index behaviour + changes. + + Two artefacts carry the new bundle: + + - A new upgrade edge, `20261002T0000_upgrade_eql_v3_3_0_6`, carrying the + invariant `cipherstash:upgrade-eql-v3-bundle-3.0.6-v1`. Existing databases + re-install through it on the next `prisma-next migration plan` followed by + `prisma-next migrate`. **`migrate` alone is not enough**: only + `migration plan` copies the new directory into your repo. + - The baseline install migration `20260601T0100_install_eql_v3_bundle`, which + now bakes 3.0.6 and gains a fifth op, a no-SQL carrier for the new + invariant, so fresh databases land on 3.0.6 from the single all-additive + genesis edge and `db init` keeps working. + + **Upgrading a 1.0.0 or 1.1.x database installs the EQL bundle twice.** Those + releases shipped eql-3.0.4, so `migrate` walks the 3.0.5 upgrade edge and then + the 3.0.6 one, re-installing the ~2.6 MB bundle once for each in the same run. + That costs time, not correctness: the database ends on 3.0.6 with every + invariant recorded. The 3.0.5 edge is kept rather than folded into this one + because the frozen-history guard treats every committed edge as published, and + a database built from `main` may already have walked it and recorded its + invariant. As with every EQL upgrade, each re-install opens with + `DROP SCHEMA IF EXISTS eql_v3 CASCADE`: re-run your grant script, and recreate + functional indexes and anything else depending on `eql_v3` (see the 3.0.5 + entry). + + **Action required: the one in the 3.0.5 entry, once.** npm has only ever + shipped the eql-3.0.4 baseline (1.0.0 on 30 July, 1.1.0 on 19 August, 1.1.1 on + 20 August), so this release changes the published baseline once, not twice. If + your project has a `migrations/cipherstash/` directory generated against any + earlier version, delete it and re-run `prisma-next migration plan`. Your + database keeps its markers, so already-applied invariants are not re-run. If + you skip the delete, a fresh `db init` refuses with + `Operation cipherstash.upgrade-eql-v3-bundle-3.0.5 has class "data" which is +not allowed by policy.` — see "Upgrading from 1.0.0 or 1.1.x" in the package + README. + + **Why the baseline was re-emitted rather than given a second genesis edge.** + In this repository the baseline was already re-emitted once, for 3.0.5, and + that decision said the trade must be re-argued on adoption numbers at the next + bump. This is that bump, and the trade came out the same way. + `@cipherstash/stack-prisma` had 144 npm downloads in September 2026 (52 in the + last week), fewer than the ~253 monthly downloads at which the 3.0.5 re-emit + was judged a small, knowable blast radius. The 3.0.5 re-emit never reached + npm, so what this one rewrites is unpublished. And since 3.0.6 changes only + the version stamp, the append-only alternative, a second `from: null` genesis + edge, would add another permanent ~2.6 MB copy of the bundle for no change in + behaviour. Once this package has real adoption, the second genesis edge is the + right shape. + +### Patch Changes + +- 4422d5c: Pin the packed `@cipherstash/eql` dependency to an exact version, closing a + route by which an installed EQL bundle could drift ahead of the code built + against it. + + Both packages declared `"@cipherstash/eql": "workspace:^"` under + `dependencies`. In this workspace that resolves in-tree either way, so nothing + in development or CI could see a difference — but the two specifiers do not + pack the same. pnpm rewrites the protocol when it builds the tarball a customer + actually installs: + + "workspace:^" packs as "^3.0.5" + "workspace:*" packs as "3.0.5" + + The caret is the problem. `@cipherstash/eql` is still published from + `cipherstash/encrypt-query-language` until the publisher repoint, so a 3.0.x can + reach npm without passing through this repository at all — and `^3.0.5` accepts + it. A customer installing `stash` or `@cipherstash/stack-prisma` would then get + SQL that STORES and queries encrypted payloads at one version, while + `@cipherstash/stack`'s v3 domain types (which EMIT those payloads) and + `stack-prisma`'s baked migrations stayed frozen at the version this repo built + and tested against. The two halves of EQL are released in lockstep precisely + because that skew does not fail at install or in CI — it fails in a database. + + `workspace:*` is the only form that closes it. A literal `"3.0.5"` would be an + exact pin too, but it is a registry pin: `pnpm run lint:eql-pins` rejects it, + because resolving EQL from a registry rather than from this repo is the same + drift one layer up. + + No API, behaviour or SQL changes. What changes is the dependency range in the + published tarballs, and only in the narrowing direction — the version resolved + today is the version that was already being resolved. Nothing needs to be done + on upgrade. + + `@cipherstash/stack` declares the same dependency under `devDependencies` and + is deliberately left alone: pnpm rewrites that range too, but no consumer of the + package ever resolves it. + +- c604028: Document the upgrade from 1.0.0 or 1.1.x in the package README: why + `migrations/cipherstash/` must be deleted and regenerated, what each Prisma Next + command does if it is not, and the exact `db init` refusal + (`Operation cipherstash.upgrade-eql-v3-bundle-3.0.5 has class "data" which is +not allowed by policy.`) that a stale vendored directory produces on a fresh + database. + + The behaviour worth knowing regardless of version: only `prisma-next migration +plan` copies new migration packages into your repo. Running `migrate` or + `db init` after upgrading this package without planning first silently leaves + the database on the older EQL bundle — a stale vendored directory is internally + intact, so it passes every integrity check and nothing reports a problem. + +- Updated dependencies [e52d331] +- Updated dependencies [b04ea2f] +- Updated dependencies [518abfd] +- Updated dependencies [c1bf387] + - @cipherstash/eql@3.0.6 + - @cipherstash/stack@1.2.0 + ## 1.1.1 ### Patch Changes @@ -52,9 +267,7 @@ ```ts const cipherstash = await cipherstashFromStack({ contractJson, - encryptionConfig: { - /* credentials and auth options only */ - }, + encryptionConfig: {/* credentials and auth options only */}, }); ``` @@ -420,7 +633,7 @@ not a function`. The bundled `stash-prisma` skill documents this too. ```ts async function makeClient( - schemas: S + schemas: S, ) { return await Encryption({ schemas }); } diff --git a/packages/stack-prisma/package.json b/packages/stack-prisma/package.json index 3a855d7a0..703c38eab 100644 --- a/packages/stack-prisma/package.json +++ b/packages/stack-prisma/package.json @@ -1,6 +1,6 @@ { "name": "@cipherstash/stack-prisma", - "version": "1.1.1", + "version": "1.2.0", "license": "MIT", "author": "CipherStash ", "description": "CipherStash extension for Prisma Next: searchable application-layer field-level encryption for Postgres (EQL v3), with domain-typed encrypted columns, the eql* query operators, bulk encrypt/decrypt middleware, and a baseline migration that installs the EQL v3 bundle.", diff --git a/packages/stack-supabase/CHANGELOG.md b/packages/stack-supabase/CHANGELOG.md index 5b0f13b27..c6751342d 100644 --- a/packages/stack-supabase/CHANGELOG.md +++ b/packages/stack-supabase/CHANGELOG.md @@ -1,5 +1,227 @@ # @cipherstash/stack-supabase +## 1.2.0 + +### Patch Changes + +- f31a202: Correct the runtime story in the TSDoc that ships as `.d.ts`. + + Three claims a user sees on hover were wrong: + + - `makeEncryptedSupabase` said "Declare your schemas and it runs anywhere; omit + them and we discover them for you, which needs a database connection and is + therefore Node-only." Declaring `schemas` does skip introspection entirely — + no Postgres connection, no `pg`, no `databaseUrl` — but it does not make the + default entry edge-capable. **The entry point decides where the wrapper runs; + `schemas` decides only whether Postgres is involved.** + - The default entry's doc named `@cipherstash/protect-ffi` as the Node-API + binary loaded on import. It is the one package in that graph that + deliberately does not load on import; the module-evaluation-time load belongs + to `@cipherstash/auth`. + - `./wasm-inline`'s doc called introspection "half of what made the default + entry Node-only". The engine is what makes it Node-only, and its emitted + bundle also carries an `import("pg")` specifier a bundler resolves at build + time. Introspection is a separate axis. + + Documentation only — no runtime behaviour changes. + +- e36f1a0: Stop telling customers the Supabase wrapper cannot run in a Worker. + + `@cipherstash/stack-supabase` has shipped two entry points since #912. The + package root introspects your database and runs on Node; the `wasm-inline` + entry carries the WASM engine, takes declared `schemas` instead of + introspecting, and runs on Deno, Supabase Edge Functions and Cloudflare + Workers. Introspection was the only thing that needed a Postgres socket, and + that entry does not do it. + + Two shipping documents were never updated and still described the state before + that change: + + - `packages/stack-supabase/README.md` said "the factory cannot run in an edge + Worker or the browser" and did not mention the `wasm-inline` entry anywhere + in the file. This is the npm package page. + - `skills/stash-supabase/SKILL.md` said the same thing in its setup section. + The skill ships inside the `stash` tarball, and `stash init` copies it into + the customer's own repository, where their coding agent reads it as + instruction. The one correct mention of the edge entry was in a callout near + the top that the setup steps never pointed at, so a reader following the + setup never learned the second entry existed. + + The population this misled hardest is the one that needs the edge entry most: + server code on Lovable, v0, Bolt and Replit runs on an edge runtime, which is + exactly the case `wasm-inline` was built for and exactly the case these + documents called impossible. + + Both files now describe both entries. The README gains an "Edge runtimes" + section with the call shape; the skill gains a fifth setup step with the same, + and the introspection paragraph now scopes its restriction to the native entry + and points there. Both name the four ways the edge entry differs: `schemas` is + required, `config` is required, `databaseUrl` is refused, and + `.withLockContext()` / `.audit()` throw rather than silently dropping an + identity claim (#797). + + The **browser** half of the old sentence was correct and is kept, with the + reason now given: the WASM client requires a workspace `clientKey` on every + authentication path, so a browser build would ship the key with it (#804). + + Four claims that were wrong in the same neighbourhood are corrected while we + are here, three of them pre-dating this change: + + - **`skills/stash-managed-platforms/SKILL.md` shipped a snippet that does not + compile.** It authored the `schemas` object from `@cipherstash/stack/wasm-inline` + and handed it to `encryptedSupabase` from `@cipherstash/stack-supabase/wasm-inline`. + The adapter types `schemas` from `@cipherstash/stack/eql/v3`, and the two + entries ship independent declarations of the column classes whose private + `columnName` field TypeScript compares nominally — so `tsc` rejects it while + the code runs perfectly, which is why nobody noticed. The schema import now + comes from `eql/v3`, and a new guard + (`scripts/__tests__/skills-supabase-edge-schema-entry.test.mjs`) fails if any + shipped document pairs the two again. + - **`skills/stash-edge/SKILL.md` is what produced that snippet.** Its "Schema + Modules Do Not Cross Entries" section told edge projects to author schemas + from `@cipherstash/stack/wasm-inline` with no carve-out. The rule is really + "author against the entry whose _client type_ consumes the schema": a raw + `Encryption` client from `wasm-inline` wants `wasm-inline` tables, but the + Supabase adapter wants `eql/v3` tables on both of its entries, WASM engine or + not. The section now says so, and the "The Supabase adapter has its own edge + entry" note points at it. + - **`skills/stash-supabase/SKILL.md` described the wrong failure mode for a + missing declaration.** Omitting `schemas` on the edge entry cannot produce a + no-column client — it is non-optional on the type and throws at construction — + and an undeclared _table_ throws rather than passing through unencrypted. The + hazard is an undeclared **column** on a declared table, which is treated as + plaintext; the bullet now says that, along with the one thing that limits it + (a plaintext write to an `eql_v3_*` column fails the domain CHECK, though a + NULL still passes) and the fact that the native entry's warning about + unverified declarations is gated on the introspector and so never fires + there. Reads get no equivalent backstop, and the bullet now says so: the + `select('*')` refusal looks like one, but a query awaited with no + `.select()` at all takes the raw-`*` branch in `query-builder.ts` and + returns every column undecrypted. + - **"Undeclared tables behave exactly as with no `schemas` at all" was false on + the native entry too.** Introspection is gated on a resolved database URL, + not on the absence of `schemas`, and an ambient `DATABASE_URL` is + deliberately ignored once tables are declared. The statement holds only when + `databaseUrl` is passed _alongside_ `schemas`, which is what it now says. + + `config` is corrected everywhere that called all four `CS_*` values mandatory: + the README, `skills/stash-supabase/SKILL.md`, and — in `skills/stash-edge/SKILL.md` + — its frontmatter description, its Credentials section, its troubleshooting + advice, and the native-vs-WASM comparison table. That skill already contradicted + itself, since its own `config.authStrategy` example passes two values, not four. + The same sentence in the `EncryptedSupabaseWasmOptions` doc comment + (`packages/stack-supabase/src/wasm-inline.ts`) is fixed too, comment-only. Only + `clientId` and `clientKey` are always required. Beyond them the config is a union — the + access-key path adds `workspaceCrn` + `accessKey`, and the strategy path takes + a pre-built `config.authStrategy` and makes `workspaceCrn` optional, because a + built strategy already carries the CRN. `OidcFederationStrategy` is re-exported + from `@cipherstash/stack/wasm-inline`, so authenticating as the end user works + on the edge; what does not work is binding data to that user, which stays + called out in its own `.withLockContext()` bullet. + + Tests now anchor the corrected claims against the code rather than against + prose. `packages/stack-supabase/__tests__/supabase-wasm-config.test-d.ts` asserts + at the type level that the edge `config` accepts both the access-key arm and a + strategy-only arm without `workspaceCrn`, and rejects `clientId` + `clientKey` + alone. `supabase-declared-mode.test.ts` gains a case pinning the real hazard: an + undeclared column on a declared table reaches PostgREST as plaintext on insert, + update and filter, and is absent from the decrypt call. + + Review found four more, one of them a change to a published type: + + - **`databaseUrl` was only refused for callers who wrote the options inline.** + `EncryptedSupabaseWasmOptions` left the field out, and omission is policed by + excess-property checking, which fires on fresh object literals alone. An + options object assembled as a `const` and passed by variable — which is what + a Node-to-edge port actually holds — type-checked clean and reached the + construction-time throw instead, from documents saying the type checker + enforced it. The field is now declared `databaseUrl?: never`, mirroring + `WasmClientConfig.eqlVersion?: never` in `@cipherstash/stack`, which exists + for the identical reason one package along. The runtime throw stays as the + backstop for plain JS. New type tests cover the inline and by-variable + shapes on both call forms, plus a positive control that the same options + object still compiles once `databaseUrl` is dropped. + - **The `select('*')` correction had landed in only one of its two shipped + copies.** `skills/stash-supabase/SKILL.md` carried it; + `skills/stash-managed-platforms/SKILL.md`, edited in the same change, still + framed declared mode as giving things up "loudly rather than silently" over a + bullet naming the refusal — precisely the inference the correction exists to + kill. That skill is read as instruction by an agent on Lovable, v0, Bolt or + Replit, and the failure it mispromised is silent: raw EQL payloads returned + as `data`, no error. The wording is carried across, and a new guard + (`scripts/__tests__/skills-select-star-not-a-read-backstop.test.mjs`) fails + if any shipped document states the refusal without the caveat in the same + section. Two copies of one fact drift the moment one of them is edited, and + no reviewer diff shows the copy nobody touched. + - **"Everything after construction is the same wrapper" was false in the first + way a reader hits it.** Both `packages/stack-supabase/README.md` and + `skills/stash-supabase/SKILL.md` said `from()`, the filters and the response + shape are identical across the two entries — the README saying so twenty-five + lines under a paragraph telling the same reader `select('*')` just works. The + edge entry is always in declared mode, where `select('*')` is refused and + `from()` on an undeclared table throws. Both sentences now name the two + exceptions, so the quick-start snippet the section tells you to port no + longer arrives with a promise it breaks. + - **The ambient-`DATABASE_URL` warning cannot fire on the edge entry.** + `skills/stash-managed-platforms/SKILL.md` said an ambient `DATABASE_URL` is + ignored when `schemas` are passed, "with a warning that the declaration is + unverified" — in a section whose subject is `wasm-inline`. Both the ambient + read and the warning are gated on the introspector, which that build does not + have, so nothing there ever tells you a declaration is incomplete. Now scoped + to Node, with the edge case stated. The sentence immediately above it had the + same fault and is fixed with it: the ⚠️ callout on undeclared columns offered + "pass `databaseUrl` so introspection fills the gaps" as the remedy, inside a + section about the entry that refuses `databaseUrl` — and it is the remedy a + Lovable or Replit agent, which has only the edge entry, would have reached + for. It now says introspection is unavailable there and what to do instead. + + `@cipherstash/wizard` is bumped alongside `stash` because `skills/` ships in + both tarballs — `packages/wizard/tsup.config.ts` copies it into `dist/skills` + and `package.json` lists that under `files`. Without the bump the published + wizard keeps shipping the old text until some unrelated change moves its + version. + + One more wording correction, of the kind #952 fixed in this package's `.d.ts`: + `packages/stack-supabase/README.md` and `skills/stash-supabase/SKILL.md` both + derived "runs on Node only" from introspection — "introspection needs a direct + Postgres connection, **so** … this entry runs on Node only". Introspection is + not the cause. The entry binds the native engine, so it is Node-only whether or + not you declare `schemas`; a reader who took the stated cause at face value + would conclude that declaring tables makes the root entry edge-capable, which is + the exact wrong turn the `wasm-inline` entry exists to prevent. Both sentences + now attribute the restriction to the engine and say that declaring `schemas` + does not move it. Both files are enrolled in #952's + `scripts/__tests__/supabase-runtime-claims.test.mjs`, which is what its own + comment said to do once this branch stopped rewriting the same lines — the + README and this skill are the two copies that reach a customer, and the guard + now fails if either grows the claim back. `skills/stash-managed-platforms/SKILL.md` + stays out, with the reason written down: its causal claims are correct, but + rewording the unqualified "Worker" in its frontmatter `description` changes + what the skill matches on, which is not a rider on a documentation fix. + + No runtime behaviour changes. The one non-documentation change is the + `databaseUrl?: never` field on `EncryptedSupabaseWasmOptions`, which is + type-level: it rejects at compile time a call that already threw at + construction. + +- c1bf387: Fix: a schema authored with `encryptedTable`/`types` from + `@cipherstash/stack/wasm-inline` was a compile error wherever an EQL v3 table was + expected — `encryptedSupabase`'s `schemas`, the Drizzle helpers, Prisma Next, the + native `Encryption` — and native-authored tables were rejected by the WASM + `Encryption`, with `Types have separate declarations of a private property +'columnName'`. The two entries shipped separately-emitted copies of every column + class, and TypeScript compares classes with private members by declaration + origin. The runtime was never affected, which made `as any` the tempting fix. + + Every entry now resolves one declaration, so one schema module can be shared + between a Node server and an Edge Function in either direction. `./wasm-inline` + keeps its ESM-only shape. + +- Updated dependencies [b04ea2f] +- Updated dependencies [518abfd] +- Updated dependencies [c1bf387] + - @cipherstash/stack@1.2.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/stack-supabase/package.json b/packages/stack-supabase/package.json index b6a304817..91c3d576b 100644 --- a/packages/stack-supabase/package.json +++ b/packages/stack-supabase/package.json @@ -1,6 +1,6 @@ { "name": "@cipherstash/stack-supabase", - "version": "1.1.1", + "version": "1.2.0", "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 419a7e088..1d2f7ebcd 100644 --- a/packages/stack/CHANGELOG.md +++ b/packages/stack/CHANGELOG.md @@ -1,5 +1,69 @@ # @cipherstash/stack +## 1.2.0 + +### Minor Changes + +- b04ea2f: Surface CipherStash token-service refusals as typed diagnostics. + + `@cipherstash/stack` operation and initialization failures now carry + `authCode`, `help`, and `url` from stack-auth. The message remains stack-auth's + original diagnostic message; Stack does not copy or rewrite its instructions. + Callers can branch on `USAGE_LIMIT_EXCEEDED` or `ORG_NOT_PROVISIONED`, render + `help`, and link to `url`. + + `LockContext.identify()` also recognizes those two codes on a genuine CTS + `402`, while declining malformed or unknown responses. Legacy valid JSON + responses without `cs_code` retain the historical usage-limit classification. + + `stash auth login` and `stash env` now consume `@cipherstash/auth` 0.44.0's + typed failures. They print the upstream diagnostic guidance, preserve its URL, + avoid suggesting another login for terminal account refusals, and expose + terminal codes on the JSON stream. The JSON error envelope gains an optional + `hint` for the upstream guidance. + +### Patch Changes + +- 518abfd: Document that `@cipherstash/stack/wasm-inline` is server-side only, and pin the + reason against the core. + + `WasmClientConfig` requires `clientId` and `clientKey` on every auth arm, + including the `authStrategy` (OIDC federation) arm. That read like an + over-declaration the SDK could relax — if federation alone sufficed, a browser + could hold a client without a workspace secret. It cannot. The core requires + both fields regardless of strategy, and loads `clientKey` as encryption key + material _before_ it ever calls the auth strategy. Since `clientKey` is a + workspace secret, no configuration of this entry belongs in a browser bundle — + which is why this entry has no `browser` export condition, and will not get one + until the core changes. + + No behaviour change. The types and runtime are unchanged; what changes is that + the constraint is now stated where callers meet it — `WasmClientConfig`, the + `stash-edge` skill, the `stash-encryption` entry-point table, and, where this + entry had been described as browser-capable, the `stash-supabase` skill and + the `supabase-worker` example — and enforced by contract tests that run + against the real WASM core instead of the mocks and stubs the rest of the wasm + suite uses. + +- c1bf387: Fix: a schema authored with `encryptedTable`/`types` from + `@cipherstash/stack/wasm-inline` was a compile error wherever an EQL v3 table was + expected — `encryptedSupabase`'s `schemas`, the Drizzle helpers, Prisma Next, the + native `Encryption` — and native-authored tables were rejected by the WASM + `Encryption`, with `Types have separate declarations of a private property +'columnName'`. The two entries shipped separately-emitted copies of every column + class, and TypeScript compares classes with private members by declaration + origin. The runtime was never affected, which made `as any` the tempting fix. + + Every entry now resolves one declaration, so one schema module can be shared + between a Node server and an Edge Function in either direction. `./wasm-inline` + keeps its ESM-only shape. + +- Updated dependencies [8839d5a] +- Updated dependencies [8839d5a] +- Updated dependencies [4422d5c] +- Updated dependencies + - @cipherstash/protect-ffi@0.33.0 + ## 1.1.1 ## 1.1.0 @@ -415,7 +479,7 @@ ```ts async function makeClient( - schemas: S + schemas: S, ) { return await Encryption({ schemas }); } @@ -2137,7 +2201,7 @@ value)` and `selectorNe(col, path, value)` (dot-notation paths; `ne` includes schemas: [users], config: { authStrategy: OidcFederationStrategy.create(workspaceCrn, () => - getUserJwt() + getUserJwt(), ), }, }); @@ -2357,7 +2421,6 @@ value)` and `selectorNe(col, path, value)` (dot-notation paths; `ne` includes - **README**: Refreshed main repo README and Stack package readme; basic example README now uses `npm install @cipherstash/stack`, CipherStash account and dashboard credentials, and drops Stash CLI references. Added docs badge linking to cipherstash.com/docs. ### Features - - **Logging**: Logger is now used consistently across Stack client interfaces for initialization and operations. ## 0.4.0 diff --git a/packages/stack/package.json b/packages/stack/package.json index b65ba1a4f..8f3dbc7e0 100644 --- a/packages/stack/package.json +++ b/packages/stack/package.json @@ -1,6 +1,6 @@ { "name": "@cipherstash/stack", - "version": "1.1.1", + "version": "1.2.0", "description": "CipherStash Stack for TypeScript and JavaScript", "keywords": [ "encrypted", diff --git a/packages/test-kit/CHANGELOG.md b/packages/test-kit/CHANGELOG.md index 5fa2eda70..cef26f46a 100644 --- a/packages/test-kit/CHANGELOG.md +++ b/packages/test-kit/CHANGELOG.md @@ -1,5 +1,14 @@ # @cipherstash/test-kit +## 0.0.4 + +### Patch Changes + +- Updated dependencies [b04ea2f] +- Updated dependencies [518abfd] +- Updated dependencies [c1bf387] + - @cipherstash/stack@1.2.0 + ## 0.0.3 ### Patch Changes diff --git a/packages/test-kit/package.json b/packages/test-kit/package.json index 7d2fb8ac0..fa3c64375 100644 --- a/packages/test-kit/package.json +++ b/packages/test-kit/package.json @@ -1,6 +1,6 @@ { "name": "@cipherstash/test-kit", - "version": "0.0.3", + "version": "0.0.4", "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 5793aa069..161e41944 100644 --- a/packages/wizard/CHANGELOG.md +++ b/packages/wizard/CHANGELOG.md @@ -1,5 +1,192 @@ # @cipherstash/wizard +## 1.2.0 + +### Patch Changes + +- 3311039: Clarify the bundled supply-chain guidance for non-publishing workload identity + federation. OIDC holders are now classified separately from registry + publishers, with each exchange and its repository permissions reviewed + explicitly. +- e36f1a0: Stop telling customers the Supabase wrapper cannot run in a Worker. + + `@cipherstash/stack-supabase` has shipped two entry points since #912. The + package root introspects your database and runs on Node; the `wasm-inline` + entry carries the WASM engine, takes declared `schemas` instead of + introspecting, and runs on Deno, Supabase Edge Functions and Cloudflare + Workers. Introspection was the only thing that needed a Postgres socket, and + that entry does not do it. + + Two shipping documents were never updated and still described the state before + that change: + + - `packages/stack-supabase/README.md` said "the factory cannot run in an edge + Worker or the browser" and did not mention the `wasm-inline` entry anywhere + in the file. This is the npm package page. + - `skills/stash-supabase/SKILL.md` said the same thing in its setup section. + The skill ships inside the `stash` tarball, and `stash init` copies it into + the customer's own repository, where their coding agent reads it as + instruction. The one correct mention of the edge entry was in a callout near + the top that the setup steps never pointed at, so a reader following the + setup never learned the second entry existed. + + The population this misled hardest is the one that needs the edge entry most: + server code on Lovable, v0, Bolt and Replit runs on an edge runtime, which is + exactly the case `wasm-inline` was built for and exactly the case these + documents called impossible. + + Both files now describe both entries. The README gains an "Edge runtimes" + section with the call shape; the skill gains a fifth setup step with the same, + and the introspection paragraph now scopes its restriction to the native entry + and points there. Both name the four ways the edge entry differs: `schemas` is + required, `config` is required, `databaseUrl` is refused, and + `.withLockContext()` / `.audit()` throw rather than silently dropping an + identity claim (#797). + + The **browser** half of the old sentence was correct and is kept, with the + reason now given: the WASM client requires a workspace `clientKey` on every + authentication path, so a browser build would ship the key with it (#804). + + Four claims that were wrong in the same neighbourhood are corrected while we + are here, three of them pre-dating this change: + + - **`skills/stash-managed-platforms/SKILL.md` shipped a snippet that does not + compile.** It authored the `schemas` object from `@cipherstash/stack/wasm-inline` + and handed it to `encryptedSupabase` from `@cipherstash/stack-supabase/wasm-inline`. + The adapter types `schemas` from `@cipherstash/stack/eql/v3`, and the two + entries ship independent declarations of the column classes whose private + `columnName` field TypeScript compares nominally — so `tsc` rejects it while + the code runs perfectly, which is why nobody noticed. The schema import now + comes from `eql/v3`, and a new guard + (`scripts/__tests__/skills-supabase-edge-schema-entry.test.mjs`) fails if any + shipped document pairs the two again. + - **`skills/stash-edge/SKILL.md` is what produced that snippet.** Its "Schema + Modules Do Not Cross Entries" section told edge projects to author schemas + from `@cipherstash/stack/wasm-inline` with no carve-out. The rule is really + "author against the entry whose _client type_ consumes the schema": a raw + `Encryption` client from `wasm-inline` wants `wasm-inline` tables, but the + Supabase adapter wants `eql/v3` tables on both of its entries, WASM engine or + not. The section now says so, and the "The Supabase adapter has its own edge + entry" note points at it. + - **`skills/stash-supabase/SKILL.md` described the wrong failure mode for a + missing declaration.** Omitting `schemas` on the edge entry cannot produce a + no-column client — it is non-optional on the type and throws at construction — + and an undeclared _table_ throws rather than passing through unencrypted. The + hazard is an undeclared **column** on a declared table, which is treated as + plaintext; the bullet now says that, along with the one thing that limits it + (a plaintext write to an `eql_v3_*` column fails the domain CHECK, though a + NULL still passes) and the fact that the native entry's warning about + unverified declarations is gated on the introspector and so never fires + there. Reads get no equivalent backstop, and the bullet now says so: the + `select('*')` refusal looks like one, but a query awaited with no + `.select()` at all takes the raw-`*` branch in `query-builder.ts` and + returns every column undecrypted. + - **"Undeclared tables behave exactly as with no `schemas` at all" was false on + the native entry too.** Introspection is gated on a resolved database URL, + not on the absence of `schemas`, and an ambient `DATABASE_URL` is + deliberately ignored once tables are declared. The statement holds only when + `databaseUrl` is passed _alongside_ `schemas`, which is what it now says. + + `config` is corrected everywhere that called all four `CS_*` values mandatory: + the README, `skills/stash-supabase/SKILL.md`, and — in `skills/stash-edge/SKILL.md` + — its frontmatter description, its Credentials section, its troubleshooting + advice, and the native-vs-WASM comparison table. That skill already contradicted + itself, since its own `config.authStrategy` example passes two values, not four. + The same sentence in the `EncryptedSupabaseWasmOptions` doc comment + (`packages/stack-supabase/src/wasm-inline.ts`) is fixed too, comment-only. Only + `clientId` and `clientKey` are always required. Beyond them the config is a union — the + access-key path adds `workspaceCrn` + `accessKey`, and the strategy path takes + a pre-built `config.authStrategy` and makes `workspaceCrn` optional, because a + built strategy already carries the CRN. `OidcFederationStrategy` is re-exported + from `@cipherstash/stack/wasm-inline`, so authenticating as the end user works + on the edge; what does not work is binding data to that user, which stays + called out in its own `.withLockContext()` bullet. + + Tests now anchor the corrected claims against the code rather than against + prose. `packages/stack-supabase/__tests__/supabase-wasm-config.test-d.ts` asserts + at the type level that the edge `config` accepts both the access-key arm and a + strategy-only arm without `workspaceCrn`, and rejects `clientId` + `clientKey` + alone. `supabase-declared-mode.test.ts` gains a case pinning the real hazard: an + undeclared column on a declared table reaches PostgREST as plaintext on insert, + update and filter, and is absent from the decrypt call. + + Review found four more, one of them a change to a published type: + + - **`databaseUrl` was only refused for callers who wrote the options inline.** + `EncryptedSupabaseWasmOptions` left the field out, and omission is policed by + excess-property checking, which fires on fresh object literals alone. An + options object assembled as a `const` and passed by variable — which is what + a Node-to-edge port actually holds — type-checked clean and reached the + construction-time throw instead, from documents saying the type checker + enforced it. The field is now declared `databaseUrl?: never`, mirroring + `WasmClientConfig.eqlVersion?: never` in `@cipherstash/stack`, which exists + for the identical reason one package along. The runtime throw stays as the + backstop for plain JS. New type tests cover the inline and by-variable + shapes on both call forms, plus a positive control that the same options + object still compiles once `databaseUrl` is dropped. + - **The `select('*')` correction had landed in only one of its two shipped + copies.** `skills/stash-supabase/SKILL.md` carried it; + `skills/stash-managed-platforms/SKILL.md`, edited in the same change, still + framed declared mode as giving things up "loudly rather than silently" over a + bullet naming the refusal — precisely the inference the correction exists to + kill. That skill is read as instruction by an agent on Lovable, v0, Bolt or + Replit, and the failure it mispromised is silent: raw EQL payloads returned + as `data`, no error. The wording is carried across, and a new guard + (`scripts/__tests__/skills-select-star-not-a-read-backstop.test.mjs`) fails + if any shipped document states the refusal without the caveat in the same + section. Two copies of one fact drift the moment one of them is edited, and + no reviewer diff shows the copy nobody touched. + - **"Everything after construction is the same wrapper" was false in the first + way a reader hits it.** Both `packages/stack-supabase/README.md` and + `skills/stash-supabase/SKILL.md` said `from()`, the filters and the response + shape are identical across the two entries — the README saying so twenty-five + lines under a paragraph telling the same reader `select('*')` just works. The + edge entry is always in declared mode, where `select('*')` is refused and + `from()` on an undeclared table throws. Both sentences now name the two + exceptions, so the quick-start snippet the section tells you to port no + longer arrives with a promise it breaks. + - **The ambient-`DATABASE_URL` warning cannot fire on the edge entry.** + `skills/stash-managed-platforms/SKILL.md` said an ambient `DATABASE_URL` is + ignored when `schemas` are passed, "with a warning that the declaration is + unverified" — in a section whose subject is `wasm-inline`. Both the ambient + read and the warning are gated on the introspector, which that build does not + have, so nothing there ever tells you a declaration is incomplete. Now scoped + to Node, with the edge case stated. The sentence immediately above it had the + same fault and is fixed with it: the ⚠️ callout on undeclared columns offered + "pass `databaseUrl` so introspection fills the gaps" as the remedy, inside a + section about the entry that refuses `databaseUrl` — and it is the remedy a + Lovable or Replit agent, which has only the edge entry, would have reached + for. It now says introspection is unavailable there and what to do instead. + + `@cipherstash/wizard` is bumped alongside `stash` because `skills/` ships in + both tarballs — `packages/wizard/tsup.config.ts` copies it into `dist/skills` + and `package.json` lists that under `files`. Without the bump the published + wizard keeps shipping the old text until some unrelated change moves its + version. + + One more wording correction, of the kind #952 fixed in this package's `.d.ts`: + `packages/stack-supabase/README.md` and `skills/stash-supabase/SKILL.md` both + derived "runs on Node only" from introspection — "introspection needs a direct + Postgres connection, **so** … this entry runs on Node only". Introspection is + not the cause. The entry binds the native engine, so it is Node-only whether or + not you declare `schemas`; a reader who took the stated cause at face value + would conclude that declaring tables makes the root entry edge-capable, which is + the exact wrong turn the `wasm-inline` entry exists to prevent. Both sentences + now attribute the restriction to the engine and say that declaring `schemas` + does not move it. Both files are enrolled in #952's + `scripts/__tests__/supabase-runtime-claims.test.mjs`, which is what its own + comment said to do once this branch stopped rewriting the same lines — the + README and this skill are the two copies that reach a customer, and the guard + now fails if either grows the claim back. `skills/stash-managed-platforms/SKILL.md` + stays out, with the reason written down: its causal claims are correct, but + rewording the unqualified "Worker" in its frontmatter `description` changes + what the skill matches on, which is not a rider on a documentation fix. + + No runtime behaviour changes. The one non-documentation change is the + `databaseUrl?: never` field on `EncryptedSupabaseWasmOptions`, which is + type-level: it rejects at compile time a call that already threw at + construction. + ## 1.1.1 ## 1.1.0 diff --git a/packages/wizard/package.json b/packages/wizard/package.json index 46b23ec08..5bc82a125 100644 --- a/packages/wizard/package.json +++ b/packages/wizard/package.json @@ -1,6 +1,6 @@ { "name": "@cipherstash/wizard", - "version": "1.1.1", + "version": "1.2.0", "description": "AI-powered encryption setup for CipherStash. Reads your codebase, picks columns to encrypt, and wires everything up.", "repository": { "type": "git", From 4aaf7dcafcf528c22c1c32abcbcd428f5a786d7f Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Fri, 2 Oct 2026 15:43:50 +1000 Subject: [PATCH 2/3] docs(skills): pin the 1.2.0 release in the stash-cli and stash-edge skills The skills ship inside the stash tarball and nothing rewrites their version literals, so the release-train test requires the stash-cli skill to pin the stash version being released. Move the stash-cli one-shot install and the two stash-edge @cipherstash/stack imports from 1.1.1 to 1.2.0, as the 1.1.1 Version Packages PR (#928) did for its release. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a --- skills/stash-cli/SKILL.md | 2 +- skills/stash-edge/SKILL.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/skills/stash-cli/SKILL.md b/skills/stash-cli/SKILL.md index d9a3a0567..c3435bfa8 100644 --- a/skills/stash-cli/SKILL.md +++ b/skills/stash-cli/SKILL.md @@ -396,7 +396,7 @@ Gets a project from zero to a direct EQL v3 install. It loads an existing `stash The removed `--eql-version`, `--latest`, `--drizzle`, `--migration`, `--direct`, `--migrations-dir`, and `--exclude-operator-family` options fail clearly instead of being ignored. A request for EQL v2 points dump-recovery users to the upstream EQL 2.3.1 SQL release. New installs are EQL v3 only; its pinned bundle self-adapts when a database role cannot create the optional operator family. -**`--database-url` is a one-shot.** It installs against that database and leaves the project untouched — no config is loaded, and none is scaffolded, nor is an encryption client. This lets `npx --package=stash@1.1.1 stash eql install --database-url 'postgres://...'` run in a bare project with no CipherStash dependencies while pinning the CLI to this skill's release. It also means the flag always wins: loading a config could pick up a parent-directory `databaseUrl` literal and install against the wrong database. +**`--database-url` is a one-shot.** It installs against that database and leaves the project untouched — no config is loaded, and none is scaffolded, nor is an encryption client. This lets `npx --package=stash@1.2.0 stash eql install --database-url 'postgres://...'` run in a bare project with no CipherStash dependencies while pinning the CLI to this skill's release. It also means the flag always wins: loading a config could pick up a parent-directory `databaseUrl` literal and install against the wrong database. **The install verifies the bundle before it runs it.** Before connecting, `eql install` hashes the SQL it resolved from `@cipherstash/eql` and compares it against the `installSqlSha256` that release attests to. A mismatch refuses — naming both digests and the resolved path — rather than executing SQL the version number does not vouch for; nothing is opened and nothing is sent. In practice this only fires on a corrupt or tampered `node_modules`, since the bundle and its manifest are emitted by the same build. diff --git a/skills/stash-edge/SKILL.md b/skills/stash-edge/SKILL.md index 81ddbfe6c..de81dd4aa 100644 --- a/skills/stash-edge/SKILL.md +++ b/skills/stash-edge/SKILL.md @@ -76,7 +76,7 @@ build step. ```ts import { Encryption, encryptedTable, types, isEncrypted, -} from 'npm:@cipherstash/stack@1.1.1/wasm-inline' +} from 'npm:@cipherstash/stack@1.2.0/wasm-inline' ``` **Pin an exact version.** Deno caches by specifier, so an unpinned import @@ -91,7 +91,7 @@ name everywhere: ```jsonc { "imports": { - "@cipherstash/stack/wasm-inline": "npm:@cipherstash/stack@1.1.1/wasm-inline" + "@cipherstash/stack/wasm-inline": "npm:@cipherstash/stack@1.2.0/wasm-inline" } } ``` From a294d964f7ab0e18f0720e0a51dc7526314e76d2 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Fri, 2 Oct 2026 16:32:57 +1000 Subject: [PATCH 3/3] chore(release): release @cipherstash/nextjs as 4.4.0, not 4.2.0 npm already has @cipherstash/nextjs 4.2.0 and 4.3.0. Both were published by hand on 10 December 2025, without provenance, as republishes of 4.1.0's source, and neither version was ever recorded in this repository. changeset publish skips a version that npm already has, so 4.2.0 would have shipped none of this release's changes. 4.2.0 also has no dist/, so it cannot be imported. npm resolves ^4.1.x to 4.3.0 today. Release as 4.4.0, the next minor above every published 4.x version. Against 4.3.0, the public API change is additive: an optional authCode on GetCtsTokenResponse. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a --- packages/nextjs/CHANGELOG.md | 2 +- packages/nextjs/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nextjs/CHANGELOG.md b/packages/nextjs/CHANGELOG.md index 028fd5306..5014b82af 100644 --- a/packages/nextjs/CHANGELOG.md +++ b/packages/nextjs/CHANGELOG.md @@ -1,6 +1,6 @@ # @cipherstash/nextjs -## 4.2.0 +## 4.4.0 ### Minor Changes diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 6f6b2529c..a23c3597a 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@cipherstash/nextjs", - "version": "4.2.0", + "version": "4.4.0", "description": "Nextjs package for use with @cipherstash/stack", "keywords": [ "encrypted",