Skip to main content

Module Manifest

The module.manifest.json is the single source of truth that describes everything the Platform-Kernel needs to know about your module. Without a valid manifest a module cannot be registered, loaded, or receive any SDK primitives.

important

Contract principle. The manifest is validated by OpenAPI schema on every POST /api/v1/modules call. A rejected manifest means the module never enters the REGISTER lifecycle stage.


Anatomy

{
"name": "@acme/crm",
"version": "1.2.0",
"description": "Customer Relationship Management for SeptemCore tenants",
"kernelSdkVersion": "^1.0.0",
"entry": "https://cdn.septemcore.com/modules/{tenantId}/acme-crm/1.2.0/remoteEntry.js",
"exposedComponent": "CrmModule",
"route": "/crm",
"icon": "users",
"healthCheck": "/api/v1/crm/health",
"category": "crm",
"author": "ACME Corp",
"permissions": [
"crm.contacts.read",
"crm.contacts.write",
"crm.deals.read",
"crm.deals.write"
],
"events": {
"publishes": ["crm.contact.created", "crm.deal.closed"],
"subscribes": ["auth.user.created", "billing.plan.changed"]
},
"dataApi": {
"models": ["contacts", "deals"],
"hooks": {
"contacts.create.before": "validateContact",
"contacts.create.after": "notifyTeam"
}
},
"notificationChannels": [],
"authProviders": [],
"embeds": [],
"dashboardWidget": {
"component": "CrmDashboardWidget",
"title": "CRM Overview",
"defaultSize": "medium"
},
"dependencies": {
"@platform/sdk-core": "^1.0.0",
"@platform/sdk-data": "^1.0.0",
"@platform/sdk-events": "^1.0.0"
}
}

Field Reference

Identity Fields

FieldTypeRequiredDescription
namestringUnique module identifier in npm-scope format: @scope/module-name. Regex-validated. 409 Conflict if already registered.
versionstringStrict semver X.Y.Z. No ranges, no pre-release suffixes in production.
descriptionstringShort human-readable description. Max 255 characters.
kernelSdkVersionstringSemver range the module is compatible with, e.g. ^1.0.0. If the running kernel SDK is outside this range, registration returns 409 Conflict.
authorstringPublisher display name. Shown in Admin → Modules catalog.
categorystringSidebar grouping hint: crm, analytics, finance, hr, marketing, custom.

UI Shell Integration

FieldTypeRequiredDescription
entrystringURL of the Module Federation remote entry. Do not set manually (see note below).
exposedComponentstringName of the exported React component that the Shell mounts as a page. Must match the key in exposes of federation.config.js.
routestringURL path in the admin sidebar, e.g. /crm. Must be unique across all active modules for a tenant.
iconstringIcon identifier from @platform/sdk-ui icon set, e.g. users, chart-bar, wallet.
healthCheckstringRelative path for health probing by Module Registry, e.g. /api/v1/crm/health. Auto-rollback fires if this path does not respond 200 within 60 s after deployment.
dashboardWidgetobjectOpt-in widget for the tenant dashboard. See Dashboard Widget below.
important

CDN Entry URL Resolution Do not set the entry field manually in your source manifest. The Module Registry automatically overwrites it with the production CDN URL after a successful upload: https://cdn.septemcore.com/modules/{tenantId}/{name}/{version}/remoteEntry.js

RBAC & Security

FieldTypeRequiredDescription
permissionsstring[]Permissions this module registers. Each must start with the module slug prefix: crm.contacts.read. At least 1 required. Auto-registered in IAM on install — appear immediately in the Role Builder.
note

Permission auto-registration. You do not call any API manually — Module Registry calls registerPermissions() internally after successful installation. Wild-card format {module}.* grants full access to all module permissions.

Permission Naming Convention

  • Format: {moduleSlug}.{resource}.{action} (e.g. crm.contacts.read)
  • All lowercase, dot-separated
  • Prefixes system.* and platform.* are reserved — using them returns 400 Bad Request

Event Bus Contract

"events": {
"publishes": ["crm.contact.created", "crm.deal.closed"],
"subscribes": ["auth.user.created", "billing.plan.changed"]
}
FieldTypeRequiredDescription
events.publishesstring[]Event types this module is allowed to publish. Any kernel.events().publish() call with a type not in this list is rejected at the SDK layer.
events.subscribesstring[]Event types this module may subscribe to. Tenant isolation is enforced: the SDK automatically appends tenantId from JWT — no cross-tenant leakage is possible.
danger

Kernel-owned events are write-protected. Events under auth.*, money.*, billing.*, audit.* can only be published by kernel services. A module attempting to publish them receives 403 Forbidden.

Data API (autoApi)

The dataApi field activates automatic CRUD endpoint generation for SQL-migrated models:

"dataApi": {
"models": ["contacts", "deals"],
"hooks": {
"contacts.create.before": "validateContact",
"contacts.create.after": "notifyTeam"
}
}
Sub-fieldDescription
modelsArray of table names (without schema prefix). Each listed model gets 5 REST endpoints automatically: POST, GET /:id, GET (list), PATCH /:id, DELETE /:id.
hooksLifecycle hooks keyed by {model}.{operation}.{timing}. Synchronous before hooks can reject the request; asynchronous after hooks emit events.

For example, model contacts in module crm generates the following endpoints:

POST /api/v1/data/crm/contacts
GET /api/v1/data/crm/contacts
GET /api/v1/data/crm/contacts/:id
PATCH /api/v1/data/crm/contacts/:id
DELETE /api/v1/data/crm/contacts/:id
note

Permission reconciliation. After each migration run, Data Layer scans information_schema and reconciles permissions: new tables → register {module}.{model}.read/write/delete; removed tables → archive those permissions. Controlled by env DATA_PERMISSION_RECONCILIATION_ON_MIGRATE=true.

Notification Channels

"notificationChannels": [
{
"identifier": "slack",
"name": "Slack",
"icon": "slack-logo",
"setupComponent": "SlackSetupForm"
}
]
Sub-fieldDescription
identifierUnique string for this channel adapter, e.g. slack, telegram.
nameDisplay name shown in Settings → Notifications.
iconIcon identifier from @platform/sdk-ui.
setupComponentName of the React component (from the module bundle) that renders the configuration form in Settings → Notifications.

When a module with notificationChannels is installed, the channel automatically appears in Settings → Notifications for the tenant without any kernel code change.

Auth Providers

"authProviders": [
{
"id": "metamask",
"type": "wallet",
"name": "MetaMask",
"icon": "metamask-logo",
"setupComponent": "MetaMaskSetupForm"
}
]
Sub-fieldDescription
idUnique provider identifier. Used in POST /api/v1/auth/login/{id} and callback routes.
typeProvider category: oauth, wallet, social, banking, enterprise, custom.
nameDisplay name on the login page.
setupComponentSettings UI component for provider configuration.

Embeds

Web Component widgets for embedding on external tenant websites:

"embeds": [
{
"name": "payment-button",
"tag": "crm-payment-button",
"script": "./dist/embed.js",
"params": ["amount", "currency", "order_id"]
}
]

Tenants copy the generated embed code from Settings → Module → Embed and paste it into any HTML page. The script authenticates via tenantId in the URL — no additional auth required on the consumer side.

Dashboard Widget

"dashboardWidget": {
"component": "CrmDashboardWidget",
"title": "CRM Overview",
"defaultSize": "medium"
}
Sub-fieldDescription
componentExported React component name from the module bundle.
titleWidget title displayed on the tenant dashboard.
defaultSizeLayout hint: small (1×1), medium (2×1), large (2×2). Tenant can resize.

Dependencies

"dependencies": {
"@platform/sdk-core": "^1.0.0",
"@platform/sdk-data": "^1.0.0",
"@platform/sdk-events": "^1.0.0"
}

List all @platform/* SDK packages your module uses. Module Registry validates kernelSdkVersion against each dependency range. The shared: EnterpriseSingletons in federation.config.js ensures React and all platform singletons remain deduplicated in the Shell.


Validation Rules

RuleBehavior
Missing required field400 Bad Request with RFC 9457 error detail
name not in @scope/module format400 Bad Request
Duplicate name409 Conflict
kernelSdkVersion incompatible with running kernel409 Conflict with version mismatch description
permissions[] contains system.* or platform.* prefix400 Bad Request
Manifest JSON body > 64 KB413 Payload Too Large
version not strict semver400 Bad Request

Lifecycle After Registration

Installation steps (all must succeed for active status):

  1. Manifest saved to Module Registry PostgreSQL
  2. JS bundle uploaded to S3: {tenantId}/modules/{name}/{version}/remoteEntry.js
  3. SRI hash verified (SHA-384) — supply-chain attack prevention
  4. Routes registered in API Gateway via Envoy xDS (zero-downtime)
  5. Permissions registered in IAM (idempotent upsert)
  6. Health check passed within 60 s (healthCheck path → 200 OK)

If any step fails, the module status becomes failed. The POST /api/v1/modules/:id/retry-install endpoint retries from the failed step.


SDK Version Compatibility Matrix

ScenarioOutcome
kernelSdkVersion: "^1.0.0" + kernel SDK 1.5.0✅ Compatible (minor/patch are backward-compatible)
kernelSdkVersion: "^1.0.0" + kernel SDK 2.0.0INCOMPATIBLE — module does not load; 12-month deprecation period
kernelSdkVersion: "^2.0.0" + kernel SDK 1.5.0409 Conflict at registration time
tip

Use @platform/sdk-codegen to auto-regenerate TypeScript types from the current OpenAPI spec after every kernel minor release. This keeps your module forward-compatible without manual updates.