feat: transition module packages to database ABI v1
CI / verify (pull_request) Successful in 1m1s
CI / infrastructure-smoke (pull_request) Successful in 36s
CI / migration-smoke (pull_request) Successful in 1m54s
CI / image-build (pull_request) Successful in 31s
CI / deploy-smoke (same-origin) (pull_request) Successful in 52s
CI / deploy-smoke (split) (pull_request) Successful in 1m32s
CI / verify (pull_request) Successful in 1m1s
CI / infrastructure-smoke (pull_request) Successful in 36s
CI / migration-smoke (pull_request) Successful in 1m54s
CI / image-build (pull_request) Successful in 31s
CI / deploy-smoke (same-origin) (pull_request) Successful in 52s
CI / deploy-smoke (split) (pull_request) Successful in 1m32s
This commit is contained in:
@@ -682,13 +682,11 @@ Module Package v1, a deterministic `.tar.gz` with its SHA-256.
|
||||
and `THIRD_PARTY_NOTICES`, and writes the archive.
|
||||
- **The verifier.** Core's `verifyModulePackage` reads the archive in memory and refuses links,
|
||||
special files, and escaping paths. It checks every digest and identity, the target and runtime,
|
||||
and the transitional Drizzle agreement, runs the platform's own module and migration validation,
|
||||
and loads the backend once in a short-lived process with an empty environment. Only then does it
|
||||
extract anything.
|
||||
- **What exists today.** Only the `any` target. Drizzle agreement is recorded as transitional, not a
|
||||
database ABI. [ADR 0050](decisions/0050-module-database-abi-v1.md) defines the Module Database ABI
|
||||
that removes it for modules that declare that ABI. Manifest version 8 now lets a module declare
|
||||
`database: { abi: 1 }`; Core gives each such module its own attributed ABI executor and validates
|
||||
its plain schema metadata. A version 8 module that omits `database` remains on the temporary
|
||||
legacy facade. Packaging still uses ADR 0049's transitional Drizzle agreement in this slice; the
|
||||
packager transition and template ABI migration are deliberately next.
|
||||
runs the platform's own module, schema, and migration validation, and loads the backend once in a
|
||||
short-lived process with an empty environment. Only then does it extract anything.
|
||||
- **What exists today.** Only the `any` target. A manifest-v8 ABI module carries no
|
||||
`transitional.databaseRuntime`; Drizzle may be bundled as its private implementation detail, and
|
||||
Core validates plain schema metadata without comparing ORM versions. A version 8 module that
|
||||
omits `database` remains on the temporary legacy facade and keeps ADR 0049's exact transitional
|
||||
Drizzle agreement. The canonical template now demonstrates ABI v1. The legacy facade still
|
||||
exists.
|
||||
|
||||
@@ -603,19 +603,21 @@ Ekklesia Module Package v1 (ADR 0049) is proven at three levels, all in `pnpm ve
|
||||
- `core/tests/module-package-verification.test.ts`: the verifier against hand-built archives,
|
||||
including what the packager never produces. A valid package verifies and extracts with normalized
|
||||
modes. A wrong digest, a corrupted or missing or unlisted file, disagreeing identities, an unknown
|
||||
format, unknown transitional metadata, an incompatible target, Node.js range, SDK, or database
|
||||
runtime, a missing entrypoint, a backend that imports something the package lacks, absolute and
|
||||
format, unknown transitional metadata, an incompatible target, Node.js range, SDK, or legacy
|
||||
database runtime, ABI metadata carrying a forbidden transitional runtime, malformed ABI schema
|
||||
metadata, a missing entrypoint, a backend that imports something the package lacks, absolute and
|
||||
`..` paths, symbolic and hard links, device nodes and FIFOs, setuid and setgid entries, a second
|
||||
top-level directory, and a duplicate entry each fail with their own sanitized code. The backend
|
||||
load check runs in a real child process.
|
||||
load check runs in a real child process and selects the schema contract from the manifest.
|
||||
- `packages/module-pack/tests/`: the packager against generated fixture modules (payload contents,
|
||||
generated `package.json`, metadata, bundled backend with only built-in imports, refusal of native
|
||||
and platform-specific dependencies and of unknown or textless licences, notices, byte-identical
|
||||
archives, normalized tar and gzip headers), and end to end against the built module template:
|
||||
packed twice to identical bytes without changing the workspace's install state, verified by Core's
|
||||
verifier, extracted elsewhere, and loaded with Node's own resolution hooks proving nothing outside
|
||||
the package is resolved. Turbo builds the template and Core first, and runs the template's own
|
||||
tests (which rebuild its UI in place) before these.
|
||||
verifier with a deliberately different Core Drizzle version, extracted elsewhere, and loaded with
|
||||
Node's own resolution hooks proving nothing outside the package is resolved. The package contains
|
||||
module-side Drizzle but no `transitional.databaseRuntime`. Turbo builds the template and Core
|
||||
first, and runs the template's own tests (which rebuild its UI in place) before these.
|
||||
|
||||
## The generic platform image and the reference deployment
|
||||
|
||||
@@ -635,7 +637,7 @@ These need Docker and build images locally; nothing is pushed or published.
|
||||
generic image with empty module storage. With `EKKLESIA_SMOKE_TOPOLOGY=split`, the same image also
|
||||
runs the installed-module lifecycle:
|
||||
- the module template is packaged by the canonical packager (Module Package v1, ADR 0049),
|
||||
verified by the image's own verifier, and installed into module storage;
|
||||
as ABI v1, verified by the image's own verifier, and installed into module storage;
|
||||
- its migration is planned and explicitly applied, and the API restarted;
|
||||
- a real browser mounts it;
|
||||
- it is removed and the API restarted, and Core still runs, migrations still plan and apply, and
|
||||
|
||||
@@ -138,18 +138,15 @@ The archive holds one directory named with your module id:
|
||||
- **Deterministic.** The same packager version and the same built inputs produce the same archive,
|
||||
byte for byte, wherever the checkout is.
|
||||
|
||||
**Drizzle, for now.** You define tables with your own copy of Drizzle, and Core runs your queries
|
||||
through the platform's Drizzle instance (see [Database access](#database-access)). Objects from two
|
||||
copies interoperate only when the copies are interchangeable, so the current implementation requires
|
||||
**exactly the Drizzle version the platform uses**: the one `@ekklesia/module-sdk` depends on. The
|
||||
package records it under `transitional`, and an installation refuses a package whose Drizzle differs.
|
||||
This is a constraint of today's implementation, not a permanent contract.
|
||||
**Database ABI v1 is the current path.** A module that declares `"database": { "abi": 1 }` keeps
|
||||
Drizzle and its exact version inside its own bundle as an implementation detail. Its package omits
|
||||
`transitional.databaseRuntime`; Core validates the module's plain schema metadata and never compares
|
||||
the bundled Drizzle version with its own. The canonical template demonstrates this path with
|
||||
`@ekklesia/module-database/drizzle`.
|
||||
|
||||
`@ekklesia/module-database` now contains the module-side ABI v1 adapters that remove that coupling,
|
||||
but deployed modules cannot receive an ABI v1 executor yet. Manifest v8 and per-module runtime
|
||||
service delivery are separate later work. Until those land, the current transitional package rule
|
||||
and database service above remain authoritative; neither Module Package v1 nor the template changed
|
||||
with the adapter package.
|
||||
A manifest-v8 module that omits `database` remains on the temporary legacy facade. Only that legacy
|
||||
package records bundled Drizzle under `transitional.databaseRuntime`, and the verifier continues to
|
||||
require exact agreement with Core. The legacy facade has not been removed.
|
||||
|
||||
#### Verifying a package
|
||||
|
||||
@@ -1468,9 +1465,10 @@ export default defineConfig({
|
||||
```
|
||||
|
||||
Keep `tables.ts` limited to Drizzle declarations so the stable generator can load it without
|
||||
executing the ESM Module SDK wrapper. `src/schema/index.ts` remains the production compiled schema
|
||||
entrypoint and wraps those same exported table objects with `defineModuleSchema`; tables are not
|
||||
declared twice.
|
||||
executing the ESM module wrapper. For an ABI-v1 module, `src/schema/index.ts` remains the production
|
||||
compiled schema entrypoint and converts those same table objects to plain metadata with
|
||||
`moduleDrizzleSchemaMetadata`; tables are not declared twice. A legacy-v8 module continues to wrap
|
||||
them with `defineModuleSchema` while that facade exists.
|
||||
|
||||
Run the module's `migration:generate` script with a lowercase snake-case name, review the generated
|
||||
SQL and snapshot, and add the matching entry to `drizzle/meta/_ekklesia.json`. Every entry must
|
||||
@@ -1587,10 +1585,10 @@ The result is `{ moduleId, tables: [{ name, foreignKeys: [{ table }] }] }`. Core
|
||||
prefixes, duplicates, and same-module foreign-key targets from those strings and never inspects an
|
||||
ABI module's ORM objects.
|
||||
|
||||
The canonical module template has moved to manifest version 8 but intentionally omits `database`,
|
||||
so it remains on the legacy facade for one more slice. The packager still emits ADR 0049's
|
||||
`transitional.databaseRuntime`; the next package-transition change will remove that fact for ABI
|
||||
packages and migrate the template at the same time.
|
||||
The canonical module template declares ABI v1, uses the module-side Drizzle adapter, and exports
|
||||
plain schema metadata derived with `moduleDrizzleSchemaMetadata`. Its Module Package v1 still
|
||||
bundles Drizzle, but carries no `transitional.databaseRuntime`: that version belongs to the module,
|
||||
not Core.
|
||||
|
||||
## Legacy database access
|
||||
|
||||
|
||||
Reference in New Issue
Block a user