Skip to content

feat(eql): add Rust text encryption and queries - #971

Draft
coderdan wants to merge 3 commits into
mainfrom
codex/eql-texteq-transcoding
Draft

feat(eql): add Rust text encryption and queries#971
coderdan wants to merge 3 commits into
mainfrom
codex/eql-texteq-transcoding

Conversation

@coderdan

@coderdan coderdan commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Rust callers can now encrypt text, find exact matches in PostgreSQL, and decrypt retrieved values through Stack Encrypt, CipherStash's Rust encryption engine. TextEq and TextEqQuery produce the stored-value and search payloads required by EQL (Encrypt Query Language), the database library for encrypted queries. The change reuses Vitamin C, Stack Encrypt's cryptography library, for plaintext encoding.

Changes

  • Rust API: add optional derives for TextEq and TextEqQuery; the table and column identifier supplies context and is stored in i. Decryption rejects empty or unexpected identifiers before requesting keys.
  • Encrypted output: convert native ciphertext and equality terms into the final EQL fields. Ciphertext uses a versioned base64 encoding; equality terms use hex. Search payloads contain no recoverable ciphertext and request no data keys.
  • Generated types: emit the derives from eql-codegen and refresh the shared ciphertext description in TypeScript types and JSON schemas. Payload shapes stay the same.
  • Tests and CI: cover native/EQL cross-decryption, saved JSON payloads, invalid input, batching, PostgreSQL equality, and index use. Add WebAssembly compilation and make the new multi-command workflow steps stop on the first failure.
  • Documentation and release: add an executable Rustdoc example covering cipher setup, column identifiers, encryption, queries, and decryption. Generated type docs state the String plaintext contract and link to the corresponding CipherStash EQL reference; docs.rs enables the encryption feature. Add a helper for the unpublished dependency, a minor changeset for @cipherstash/eql, and the internal test crate/example inventory in AGENTS.md and SECURITY.md.

Verification

Rust checks used toolchain 1.90.0 and the suite checkout at cbb3e000009b5974e90ec7da98d015b4b19a7270. Cargo test/build commands used --offline --locked with temporary dependency overrides supplied by bash tasks/dev/with-stack-encrypt.sh <suite-checkout>. Commands below run from packages/eql, except the pnpm commands, which run from the repository root.

Command/check Result
cargo test -p eql-encryption-tests --test text_eq_example Passed: executed the exact source rendered in the encryption guide, including cipher setup, stored identifiers, matching query terms, and decryption. This example is excluded from eql-bindings doctests because its test key source and runtime live in the separate test crate.
cargo test -p eql-encryption-tests --test text_eq -- --include-ignored All 7 passed. The database test used fresh real encryption, a fake key source, and disposable PostgreSQL; verified =, <>, retrieval/decryption, and equality-index use.
cargo test -p eql-bindings --features stack-encrypt and cargo test -p eql-bindings --no-default-features Passed with the feature enabled and disabled; all 4 compile-fail examples passed.
cargo test -p eql-codegen -p eql-bindings --features eql-bindings/stack-encrypt Passed after the documentation changes: 321 tests, including all 4 compile-fail examples. The successful example is run separately by text_eq_example, as described above.
cargo test -p eql-codegen -p eql-domains Passed.
cargo clippy -p eql-bindings -p eql-encryption-tests -p eql-codegen --all-features --all-targets -- -D warnings Passed.
cargo fmt --all -- --check; RUSTDOCFLAGS='-D warnings' cargo doc -p eql-bindings --no-deps with --features stack-encrypt and with --no-default-features Passed. Inspected the generated plaintext/context descriptions, example anchors, and site links on all 93 domain/query pages.
cargo check -p eql-bindings --features stack-encrypt --target wasm32-wasip1 Passed without enabling Stack Encrypt's HTTP transport.
Regenerate Rust bindings and schemas; node packages/eql/scripts/sync-generated.mjs --check; compare generated files with Git Passed after the documentation commit; no drift in generated Rust, TypeScript, or JSON Schema.
cargo metadata --locked --format-version 1 --filter-platform aarch64-apple-darwin in packages/protect-ffi Existing consumer resolved with its unchanged lockfile and no local dependency overrides.
pnpm run code:fix Passed with warnings and informational diagnostics; no fixes applied.
pnpm --filter @cipherstash/eql build and pnpm --filter @cipherstash/eql test Build passed; all 5 package tests passed.
pnpm exec vitest run --config scripts/vitest.config.mjs scripts/__tests__/workflow-run-fail-fast.test.mjs scripts/__tests__/eql-suite-ci.test.mjs scripts/__tests__/eql-workflow-filters.test.mjs All 65 guard tests passed after the workflow fix.

CI has not passed. The Rust workspace job failed because crates.io has no stack-encrypt package yet. The Node 22 job also rejected lockfile entries for local suite crates that are absent in a clean checkout, and found the workflow fail-fast issue fixed in this update. Publish the prerequisite crates and regenerate packages/eql/Cargo.lock without local overrides before merging. The local checks above do not replace that registry-based CI validation.

Related

Refs cipherstash/cipherstash-suite#2215 — the architecture decision and Stack Encrypt API this companion PR consumes.

Review notes

  • Start with packages/eql/crates/eql-bindings/src/encryption.rs, then the generated derives in src/v3/text.rs and tests/encryption/tests/text_eq.rs.
  • This is a new encryption profile. Its ciphertext and search terms are independent of existing cipherstash-client output. Equality is exact, with no added case folding or Unicode normalization.
  • Keep this PR in draft until the publication and lockfile prerequisites above are met. Other scalar domains and JSON document encryption are follow-up work.
  • Checked the shipped PostgreSQL and supply-chain skills; this change leaves their JavaScript query APIs and dependency-policy guidance intact. Coordinate a docs-site update when the Rust API is released; the in-repo README already includes examples.

Use Stack Encrypt declarations and native readers for TextEq and
TextEqQuery. The stored identifier supplies context; Vitamin C owns
plaintext encoding and equality input. Encode only encrypted output.

Exercise canonical cross-opening, validation before KMS, batching,
query-only operation and PostgreSQL equality/index use. Registry CI
requires the unpublished suite crates and a refreshed registry lock.

Refs cipherstash/cipherstash-suite#2215
@changeset-bot

changeset-bot Bot commented Sep 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 545c700

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@cipherstash/eql Minor
stash Patch
@cipherstash/stack-prisma Patch
@cipherstash/basic-example Patch
@cipherstash/e2e Patch
@cipherstash/prisma-example Patch
@cipherstash/stack-drizzle Patch
@cipherstash/stack-supabase Patch
@cipherstash/stack Patch
@cipherstash/wizard Patch
@cipherstash/bench Patch
@cipherstash/test-kit Patch

Not sure what this means? Click here to learn what changesets are.

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

The workflow's custom bash shell omits errexit, so a successful final
command could hide an earlier failure. Enable fail-fast behavior in both
new checks and document the internal test crate in the repository map
and security scope.

Refs #971
@coderdan coderdan changed the title feat(eql): derive TextEq encryption targets feat(eql): add Rust text encryption and queries Sep 13, 2026
Make successful encryption discoverable alongside the type contracts.
Execute the displayed example in the encryption test crate so its
runtime and fake key source stay out of the bindings' default tests.
Generate plaintext/context descriptions and EQL reference links with
the Rust types so regeneration preserves them.

Refs #971
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