Skip to content

test(protect-ffi): migrate Protect.js 10.2.1 EQL v2 data to v4 end to end - #1497

Draft
tobyhede wants to merge 1 commit into
test/eql-v3-to-v4-migrationfrom
test/eql-v2-to-v4-migration
Draft

tobyhede wants to merge 1 commit into
test/eql-v3-to-v4-migrationfrom
test/eql-v2-to-v4-migration

Conversation

@tobyhede

@tobyhede tobyhede commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Proves that data written by Protect.js, the client before Stack, can be moved to the new EQL v4 format and keeps answering the same queries. EQL is the format CipherStash uses to store encrypted values in Postgres. Protect.js wrote EQL v2, and v4 is what the new Stack Encrypt engine writes. Getting from one to the other means decrypting and re-encrypting every value. The v2 rows here are written by the real @cipherstash/protect@10.2.1, not by today's client pretending to be old.

Stacked on #1496 (EQL v3 → v4). Review that one first. This PR's diff is only the v2 part.

Changes

  • New live test integration-tests/tests/migrate-v2-to-v4.test.ts (9 tests), following the same steps as the v3 suite:
    • Protect 10.2.1 writes rows into eql_v2_encrypted columns, and its own createSearchTerms queries them as a baseline.
    • Each value is decrypted with in-tree protect-ffi (the engine Stack 1.x encrypts through, which still reads v2) and re-encrypted as v4 with the encoder from test(protect-ffi): migrate live EQL v3 data to v4 end to end #1496.
    • Equality, ORE range and ordering, and text-match queries must return the same rows from both columns.
    • Every v4 value must decrypt to the original, and the v4 columns must still answer after the rename.
  • Column mapping. v2 stores every column as one type, and v4 has a type per capability, so the test picks one per column: equality() → TextEq, freeTextSearch() → TextMatch, a plain boolean → Boolean. Protect's dataType('number') encrypted a 64-bit float, so orderAndRange() on it becomes DoubleOrdOre, not an integer type.
  • Hazards pinned: casting a v2 payload into a v4 column fails the domain check, and changing v to 4 gives a value Stack Encrypt refuses to open.
  • tests/v4-codec.ts adds DoubleOrdOre to the type list.

Supply chain note

New dev-only dependency of the private @cipherstash/ffi-integration-tests package, which is never published: @cipherstash/protect@10.2.1, exact-pinned. It is the same pin #1436 adds to @cipherstash/e2e. Its transitive packages (@cipherstash/protect-ffi@0.18.1 and its platform packages, @cipherstash/schema, and others) are added to pnpm-lock.yaml. No install scripts, and nothing added to onlyBuiltDependencies.

Protect 10.2.1 is loaded with import(), because its types export condition points at ESM declarations, which TypeScript won't import statically into this CommonJS suite. Its plaintext type leaves out boolean even though it encrypts booleans (#1436 pins that), so the test casts at that one call.

No changeset: only test code and a private package's dev dependencies changed.

Verification

  • pnpm run typecheck in integration-tests: clean. pnpm run test:scripts: 85 files passed. pnpm install --frozen-lockfile: clean.
  • Live, in CI only: integration-protect-ffi.yml run 41371866544 passed. migrate-v2-to-v4.test.ts passed 9/9 and migrate-v3-to-v4.test.ts 11/11, with 22 suite files passing in total. It wasn't run locally: Protect 10.2.1 reads credentials only from CS_* environment variables, and those weren't set on the local machine.

Related

Stacked on #1496. Refs #1436.

… end

The v3 -> v4 suite covers data Stack 1.x wrote. Customers still on
Protect.js hold EQL v2 data written by an older client (protect-ffi
0.18.1, cipherstash-client 0.28), and nothing showed that data can
reach v4 and keep answering the same queries.

The new suite writes v2 rows with the real @cipherstash/protect@10.2.1
into eql_v2_encrypted columns and queries them with its own search
terms. It then runs the same migration as the v3 suite: add a v4 column
per v2 column, decrypt with the engine Stack 1.x encrypts through
(in-tree protect-ffi, which still reads v2), re-encrypt through the v4
codec, and check equality, ORE range and ordering, and match queries
return the same rows from both columns. Every v4 value must decrypt to
the original plaintext, and the v4 columns must still answer after the
cut-over rename. Protect's dataType('number') encrypted a float64, so
that column migrates to DoubleOrdOre rather than an integer type.

v2 hazards are pinned too: a v2 payload cast into a v4 column fails the
domain CHECK, and relabelling `v` gives a value Stack Encrypt refuses.

@cipherstash/protect@10.2.1 is added exact-pinned as a dev dependency of
the private integration-tests package. It is loaded through import()
because its `types` export condition names ESM declarations.
@changeset-bot

changeset-bot Bot commented Nov 2, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 7304baa

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

This branch has not been deployed

No deployments
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.

1 participant