# Ekklesia module template This is the smallest supported backend-only Ekklesia module. It proves the static manifest, compiled runtime and schema entrypoints, capability registration, a module-owned Drizzle table, a generated module-owned migration, logical storage-bucket declarations, and one permissioned HTTP route (`GET /records`, declared in `register`) that reads that table through `context.services.database` — the platform-provided `ModuleDatabase` facade, never a module-constructed Drizzle instance or a raw `pg` client. Migration execution and storage provisioning are intentionally not included. It also ships an **optional** React UI (`src/ui/`), compiled to the `entrypoints.ui` artifact its manifest declares by `pnpm build` ([ADR 0023](../../docs/architecture/decisions/0023-module-ui-build-pipeline.md)). UI is a choice, not a requirement: to keep a module backend-only, delete `src/ui/`, `tsconfig.ui.json`, `tsconfig.ui-test.json`, `tests/module-ui.test.ts`, the `ui` entrypoint from the manifest, and the React and module-UI dependencies. Everything else keeps working, and `ekklesia-build-module-ui` simply reports that there is nothing to build. No host mounts a module UI yet — the Dashboard shell does not exist. `tests/module-ui.test.ts` builds the artifact from source, validates it with `parseModuleUiExport`, mounts it into a real DOM, and unmounts it, which is what proves the pipeline today. See [Declaring a UI entrypoint](../../docs/module-development/README.md#declaring-a-ui-entrypoint). To create a module inside this repository: 1. Copy this directory to `modules/`. 2. Rename the package in `package.json` and remove `private` when it is ready for publication. 3. Update the ID, name, version, namespaces, compatibility ranges, and bucket requests in `ekklesia.module.json`. 4. Keep the manifest capability versions and dependency ranges aligned with the contracts the module actually implements and consumes. 5. Replace `src/server/index.ts` with backend behavior only. Its `defineModuleRuntime` ID and version must match the manifest; do not duplicate the rest of the manifest in TypeScript. Declare HTTP routes only in `register`, against a permission the manifest already declares; `start`, `stop`, and every route handler — never `register` — receive `context.services.database`, a typed `ModuleDatabase` view over the platform's one PostgreSQL pool. 6. Replace the pure Drizzle declarations in `src/schema/tables.ts` and keep its table prefix aligned with the module ID. Keep the `defineModuleSchema` ownership wrapper in `src/schema/index.ts` aligned too, and never import another module's schema. 7. Update `drizzle.config.ts`, generate forward migrations with `pnpm migration:generate --name=lowercase_name` from the module directory, and review the SQL. Keep each generated tag in `drizzle/meta/_ekklesia.json`, classify it as `none` or `approval-required`, and change that file's `moduleId` when renaming the template. 8. Run `pnpm install`, `pnpm build`, `pnpm modules:validate`, `pnpm migrations:validate`, and `pnpm verify` from the repository root. The declared production entrypoint is the compiled `dist/server/index.js`, not source TypeScript; the schema entrypoint is likewise compiled to `dist/schema/index.js`. Do not add UI components, styles, renderers, navigation, or layouts. Those belong to surfaces. Keep imports side-effect-free, register capabilities in `register`, acquire resources in `start`, and release them in `stop`. Never construct a Drizzle instance, read `DATABASE_URL`, or reach for a raw `pg` client — `context.services.database` is the only supported way to query this module's own tables, and it is never available during `register`. The migration directory uses the pinned stable Drizzle Kit `0.31.10` PostgreSQL journal and snapshot format. Migrations are forward-only and may modify only this module's objects. Offline validation treats SQL as opaque bytes, calculates SHA-256 from the exact contents, and never opens a database or executes SQL. Destructive declarations are review metadata, not automatic SQL classification or approval.