Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/pre.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@
"cli-anonymous-telemetry",
"cli-eql-v3-single-bundle",
"codex-skills-eperm",
"drizzle-encrypted-indexes",
"drizzle-kit-eql-v3-ddl",
"dynamodb-eql-v3",
"eql-baked-digest-verified",
"eql-migration-command",
"eql-v3-adapter-type-robustness",
"eql-v3-bigint-domains",
Expand Down Expand Up @@ -59,17 +62,21 @@
"khaki-pugs-play",
"migrate-eql-v3",
"plan-complete-rollout-yes",
"plan-honest-outcome",
"prisma-example-eql-v3",
"prisma-next-0-14",
"prisma-next-0-16",
"prisma-next-drop-encrypted-prefix",
"prisma-next-eql-runtime-source",
"prisma-next-joins-release-train",
"prisma-next-migrate-command",
"prisma-next-v3-only-install",
"protect-ffi-030-json-selectors",
"release-train-coupling",
"remove-legacy-drizzle-package",
"remove-secrets-leftovers",
"rename-db-install-to-eql-install",
"schema-build-v3-domain-picker",
"schema-stevec-standard-pin",
"skills-eql-v3-accuracy",
"skills-identity-docs-refresh",
Expand All @@ -79,6 +86,7 @@
"stash-cli-skill-refresh",
"stash-drizzle-skill-encrypt-query",
"stash-env-mint-credentials",
"stash-indexing-skill",
"stash-prisma-next-skill-v3-only",
"stash-prisma-next-skill",
"stash-skills-contains-to-matches",
Expand All @@ -89,9 +97,12 @@
"supabase-or-string-parser",
"supabase-v3-json-querying",
"supabase-v3-order-by-ope-term",
"v3-payload-routing",
"v3-supabase-needle-lockcontext-errors",
"wasm-encrypt-query",
"wasm-inline-bulk-ops",
"wasm-inline-model-helpers",
"wild-donkeys-shave",
"wizard-allow-env-templates",
"wizard-analytics-privacy",
"wizard-joins-release-train"
Expand Down
16 changes: 16 additions & 0 deletions e2e/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# @cipherstash/e2e

## 0.0.3-rc.5

### Patch Changes

- Updated dependencies [d26950d]
- Updated dependencies [d6bc9e9]
- Updated dependencies [b7fa61f]
- Updated dependencies [b7fa61f]
- Updated dependencies [f78fd7a]
- Updated dependencies [239f79b]
- Updated dependencies [d25d100]
- Updated dependencies [f628463]
- @cipherstash/stack@1.0.0-rc.5
- stash@1.0.0-rc.5
- @cipherstash/wizard@1.0.0-rc.5

## 0.0.3-rc.4

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion e2e/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cipherstash/e2e",
"version": "0.0.3-rc.4",
"version": "0.0.3-rc.5",
"private": true,
"description": "End-to-end tests that exercise built CipherStash binaries and cross-package behaviour.",
"type": "module",
Expand Down
11 changes: 11 additions & 0 deletions examples/basic/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# @cipherstash/basic-example

## 1.2.14-rc.5

### Patch Changes

- Updated dependencies [239f79b]
- Updated dependencies [d26950d]
- Updated dependencies [d25d100]
- @cipherstash/stack-drizzle@1.0.0-rc.5
- @cipherstash/stack@1.0.0-rc.5
- @cipherstash/stack-supabase@1.0.0-rc.5

## 1.2.14-rc.4

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion examples/basic/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@cipherstash/basic-example",
"private": true,
"version": "1.2.14-rc.4",
"version": "1.2.14-rc.5",
"type": "module",
"scripts": {
"start": "tsx index.ts"
Expand Down
13 changes: 13 additions & 0 deletions examples/prisma/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# @cipherstash/prisma-next-example

## 0.1.0-rc.5

### Patch Changes

- Updated dependencies [d26950d]
- Updated dependencies [b7fa61f]
- Updated dependencies [b7fa61f]
- Updated dependencies [b7fa61f]
- Updated dependencies [b7fa61f]
- Updated dependencies [d25d100]
- @cipherstash/stack@1.0.0-rc.5
- @cipherstash/prisma-next@1.0.0-rc.5

## 0.1.0-rc.4

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion examples/prisma/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@cipherstash/prisma-next-example",
"private": true,
"version": "0.1.0-rc.4",
"version": "0.1.0-rc.5",
"description": "End-to-end example of @cipherstash/prisma-next: searchable application-layer encryption for Postgres with Prisma Next, using @cipherstash/stack as the SDK.",
"type": "module",
"scripts": {
Expand Down
10 changes: 10 additions & 0 deletions packages/bench/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# @cipherstash/bench

## 0.0.5-rc.5

### Patch Changes

- Updated dependencies [239f79b]
- Updated dependencies [d26950d]
- Updated dependencies [d25d100]
- @cipherstash/stack-drizzle@1.0.0-rc.5
- @cipherstash/stack@1.0.0-rc.5

## 0.0.5-rc.4

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/bench/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cipherstash/bench",
"version": "0.0.5-rc.4",
"version": "0.0.5-rc.5",
"private": true,
"description": "Performance / index-engagement benchmarks for stack integrations (Drizzle, encryptedSupabase, Prisma).",
"type": "module",
Expand Down
173 changes: 173 additions & 0 deletions packages/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,178 @@
# @cipherstash/cli

## 1.0.0-rc.5

### Minor Changes

- 239f79b: New bundled agent skill: `stash-indexing` — how to index EQL v3 encrypted
columns. Integrations that were otherwise correct shipped with no index on any
encrypted predicate because nothing in the installed skills said encrypted
columns _can_ be indexed (#753). The skill covers the functional-index recipes
over the term extractors (`eql_v3.eq_term` / `ord_term` / `ord_term_ore` /
`match_term` / `to_ste_vec_query`) mapped to the `types.*` domains, what works
without superuser on Supabase and managed Postgres versus the ORE opclass
restriction, which domains are storage-only by design, the query shapes that
engage an index (`ORDER BY` sort-key and `GROUP BY` traps), building indexes on
large tables, an `EXPLAIN` verification checklist, and when to create indexes
during an encryption rollout (after backfill, before switching reads).

`stash init` / `stash impl` handoffs — and the `@cipherstash/wizard` skills
prompt — now install it for **every** integration (Drizzle, Supabase, Prisma
Next, plain PostgreSQL) — the gap is cross-cutting.
The existing per-integration skills gained pointers to it (including the
missing `stash-prisma-next` one-line purpose in the setup prompt, which
previously rendered "(no description)").

### Patch Changes

- d26950d: `encryptedDynamoDB` now accepts EQL v3 tables.

Pass a table built with `encryptedTable` + the `types.*` domains from
`@cipherstash/stack/v3` (or `@cipherstash/stack/eql/v3`) to any of
`encryptModel`, `bulkEncryptModels`, `decryptModel`, `bulkDecryptModels`. Both
the typed client from `EncryptionV3` and the nominal client from
`Encryption({ config: { eqlVersion: 3 } })` are accepted.

EQL v2 tables continue to work unchanged — this is additive, and no existing
caller needs to change. The table decides which wire format is used, so a
DynamoDB table populated under one version must keep being read with that
version.

This fixes a latent bug that made v3 unusable: the write path detected an
encrypted value by its `k: 'ct'` tag, but EQL v3 scalars carry no `k`
discriminator at all. Every v3 scalar fell through to the nested-object branch
and was written as a raw map instead of being split into `<attr>__source` and
`<attr>__hmac`.

Notes on capability:

- Only equality is usable on DynamoDB. `<attr>__hmac` is written for domains
that mint an `hm` term — the `*Eq` family, plus `TextOrd`/`TextOrdOre`/
`TextSearch`. Ordering and bloom-filter terms have no DynamoDB query surface
and are not stored, so those columns remain decryptable but not queryable.
- Nested attributes are supported in v3. There is no nested-group authoring
form (that is a compile error), so declare the column flat with a dotted
path — `{ 'profile.ssn': types.TextEq('profile.ssn') }`. The model is
matched by dotted path, so `{ profile: { ssn } }` resolves, and the nested
attribute keeps its `__hmac` for key conditions.
- Audit metadata on `decryptModel` / `bulkDecryptModels` requires the nominal
client; the `EncryptionV3` client has no audit surface on decrypt.

The DynamoDB adapter also gains its first test coverage — across the v2 and v3
paths, where it previously had none.

Robustness, from review:

- Passing a v3 table to a client that never registered it (one built for a
different schema set, so it is not in v3 mode for that table) now throws a
clear, actionable error naming the table, instead of failing opaquely deep in
the FFI.
- A malformed decrypt result from a non-conforming client is surfaced as a
failure rather than resolving as a silent `undefined` success.
- Reading back a `<attr>__source` attribute that matches no declared column now
logs a debug diagnostic instead of silently returning the raw ciphertext.
- Caller input that cannot be structurally cloned no longer reaches the FFI by
reference — the "encryption never mutates a caller's object" guarantee holds
on that path too.
- The write path now splits only declared columns, matched on the same property
path the read path rebuilds from. A pre-encrypted payload placed under an
undeclared nested name is stored whole (and round-trips) instead of being
split into a `<attr>__source` the read path could never reassemble.
- A degenerate payload with an empty-string ciphertext is split like any other
ciphertext rather than falling through and being written as a raw map, which
had leaked its `v`/`i` envelope metadata into storage.
- Arrays are documented as a deliberate carve-out: the mapping does not descend
into them, so a payload inside a list is stored whole (still decryptable, but
not queryable and not part of the `__source`/`__hmac` layout).

The v3 overloads are strongly typed. `encryptModel` / `bulkEncryptModels` check
the input model against the table's column domains, and return the DynamoDB
attribute map that is actually written — the new exported `EncryptedAttributes`
type, where a declared column `email` becomes `email__source` (plus
`email__hmac` for the equality domains that mint one) rather than surviving as
`email`. `decryptModel` / `bulkDecryptModels` invert it via `DecryptedAttributes`.
`AnyEncryptedTable`, `DynamoDBEncryptionClient` and `AuditConfig` are now
exported from `@cipherstash/stack/dynamodb` so these signatures can be named.
The EQL v2 overloads are unchanged.

- d6bc9e9: `stash plan` now reports the outcome that actually occurred instead of unconditionally printing `Plan drafted at .cipherstash/plan.md` and exiting 0 (#738). The plan file is written by the handed-off agent, so the command verifies it on disk after the handoff: "Plan drafted" appears only when the file exists; if a launched agent (Claude Code, Codex, or the wizard) exits without writing it, `plan` errors and exits non-zero so automation never proceeds against a plan that was never created; deferred handoffs (`--target agents-md`, or a CLI target that isn't installed) end with an honest "No plan drafted yet" hint; and a pre-existing plan the run didn't modify is reported as left unchanged rather than drafted. An unexpected filesystem error while reading the plan path (a locked or malformed `.cipherstash/`) now exits non-zero with a clear message rather than an opaque crash.
- b7fa61f: Upgrade the Prisma Next integration to Prisma Next 0.16.

All `@prisma-next/*` dependencies move from `0.14.0` to `0.16.0`, in lockstep. The
CipherStash encryption surface is unchanged — column types, envelopes, the `eql*`
operators, `cipherstashFromStack`, and every subpath export behave exactly as before.

**Action required in your `prisma-next.config.ts`:** Prisma Next 0.15 stopped
materialising a placeholder namespace, so authoring a SQL contract now requires the
target's namespace factory. Add `createNamespace` to your `prismaContract(...)` call:

```typescript
import { postgresCreateNamespace } from '@prisma-next/target-postgres/types'

contract: prismaContract('./prisma/schema.prisma', {
output: 'src/prisma/contract.json',
target: postgresPack,
createNamespace: postgresCreateNamespace,
}),
```

Without it, `prisma-next contract emit` fails at runtime with `createNamespace is
not a function`. The bundled `stash-prisma-next` skill documents this too.

The bundled EQL v3 baseline migration is re-emitted so its label and hash reflect
the pinned `@cipherstash/eql` 3.0.2 (the committed artifact still said 3.0.0).

Re-run `prisma-next contract emit` after upgrading. The regenerated
`contract.{json,d.ts}` picks up the 0.15/0.16 shape changes — the namespace
discriminator becomes the target-specific `'postgres-schema'` (was
`'sql-namespace'`), emit adds the `StorageColumnTypes` / `StorageColumnInputTypes`
maps and the `scalarList` capability marker, and foreign keys and their backing
indexes become discrete contract entities. Your contract's `storageHash` is
unaffected by the upgrade itself.

- b7fa61f: Fix the wrong `prisma-next migration apply` command name in the Prisma Next
guidance. Prisma Next has no `migration apply` subcommand — the apply verb is the
top-level `prisma-next migrate` (`migration` only has `plan`/`new`/`show`/
`status`/`log`/`list`/`graph`/`check`). The stale name appeared in the
`stash-prisma-next` and `stash-cli` skills, the `@cipherstash/prisma-next`
README, and — user-visibly — in `stash init --prisma-next`'s printed next-steps,
the `stash init` flag help, and the `stash eql install` Prisma-Next refusal
message, all of which now say `prisma-next migrate`. Surfaced by the rc.4
skilltester run (found independently at Prisma Next 0.14.0, confirmed at 0.16.0).
- f78fd7a: `stash schema build` now picks a concrete EQL v3 domain per column
(`TextSearch`, `IntegerOrd`, `TextEq`, …) instead of the legacy v2
"searchable capabilities" toggle. Boolean columns are assigned the
storage-only `types.Boolean` domain automatically, while JSON columns are
assigned the queryable `types.Json` domain, with encrypted containment and
selector queries. Other columns default to the widest searchable domain,
matching the previous behaviour. The internal `SearchOp` capability tuple
and the `v3DomainFactory` translation shim are removed, unblocking EQL v2
removal (#707, #751).
- d25d100: `@cipherstash/stack/wasm-inline` now has the model helpers: `encryptModel` / `decryptModel` and `bulkEncryptModels` / `bulkDecryptModels` (#742). They run the same schema traversal as the native entry (shared code, so the two entries cannot drift on which fields get encrypted): declared columns are encrypted — matched by JS property name, nested fields via the column's dotted path — everything else passes through, and `null`/`undefined` fields are preserved without reaching ZeroKMS. A call that encrypts (or decrypts) at least one field is one ZeroKMS round trip regardless of how many fields or models it covers; a `null`/empty batch, or one whose models carry no schema fields, returns without contacting ZeroKMS at all. `types.Date`/`types.Timestamp` columns round-trip `Date` → `Date` (ISO strings on the wire), and failures follow this entry's `{ data } | { failure }` Result contract, with decrypt failures naming every failing field by its model path. Edge code no longer needs the hand-written `bulkEncrypt` field mapping whose failure mode was a schema column silently persisted in plaintext.

The shared model traversal is also hardened: it no longer mutates the caller's model (previously a nested-column decrypt wrote decrypted plaintext back into the caller's input, and encrypt overwrote it with ciphertext); a literal flat dotted key, a `__proto__`-shaped key, or a non-object model element is handled safely instead of crashing, leaking plaintext, or reaching `Object.prototype`; an already-encrypted field is passed through rather than re-encrypted; and an invalid `Date` is rejected per field. On the WASM entry, model ops now validate the table against the client's schemas, `Date` values are normalized at every encrypt/query crossing (not just the model path), and a `null`/empty model batch returns `{ data: [] }`. The skills update ships in the `stash` tarball, hence the `stash` patch.

- f628463: Fix invalid DDL when a Drizzle column changes to an EQL v3 domain.

`drizzle-kit generate` emits an in-place `ALTER TABLE … ALTER COLUMN … SET DATA TYPE`
when a plaintext column is changed to an encrypted one, which Postgres rejects — there
is no cast from `text`/`numeric` to an EQL type, and on drizzle-kit 0.31.0+ the emitted
type name is additionally mangled to `"undefined"."eql_v3_<name>"`. The migration
rewriter only recognised the EQL v2 type, so a v3 user was left with an un-runnable
migration and nothing to repair it.

The rewriter now matches the whole `eql_v3_*` domain family alongside `eql_v2_encrypted`,
across every mangled form observed from drizzle-kit 0.24 through 0.31, and emits the
matched domain in the replacement instead of a hardcoded v2 type. `stash eql migration
--drizzle` — the EQL v3 migration-first path — now runs the same sweep that `eql install
--drizzle` has always run, so the repair actually reaches v3 projects.

The rewrite's guidance comment now also warns that it drops the plaintext column in the
same migration, and points at the staged `stash encrypt` path (add → backfill → cutover →
drop) for populated production tables.

- @cipherstash/migrate@1.0.0-rc.1

## 1.0.0-rc.4

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "stash",
"version": "1.0.0-rc.4",
"version": "1.0.0-rc.5",
"description": "CipherStash CLI — the one stash command for auth, init, encryption schema, database setup, and secrets.",
"repository": {
"type": "git",
Expand Down
Loading