Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/architecture docs/perf/contract-spaces-overhead.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ The per-extension-space overhead is dominated by the extra `readFile` of `refs/h

## Reading the percentages honestly

The benchmark reports a relative delta of "≈ +200 %" between scenarios. **Do not interpret that as a budget violation.** The denominator is sub-millisecond synthetic work (one `readdir`); doubling something tiny is still tiny. The "< 5 %" budget was written against the total wall-clock of `emit + dbInit` end-to-end, which in practice runs in hundreds of milliseconds to seconds (PGlite startup + DDL roundtrips for the cipherstash / pgvector extensions involve `CREATE EXTENSION`, schema creation, index creation, and optional EQL bundle install).
The benchmark reports a relative delta of "≈ +200 %" between scenarios. **Do not interpret that as a budget violation.** The denominator is sub-millisecond synthetic work (one `readdir`); doubling something tiny is still tiny. The "< 5 %" budget was written against the total wall-clock of `emit + dbInit` end-to-end, which in practice runs in hundreds of milliseconds to seconds (PGlite startup + DDL roundtrips for extensions like pgvector involve `CREATE EXTENSION`, schema creation, and index creation).

A single extra `readFile` adding ~60 µs is well inside any reasonable interpretation of the 5 % budget for a multi-hundred-millisecond `dbInit`.

**Conclusion: the 5 % budget holds.**

## Limitations

- **Synthetic project layout.** The benchmark constructs a tiny pinned-space directory with a stub `refs/head.json`; real cipherstash / pgvector projects ship slightly larger pinned `contract.json` files. We don't expect this to change the picture — the IO path measured (`readFile` of a few-hundred-byte JSON file) is the same shape as production.
- **Synthetic project layout.** The benchmark constructs a tiny pinned-space directory with a stub `refs/head.json`; real extension projects (e.g. pgvector) ship slightly larger pinned `contract.json` files. We don't expect this to change the picture — the IO path measured (`readFile` of a few-hundred-byte JSON file) is the same shape as production.
- **Framework-only scope.** As described above, we deliberately do not run the full `emit + dbInit` end-to-end through PGlite. The framework is the layer that scales with extension count; the database operations are the same regardless of how the schema arrived (they would be identical whether authored as `databaseDependencies` or as a contract space).
- **One-shot capture.** The script lives under `wip/perf/` rather than a committed `bench/` directory because we have no convention for permanent perf benches in this repo and no CI gate consumes it. If we add such a convention in future, this bench is small enough to re-home easily — it imports framework helpers via the package's source path.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,31 +299,26 @@ The framework reads the descriptor only at **authoring time** (during `prisma-ne

### Authoring a contract space extension

Two worked examples ship in this repo:
In-repo worked examples:

- **`packages/3-extensions/cipherstash/`** — authored greenfield directly on the contract-space mechanism. Contributes the `eql_v2_configuration` table, the `eql_v2_configuration_state` enum, the `eql_v2_encrypted` composite, and the `eql_v2.{bloom_filter, hmac_256, blake3}` domains in its `contract.json`. The vendored ~5,750-line EQL bundle SQL is inlined into the body of one migration op (`installEqlBundle`) — functions / operators / casts / op classes are below the IR vocabulary boundary and live as opaque DDL inside migration ops, not in the contract IR. Demonstrates [ADR 213 — Codec lifecycle hooks](../adrs/ADR%20213%20-%20Codec%20lifecycle%20hooks.md) via the `cipherstash:string@1` codec emitting per-`(table, column)` `add_search_config` / `remove_search_config` ops.
- **`packages/3-extensions/pgvector/`** — ported from `databaseDependencies` to a `contractSpace`. Declares the parameterized `vector` type in its `contract.json`; ships one baseline migration whose body is `CREATE EXTENSION IF NOT EXISTS vector`, carrying a `pgvector:install-vector-v1` invariantId.

The extension descriptor never imports build-time-only material: everything it exposes is in-memory JSON values plus codec runtime functions. The build step (`tsdown`) produces self-contained descriptor values — for cipherstash, the EQL bundle is inlined into the `installEqlBundle` op as a TypeScript string literal so the published package's descriptor exposes self-contained migration JSON to the framework's emit pipeline.
An external reference implementation is the CipherStash extension (`@cipherstash/prisma-next`), which lives in CipherStash's own repository. It demonstrates the full contract-space mechanism for an encryption extension: contributing composite types, codec lifecycle hooks ([ADR 213](../adrs/ADR%20213%20-%20Codec%20lifecycle%20hooks.md)), and per-`(table, column)` migration ops.

See `packages/3-extensions/pgvector/`, `packages/3-extensions/paradedb/`, and `packages/3-extensions/cipherstash/` for the canonical on-disk layout (`migrations/refs/head.json`, `migrations/<dirName>/...`, `src/contract.{ts|prisma,json,d.ts}`, `prisma-next.config.ts` at the package root). See [`.cursor/rules/contract-space-package-layout.mdc`](../../../.cursor/rules/contract-space-package-layout.mdc) for the rule spelled out.
The extension descriptor never imports build-time-only material: everything it exposes is in-memory JSON values plus codec runtime functions. The build step (`tsdown`) produces self-contained descriptor values.

See `packages/3-extensions/pgvector/` and `packages/3-extensions/paradedb/` for the canonical on-disk layout (`migrations/refs/head.json`, `migrations/<dirName>/...`, `src/contract.{ts|prisma,json,d.ts}`, `prisma-next.config.ts` at the package root). See [`.cursor/rules/contract-space-package-layout.mdc`](../../../.cursor/rules/contract-space-package-layout.mdc) for the rule spelled out.

### Pinned per-space artefacts on disk

`prisma-next migrate` writes (or overwrites) one pinned-artefact subtree per loaded extension:

```text
migrations/
├── cipherstash/
│ ├── contract.json ← byte-for-byte == descriptor.contractSpace.contractJson
│ ├── contract.d.ts ← typed interface for the cipherstash schema
│ ├── refs/head.json ← byte-for-byte == descriptor.contractSpace.headRef
│ └── 20260601T0000_install_eql_bundle/
│ └── …
└── pgvector/
├── contract.json
├── contract.d.ts
├── refs/head.json
├── contract.json ← byte-for-byte == descriptor.contractSpace.contractJson
├── contract.d.ts ← typed interface for the pgvector schema
├── refs/head.json ← byte-for-byte == descriptor.contractSpace.headRef
└── 20240601T0000_install_vector/
└── …
```
Expand All @@ -332,7 +327,7 @@ Bumping an extension's package version produces a clear PR diff: updated pinned

### Codec lifecycle hooks (schema-driven companion)

Schema-driven per-column behaviour — e.g. cipherstash registering each searchable column with EQL — is *not* a function of the extension version but of the consuming application's schema. Codecs may declare a plan-time `onFieldEvent` hook (synchronous, app-space-bound) that returns migration ops the planner inlines into the app-space migration alongside the user's own structural ops. See [ADR 213](../adrs/ADR%20213%20-%20Codec%20lifecycle%20hooks.md).
Schema-driven per-column behaviour — e.g. an encryption extension registering each searchable column with its search configuration — is *not* a function of the extension version but of the consuming application's schema. Codecs may declare a plan-time `onFieldEvent` hook (synchronous, app-space-bound) that returns migration ops the planner inlines into the app-space migration alongside the user's own structural ops. See [ADR 213](../adrs/ADR%20213%20-%20Codec%20lifecycle%20hooks.md).

## Function and operator registry

Expand Down
14 changes: 4 additions & 10 deletions docs/architecture docs/subsystems/7. Migration System.md
Original file line number Diff line number Diff line change
Expand Up @@ -441,16 +441,10 @@ migrations/
│ ├── manifest.json
│ ├── ops.json
│ └── contract.json
├── cipherstash/ ← extension-space root
│ ├── contract.json ← pinned current contract
│ ├── contract.d.ts ← pinned current typings
│ ├── refs/head.json ← pinned head ref
│ └── 20260601T0000_install_eql_bundle/
│ └── …
└── pgvector/
├── contract.json
├── contract.d.ts
├── refs/head.json
└── pgvector/ ← extension-space root
├── contract.json ← pinned current contract
├── contract.d.ts ← pinned current typings
├── refs/head.json ← pinned head ref
└── 20240601T0000_install_vector/
└── …
```
Expand Down
2 changes: 1 addition & 1 deletion docs/design/10-domains/migration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ Grouped by sub-area so the relationships are visible. Some terms appear in more
- **Contract space** — a `(contract.json, migrations, headRef)` triple owned by exactly one contributor. The application owns one space (`'app'`); each schema-contributing extension owns one. Spaces are disjoint on disk; they integrate only via the live database.
- **Space-id** — identifier for a contract space. `[a-z][a-z0-9_-]{0,63}`. `'app'` is reserved for the application.
- **App-space** — the application's contract space.
- **Extension-space** — a contract space owned by an installed extension (e.g. `cipherstash`, `pgvector`).
- **Extension-space** — a contract space owned by an installed extension (e.g. `pgvector`, `paradedb`).
- **Pinned per-space artifacts** — the framework-owned on-disk mirror of each loaded extension's `contractSpace` (`migrations/<space-id>/{contract.json, contract.d.ts, refs/, <migration dirs>}`). Execution-time and verify-time read *only* the pinned files, never the extension's descriptor module.
- **Descriptor** — the runtime/control descriptor of an extension. Carries `contractSpace` when the extension contributes schema.

Expand Down
9 changes: 0 additions & 9 deletions examples/cipherstash-integration/.env.example

This file was deleted.

93 changes: 0 additions & 93 deletions examples/cipherstash-integration/README.md

This file was deleted.

Loading
Loading