project: scaffold cipherstash-extension + middleware-param-transform shaping artifacts#411
Merged
Conversation
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (12)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including ⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
@prisma-next/mongo-runtime
@prisma-next/family-mongo
@prisma-next/sql-runtime
@prisma-next/family-sql
@prisma-next/extension-arktype-json
@prisma-next/middleware-telemetry
@prisma-next/mongo
@prisma-next/extension-paradedb
@prisma-next/extension-pgvector
@prisma-next/postgres
@prisma-next/sql-orm-client
@prisma-next/sqlite
@prisma-next/target-mongo
@prisma-next/adapter-mongo
@prisma-next/driver-mongo
@prisma-next/contract
@prisma-next/utils
@prisma-next/config
@prisma-next/errors
@prisma-next/framework-components
@prisma-next/operations
@prisma-next/ts-render
@prisma-next/contract-authoring
@prisma-next/ids
@prisma-next/psl-parser
@prisma-next/psl-printer
@prisma-next/cli
@prisma-next/emitter
@prisma-next/migration-tools
prisma-next
@prisma-next/vite-plugin-contract-emit
@prisma-next/mongo-codec
@prisma-next/mongo-contract
@prisma-next/mongo-value
@prisma-next/mongo-contract-psl
@prisma-next/mongo-contract-ts
@prisma-next/mongo-emitter
@prisma-next/mongo-schema-ir
@prisma-next/mongo-query-ast
@prisma-next/mongo-orm
@prisma-next/mongo-query-builder
@prisma-next/mongo-lowering
@prisma-next/mongo-wire
@prisma-next/sql-contract
@prisma-next/sql-errors
@prisma-next/sql-operations
@prisma-next/sql-schema-ir
@prisma-next/sql-contract-psl
@prisma-next/sql-contract-ts
@prisma-next/sql-contract-emitter
@prisma-next/sql-lane-query-builder
@prisma-next/sql-relational-core
@prisma-next/sql-builder
@prisma-next/target-postgres
@prisma-next/target-sqlite
@prisma-next/adapter-postgres
@prisma-next/adapter-sqlite
@prisma-next/driver-postgres
@prisma-next/driver-sqlite
commit: |
10c95d3 to
01b944b
Compare
…Execute seam Records the project shape for promoting beforeExecute middleware to a mutable param-transformation seam (TML-2359). The middleware contract grows a ParamRefMutator API (replace ParamRef.value only; cannot rewrite SQL or projection), and MiddlewareContext carries the same per-query AbortSignal as TML-2330's CodecCallContext. The seam is general-purpose infrastructure — any extension whose codec is network-backed and bulk-friendly (KMS encryption, signing, audit-stamping, schema-bound JSON validation) can write a plan-walking beforeExecute middleware that batches the work and mutates outbound parameters with the result. CipherStash's bulk-encrypt is the first concrete consumer, not the owner. Spec captures functional + non-functional requirements, acceptance criteria across mutation surface / cancellation / backwards compatibility / family parity / type safety, and a worked-example AC demonstrating the bulk-encrypt middleware shape. Open questions flagged around Promise-accepting mutator, Mongo's tree-shaped ParamRef, mutator reuse across chained middleware, and family extension of MiddlewareContext.
…nsion
Records the project shape for @prisma-next/extension-cipherstash, the
first real consumer of TML-2330 (codec call context) + TML-2359
(middleware param transform). Uses the envelope-codec pattern:
- Write side: users construct envelopes from plaintext
(EncryptedString.from('me@example.com')); the extension's
middleware walks ParamRefs in beforeExecute, batches all envelope
plaintexts into one bulkEncrypt({ signal }) call, replaces values
with ciphertexts. Codec encode is identity by the time it runs.
- Read side: codec.decode constructs an envelope wrapping the
ciphertext + the column handle (table, column, KMS metadata)
supplied by SqlCodecCallContext.column. Users call
await row.email.decrypt() per cell, or use bulk-decrypt
utilities (decryptAll, post-buffering) to amortize across many
envelopes.
Handle is internal to the extension package — no public TypeScript
surface. Stored on the envelope via private field / closure
(implementation detail).
Spec covers package layout, envelope classes, codec, bulk-encrypt
middleware, bulk-decrypt utility, end-to-end ACs, and documentation
expectations. Project's close-out includes migrating the
envelope-codec pattern doc into a durable docs/ location so future
network-backed bulk-amortizable extensions (Vault, AWS KMS, etc.)
have a canonical reference.
Direct dependencies: TML-2330 / PR #400 (must merge first), TML-2359
/ projects/middleware-param-transform/spec.md (must merge before
this project's middleware lands). The codec / envelope / bulk-decrypt
parts can land independently of TML-2359.
Reorganize both shaping specs around the structure a teammate without project context would actually want to read: - TL;DR at top with the decision + what it unblocks (1 paragraph). - Grounding example showing the new code surface end-to-end before any architecture detail. - Decision as 3 load-bearing declarative bullets; mechanical fallout consequences described in 'How it works'. - Why as narrative motivation in one place (no longer dispersed across the FR list). - How it works section with concrete TypeScript shapes and threading, detailed enough that an implementer can start coding. - What this enables / Non-goals split — load-bearing capabilities separated from explicit out-of-scope. - Acceptance criteria slimmed to focused validation hooks (no longer redundant: 'middleware can mutate' + 'codec.encode receives mutated value' merged into one observable). - Alternatives considered at the end, collected from real alternatives weighed (microtask-coalescing batcher, bulk-codec trait, AST-rewrite middleware, async mutator, lazy field-access decryption, KMS provider abstraction, public handle type, etc.) instead of dispersed as parenthetical justifications. - Removed all framework-gaps.md references; framework-gaps is a transient audit document, the specs describe their problem statements inline and link only to ADRs / TML tickets / external standards. - Removed the three-Description-section sprawl from cipherstash-extension (collapsed into a single 'How it works' with subsections). - Moved the cipherstash-extension 'Close-out' section out of the spec (project-execution process belongs in the plan, not the spec). No semantic changes — same decisions, same scope, same dependencies. The prose is rebuilt to read end-to-end without prior context, with the design stated explicitly and requirements named as the validation surface for that design rather than the source the reader has to reverse-engineer.
…lock Project 1 scope
Promotes cipherstash-extension and middleware-param-transform from standalone
projects into the cipherstash-integration umbrella, and adds two new task
specs covering the PSL constructor surface and migration factories.
Project 1 scope is locked end-to-end-tested: EncryptedString only (3 argument
shapes, nullable + non-nullable), eq + ilike operators, EQL bundle install via
databaseDependencies.init, hand-authored migration.ts using rawSql({...}) for
EQL search-config installation. Other column types (Number/Date/Boolean/Json),
other operator families (orderAndRange, searchableJson), and planner-driven
per-column DDL are deferred to Project 2.
Key design corrections vs the prior drafts:
- Codec descriptor uses post-#402 RuntimeParameterizedCodecDescriptor<P> with
arktype-validated params (mirrors pgvector's length plumbing).
- PSL surface is namespaced constructors (cipherstash.EncryptedString(...)),
not attribute form (@cipherstash.encrypted(...)) — attributes can't couple
legal arguments to the field's scalar type at the grammar level.
- Migration factories ride on the pre-existing Postgres rawSql({...}) escape
hatch with operationClass: 'additive', not DataTransformOperation. PR #404
is therefore an indirect (Project-2-only) dependency, not Project 1.
…h middleware-param-transform cross-refs Rewrites envelope-codec-extension.spec.md against the post-#402 codec API and the locked Project 1 scope: - Codec uses RuntimeParameterizedCodecDescriptor<P> with arktype paramsSchema for { equality, freeTextSearch }, registered separately from the codec body itself (mirrors pgvector's plumbing for length). - Constrains the column-type surface to EncryptedString only (3 argument shapes, nullable + non-nullable). Removes EncryptedJson, EncryptedNumber, and other types from scope — those land in Project 2 alongside their codec round-trip / search-operator / migration tests. - Adds the EQL bundle install via databaseDependencies.init, sourced from the first-attempt repo's eql-bundle.ts. Same shape pgvector uses for CREATE EXTENSION vector. - Adds operator lowering for eq and ilike, deferring the canonical SQL function names to the first-attempt's operation-templates.ts. - Native type is eql_v2_encrypted (not text), reflecting EQL's actual storage type. - Acceptance criteria expanded to cover end-to-end live-Postgres+EQL tests for round-trip, bulk amortization, nullable handling, and cancellation. Refreshes middleware-param-transform.spec.md cross-references: - Path depth bumped (../../ → ../../../) for the new specs/ subdirectory location. - Adds back-reference to the umbrella spec. - Updates the cipherstash-extension cross-reference to point at the new envelope-codec-extension.spec.md path. - Annotates ADR 207 reference as forthcoming with PR #400.
…herstash, narrow sql-raw-factory to public surface Add raw-sql-ast-node task spec under cipherstash-integration: defines RawSqlExpr as a new AnyQueryAst arm, the Postgres lowerer arm, and a planFromAst envelope helper. Cipherstash owns the AST node so the migration-factories spec can build DataTransformOperations without depending on the parallel sql-raw-factory project's user-facing template-literal factory. Rewrite migration-factories spec accordingly: factories now produce DataTransformOperation entries carrying invariantIds (per PR #404) rather than rawSql({...}) additive ops. Each entry's run() builds a SqlQueryPlan with a RawSqlExpr ast via the package-internal API, parameterizing all four arguments to eql_v2.add_search_config so user table/column inputs flow through ParamRefs rather than text-inlined. Narrow sql-raw-factory spec to its actual scope: the public raw\`...\` template-literal factory layered on top of the AST node (RawArg type union, identifier(...) escape hatch, type-level rejection of bare values, param() ergonomic re-export). Hard upstream dependency on the AST node existing — which it will, because cipherstash-integration ships it. sql-raw-factory now consumes that work rather than owning it. Update umbrella status table and References to reflect: raw-sql-ast-node joins as a Project 1 task; sql-raw-factory moves from "Direct, upstream sequencing" dependency to "Downstream consumer (not a dependency)".
… with high-level plan Restructure the cipherstash-integration project from a single-spec layout into a three-component umbrella: - project-1/: searchable-encryption MVP (the previous umbrella spec rescoped as Project 1, plus its 5 task specs moved under specs/) - project-2/: planner-driven DDL + expanded surface (new stub) - sql-raw-factory/: public raw\`...\` template-literal factory (moved from projects/sql-raw-factory/) New artifacts at the umbrella level: - spec.md: scope of the umbrella, why three components, cross-component design decisions (RawSqlExpr ownership, DataTransformOperation choice, end-to-end-tested-only scope), in-flight framework dependency status (PRs #400/#402 merged 2026-05-01; #404/#409 still open). - plan.md: component-level sequencing. Phase A is Project 1 (critical path, gated externally on #404 + #409); phase B is sql-raw-factory and Project 2 in parallel afterwards, each with its own gating story (sql-raw-factory blocks on Project 1's RawSqlExpr AST node landing; Project 2 blocks on Project 1 + TML-2338 + TML-2339). Project 1's spec is reframed: drop "this is the umbrella" language, add a header pointing to the new umbrella, repath all relative references for the new directory depth (3-up from project-1/, 4-up from project-1/specs/, 3-up from sql-raw-factory/). Resolve the previously- open question about Project 2's on-disk slug. No content changes to the per-task specs beyond reference repaths.
…e-slice milestones project-1/plan.md sequences Project 1 as five end-to-end-demoable milestones rather than one-milestone-per-task-spec: - M1: framework SPI (raw-sql-ast-node + middleware-param-transform; no user-facing surface yet, but the seams unblock other extensions) - M2: store-only round-trip (psl + envelope-codec storage path; encrypted column type works for storage; no operators yet) - M3: eq operator + manual addSearchConfig migration (first searchable round-trip end-to-end against live EQL) - M4: ilike + activatePendingSearches + decryptAll (completes Project 1's user-facing surface; all UMB ACs green) - M5: close-out per projects/README.md lifecycle Records that Project 1 is independent of both open framework PRs: - #404 (invariant-aware ref routing): migration factories carry invariantId fields regardless; the routing benefit is retroactive when #404 lands. - #409 (middleware intercept + contentHash): edits the same RuntimeMiddleware types but adds non-overlapping fields; whichever lands first, the other rebases mechanically. Updates the umbrella plan and spec to reflect the new posture: PRs #404 and #409 demoted from "hard gating" to "coordinate-only" / "not a dependency"; status table marks Project 1's plan as drafted.
…ee milestones sql-raw-factory/plan.md sequences this component as three small milestones matching the spec's deliverables: - M1: raw\`...\` template factory + param() ergonomic re-export, with the RawArg type union narrowed to ParamRef + Expression (no identifier() yet). Type-level rejection of bare values is the entire SQL-injection defense; negative type tests pin AC-TYPE2 / AC-TYPE3. - M2: identifier(...) escape hatch + Postgres lowerer arm with a curated adversarial fuzz fixture (quote / null byte / newline) verifying the escape function doesn't break out of the surrounding quotes. - M3: integration + close-out. End-to-end Postgres execution, middleware composition, cipherstash bulk-encrypt composition, then the lifecycle close-out per projects/README.md. Critical-path note: M1 hard-merge-blocks on Project 1's M1 landing (needs the RawSqlExpr AST node + lowerer arm + planFromAst envelope helper); M3's AC-COMP3 additionally needs Project 1's M2 (cipherstash codec + bulk-encrypt middleware). M2 is in-component sequencing. Resolves three of the spec's six open questions at the plan level (public entry point: dual-export; drop second call signature: confirmed; contract acquisition: createRaw factory-of-factories) and tags the other three to specific milestones. Updates the umbrella plan's status table and Phase B description to reflect sql-raw-factory's plan as drafted.
Replace stale references to cancelled per-task tickets (TML-2338, TML-2339, TML-2359, TML-2360) with the three new component-level umbrella tickets (TML-2373 = Project 1, TML-2374 = sql-raw-factory, TML-2375 = Project 2). Where the cancelled tickets described real, still-pending work (the per-column planTypeOperations input shape and prior-state contract that Project 2 needs), the work is now described in prose with a parenthetical "was TML-XXXX, cancelled in Linear redesign" so the historical pointer survives. References to TML-2330, TML-2229, and TML-2292 are kept — those tickets remain alive in Linear. Resolves the umbrella's open question on Linear ticket redesign timing: component-level tracking only, no per-task or per-milestone sub-issues.
01b944b to
cba5dfd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two new project shaping documents recording our discussion about the envelope-codec pattern for CipherStash and the middleware seam it depends on.
Projects
projects/middleware-param-transform/spec.md(TML-2359)Promotes
beforeExecutemiddleware to a mutable param-transformation seam: middleware can rewrite outboundParamRef.valueslots before encode runs, with access to the per-queryAbortSignalfrom TML-2330 / ADR 207'sCodecCallContext. Scoped mutator (replace values only — cannot rewrite SQL or projection). General-purpose infrastructure: any extension whose codec is network-backed and bulk-friendly (KMS encryption, signing, audit-stamping, schema-bound JSON validation) can write a plan-walking middleware that batches the work and mutates outbound parameters with the result.projects/cipherstash-extension/spec.md(TML-2360)@prisma-next/extension-cipherstash, the first real consumer of TML-2330 + TML-2359. Uses the envelope-codec pattern:EncryptedString.from('me@example.com')); the extension's middleware walksParamRefs inbeforeExecute, batches all envelope plaintexts into onebulkEncrypt({ signal })call, replaces values with ciphertexts. Codecencodeis identity by the time it runs.decodeconstructs an envelope wrapping the ciphertext + the column handle ({ table, column }+ KMS metadata) supplied bySqlCodecCallContext.column. Users callawait row.email.decrypt()per cell, or use bulk-decrypt utilities (decryptAll, post-buffering) to amortize across many envelopes.Handle is internal to the extension package — no public TypeScript surface. Stored on the envelope via private field / closure (implementation detail).
Project relationships
SqlCodecCallContext.columnand per-queryAbortSignalplumbing the codec uses at decode time.ParamRefMutatorAPI. The codec / envelope / bulk-decrypt parts of the extension can land independently.Status
These are shaping artifacts —
projects/<project>/spec.mdfiles, no implementation. Per the project workflow, the next step on each is plan generation (drive-create-plan) once the spec is validated. This PR is for collaboration on the specs themselves.Both specs flag open questions that need stakeholder input before implementation begins. See the "Open Questions" section at the bottom of each.