# Module development An Ekklesia module is a backend capability package. It owns business behavior and data declarations; it never owns components, styles, layouts, renderers, navigation, or other UI. ## Required package shape Each immediate child of `modules/` must contain: ```text modules/example/ ├── ekklesia.module.json ├── package.json └── src/server/index.ts ``` `package.json` must use ESM, a strict semantic version, a unique package name, and: ```json { "ekklesia": { "layer": "module" } } ``` Its version must match the module manifest version. Internal package dependencies use `workspace:` ranges and supported package entrypoints. ## Manifest The canonical manifest is inert JSON so discovery can validate a module without executing it: ```json { "manifestVersion": 1, "id": "calendar", "name": "Calendar", "version": "0.1.0", "compatibility": { "platform": "^0.1.0", "moduleSdk": "^0.1.0" }, "dependencies": { "modules": { "required": { "people": "^0.1.0" } }, "capabilities": { "optional": ["media.items"] } }, "capabilities": ["calendar.events"], "permissions": ["calendar.events.read"], "storage": { "buckets": [ { "id": "assets", "description": "Private event assets owned by Calendar." } ] }, "entrypoints": { "server": "./src/server/index.ts" } } ``` Module, capability, and permission identifiers are lowercase and namespaced. Entrypoints must begin with `./`, stay inside the package, and exist. Unknown manifest properties are rejected, including presentation fields such as components or renderers. Storage bucket IDs are logical names scoped to the module. Modules may request multiple buckets. Core will later map them to deployment-specific physical S3 buckets and provide scoped access; module code must not configure SeaweedFS or other S3 clients directly. ## Discovery behavior Run: ```bash pnpm modules:validate ``` Discovery checks every installed module atomically. It rejects malformed manifests, duplicate IDs or package names, version mismatches, incompatible platform or SDK ranges, missing required modules or capabilities, incompatible installed dependencies, cycles, and missing or escaping entrypoints. Missing optional dependencies are allowed. Valid modules are ordered dependency-first with stable identifier sorting. Discovery does not load server code. Lifecycle execution, route/job registration, Drizzle schema aggregation and migrations, and physical bucket provisioning are deliberately deferred. ## Start from the template Copy `templates/module` to `modules/` and follow its README. The template is part of the workspace and is validated by the same contracts and discovery implementation as installed modules.