Skip to content

Version Packages (rc)#750

Open
github-actions[bot] wants to merge 1 commit into
mainfrom
changeset-release/main
Open

Version Packages (rc)#750
github-actions[bot] wants to merge 1 commit into
mainfrom
changeset-release/main

Conversation

@github-actions

@github-actions github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

main is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on main.

⚠️⚠️⚠️⚠️⚠️⚠️

Releases

@cipherstash/stack@1.0.0-rc.5

Minor 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.

  • d25d100: @cipherstash/stack/wasm-inline now has the model helpers: encryptModel / decryptModel and bulkEncryptModels / bulkDecryptModels (wasm-inline has no model helpers: encryptModel/decryptModel (and their bulk forms) are Node-only #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 DateDate (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.

stash@1.0.0-rc.5

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 (stash plan reports "Plan drafted at .cipherstash/plan.md" and exits 0 without writing the file (rc.3 M2) #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.

  • 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 (Remove EQL v2 repo-wide (umbrella) #707, stash schema build: port the v2 capability column picker to EQL v3 domains #751).

  • d25d100: @cipherstash/stack/wasm-inline now has the model helpers: encryptModel / decryptModel and bulkEncryptModels / bulkDecryptModels (wasm-inline has no model helpers: encryptModel/decryptModel (and their bulk forms) are Node-only #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 DateDate (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

@cipherstash/prisma-next@1.0.0-rc.5

Patch Changes

  • Updated dependencies [d26950d]
  • Updated dependencies [d25d100]
    • @cipherstash/stack@1.0.0-rc.5

@cipherstash/stack-drizzle@1.0.0-rc.5

Patch Changes

  • Updated dependencies [d26950d]
  • Updated dependencies [d25d100]
    • @cipherstash/stack@1.0.0-rc.5

@cipherstash/stack-supabase@1.0.0-rc.5

Patch Changes

  • Updated dependencies [d26950d]
  • Updated dependencies [d25d100]
    • @cipherstash/stack@1.0.0-rc.5

@cipherstash/wizard@1.0.0-rc.5

@cipherstash/e2e@0.0.3-rc.5

Patch Changes

  • Updated dependencies [d26950d]
  • Updated dependencies [d6bc9e9]
  • Updated dependencies [f78fd7a]
  • 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

@cipherstash/basic-example@1.2.14-rc.5

Patch Changes

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

@cipherstash/prisma-next-example@0.1.0-rc.5

Patch Changes

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

@cipherstash/bench@0.0.5-rc.5

Patch Changes

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

@cipherstash/test-kit@0.0.1-rc.5

Patch Changes

  • Updated dependencies [d26950d]
  • Updated dependencies [d25d100]
    • @cipherstash/stack@1.0.0-rc.5

@github-actions
github-actions Bot requested a review from a team as a code owner July 21, 2026 23:41
@github-actions
github-actions Bot force-pushed the changeset-release/main branch 3 times, most recently from aa6b844 to 1f5c27e Compare July 23, 2026 05:10
@github-actions
github-actions Bot force-pushed the changeset-release/main branch from 1f5c27e to d01cc69 Compare July 23, 2026 05:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants