DynamoDB: support EQL v3 schemas in encryptedDynamoDB #657
Description
Problem
encryptedDynamoDB (@cipherstash/stack/dynamodb) is built on the EQL v2 schema surface: columns are declared with encryptedColumn("email").equality() / encryptedField, and searchable lookups use the v2 envelope's HMAC term (email__hmac attributes for key-condition queries via encryptQuery).
It has no support for the EQL v3 typed schema (types.* concrete-domain columns from @cipherstash/stack/eql/v3, EncryptionV3): a v3 table can't be passed to encryptedDynamoDB, and none of the v3 term shapes are consumed.
Why now
The docs are moving to EQL v3 as the sole documented approach for 1.0 (Drizzle, Supabase, and the core client are already there; prisma-next is in review at #655). DynamoDB is the deliberate exception — its skill keeps teaching the v2 schema until this lands, which leaves it the only integration on the old authoring surface.
Shape of the work (sketch)
- Accept v3 tables (
encryptedTable+types.TextEq/types.Textetc. fromeql/v3); equality-searchable attributes come from the v3 equality (hm) term — same HMAC concept, new envelope shape (v: 3). - Map the v3 storage envelope into the item-attribute layout (
<attr>ciphertext +<attr>__hmacterm), mirroring what the v2 path does withc/hm. encryptQuery(value, { table, column })on a v3 equality column already mints the bare HMAC term — the DynamoDB key-condition path should consume that directly.- Skill (
skills/stash-dynamodb) moves to the v3 authoring surface once shipped; note that only equality (*Eqdomains) is meaningful on DynamoDB (no range/free-text index engagement outside Postgres).
References
- v3 typed schema:
packages/stack/src/eql/v3/columns.ts(typescatalog, capabilities) - Current adapter:
packages/protect-dynamodb/ - Docs decision (v3-sole, DynamoDB exempted until this issue): docs(skills): stop teaching deprecated LockContext.identify() (#591) #642 follow-up work
Activity
tobyhede commentedon Jul 20, 2026
Scoping correction — this issue points at the wrong package.
The References section cites
packages/protect-dynamodb/as "Current adapter", but the API described throughout (encryptedDynamoDB,@cipherstash/stack/dynamodb) lives elsewhere. There are two forked DynamoDB implementations in this repo:packages/protect-dynamodbpackages/stack/src/dynamodbprotectDynamoDBencryptedDynamoDB← this issue@cipherstash/protect-dynamodb@cipherstash/stack/dynamodb@cipherstash/protect@/schema,@cipherstash/protect-ffiThey are the same algorithm with a renamed error type and a different import root — a fork, not a re-export.
AGENTS.md:89carried the same error (it attributedencryptedDynamoDBtopackages/protect-dynamodb), which is most likely where it came from; that line has since been corrected.The bigger finding: the shipping adapter has no test coverage. All 861 lines of DynamoDB tests exercise
protectDynamoDBin the other package. Nothing underpackages/stackreferencesencryptedDynamoDBexcept its own source and build output. So this is not a port — it is a port plus writing the test suite that was never written, and that is the larger half of the work.Where the v2 coupling actually is
Four structural couplings, all in
packages/stack/src/dynamodb/helpers.ts(mirrored in the fork)::183—const v = 2, synthesizing v2 envelopes on the read path:196k: 'sv'/:206k: 'ct', plus:111,:120— the v2 tagged-union envelope shape, hardcoded on both read and write:189-190—cast_asandindexes.ste_vec, which are the v2EncryptedTable.build()output shape; v3 columns are typed domains with fixed capabilities and noindexesbagEncryptedTable<EncryptedTableColumn>from@/schemain all four public methods (dynamodb/index.ts:47,59,71,83) and all four operation classesThe
__hmacsuffix (helpers.ts:9, consumed at:114-115,:168) is not a v2 coupling — as this issue already notes, v3 equality mints the same HMAC concept underhm. That part ports cheaply.Worth deciding as part of this: whether
packages/protect-dynamodbis ported alongside, or retired. It also declares@cipherstash/protectasworkspace:*in peerDependencies, which does not resolve for external consumers — likely already broken on npm, independent of anything here.Relationship to the v2 removal
Tracked under #707. Note this is not a hard blocker for removing v2: DynamoDB shares none of the v2 Postgres machinery, so a private, un-exported v2 path for this one adapter is technically clean. It does block deleting the v2 schema builders, which is a later milestone. Conflating the two inflates the critical path.
Full context:
docs/plans/2026-07-20-eql-v2-removal-scope.md§2 and §3.3.🤖 Generated with Claude Code
test(stack): characterise encryptedDynamoDB v2 behaviour
feat(stack): accept EQL v3 tables in encryptedDynamoDB
Merge pull request #725 from cipherstash/feat/dynamodb-eql-v3
docs(skills,meta): correct the naming the v2 removal inverted
docs(skills,meta): correct the naming the v2 removal inverted
docs(skills,meta): correct the naming the v2 removal inverted