Skip to main content

Module Registry — Overview

The Module Registry is the platform's module lifecycle manager. Every capability installed on the platform — CRM, iGaming, analytics, custom tools — is a module registered here. The Registry controls discovery, activation, versioning, migration execution, and deactivation of all modules.


Technical Stack

ComponentTechnology
LanguageGo (net/http + chi router)
DatabasePostgreSQL — manifest history, module state
CacheValkey (valkey-io/valkey-go)
Bundle storageS3 bucket
CDNVersioned immutable URLs
Migrationsgoose — runs module SQL migrations on install/update
gRPCProtobuf definition

Paths & Schemas

Cache (SET):

modules:active:{tenantId}

Bundle storage:

modules/{tenantId}/modules/{name}/{version}/

CDN:

https://cdn.platform.io/modules/{tenantId}/{moduleId}/{version}/remoteEntry.js

gRPC:

proto/platform/module_registry/v1/module_registry_service.proto

Module Lifecycle

REGISTER → DISCOVER → LOAD → MOUNT → CONNECT → RUN → UPDATE → DISABLE
StageDescription
REGISTERModule registered via POST /api/v1/modules with module.manifest.json
DISCOVERUI Shell discovers module via GET /api/v1/modules/active
LOADModule Federation 2.0 loads remote bundle from CDN
MOUNTMFE component rendered in the shell zone
CONNECTModule subscribes to Event Bus events (events.subscribes[])
RUNModule operates, accesses platform primitives via SDK
UPDATEIndependent deploy without shell rebuild. Cache invalidation via module.registry.updated event. Users online at update time see a toast: "Module {name} updated. Refresh to apply."
ROLLBACKRegistry switches entry CDN URL back to previous version — instant rollback
DISABLEDeactivated via Registry. Data is preserved. Module removed from Shell.
INSTALL (state machine)pendinginstallingactive / failed. UI Shell shows module only when status = active. Timeout: MODULE_INSTALL_TIMEOUT_SEC=120. Retry: POST /modules/:id/retry-install

Install Methods

All methods call the same underlying POST /api/v1/modules/install endpoint:

MethodWho uses itHow
MarketplaceTenant adminOpens module catalog → clicks "Install"
CLIDevelopernpx @platform/cli module install @scope/module-name
REST APICI/CD, automationPOST /api/v1/modules/install { "source": "url or package name" }

Bundle Hosting Flow

POST /api/v1/modules/install { "source": "@scope/crm" }

Step 1: Registry downloads bundle from npm / Git URL
Step 2: Upload to S3: {tenantId}/modules/{name}/{version}/remoteEntry.js
Step 3: SRI hash check (SHA-384): manifest hash vs actual file hash
→ mismatch → 422 Unprocessable Entity: "Bundle integrity check failed"
Step 4: entry field rewritten → CDN URL
Step 5: Versioned CDN URL stored: modules:active:{tenantId} updated in Valkey
Step 6: status: active (all steps complete)

Bundle integrity (SRI SHA-384) prevents supply chain attacks — the same pattern used by Apple App Store and Google Play.


Module Manifest

Each module describes itself with a JSON manifest submitted when registering:

FieldTypeRequiredDescription
namestringUnique npm-scope format: @scope/module-name
versionstringSemver: X.Y.Z (strict)
descriptionstringShort human-readable description
entrystringRemote entry URL for Module Federation
exposedComponentstringName of the exported React component
routestringURL path in the sidebar
iconstringIcon name for the sidebar menu
permissionsstring[]Permissions the module registers (min 1, format: lowercase.dot.notation)
dependenciesobjectDependencies — minimum @platform/sdk
healthCheckstringPath for the module's health check endpoint
kernelSdkVersionstringSemver range: "^1.0.0" — minimum compatible kernel SDK
events.publishesstring[]Events the module publishes
events.subscribesstring[]Events the module subscribes to
notificationChannelsarrayNotification channel adapters provided by the module
authProvidersarrayAuth provider adapters provided by the module
embedsarrayWidgets for embedding on external sites
dashboardWidgetobjectTenant dashboard widget: { component, title, defaultSize }
categorystringSidebar grouping: crm, analytics, finance, hr, marketing, custom
dataApiobjectAuto-generates REST endpoints for module models

Maximum manifest size: 64 KB.

Manifest Validation Rules

RuleDetail
All required fields presentMissing any → 400 Bad Request
name formatMust match npm-scope regex @scope/module-name
version formatStrict semver X.Y.Z
kernelSdkVersionSemver range. Incompatible with current kernel SDK → 409 Conflict
permissions[]Minimum 1. Each must start with {moduleId}. prefix. system.* and platform.* reserved → 400 Bad Request
Duplicate name409 Conflict
Max size64 KB → 413 Request Entity Too Large

Versioning and Rollback

Module Registry stores the full history of all manifest versions. Previous versions are archived, not deleted.

Developer publishes v2.0:
Registry registers new version
Admin sees "Update available" in Admin → Modules
Admin confirms → Registry sets tenant entry URL to v2.0 CDN URL

Health check fails within 60 seconds of update:
Auto-rollback → entry URL switched back to v1.9 (still on CDN, immutable)
Admin notified: "Module update failed, rolled back to v1.9"

Manual rollback (Admin):
POST /api/v1/modules/:id/rollback { "targetVersion": "1.9.0" }
→ Registry switches entry URL to v1.9 CDN URL → instant
ParameterValue
Version historyStored indefinitely (text records, minimal size)
CDN bundles retainedLast 5 versions per module. Older versions → S3 Lifecycle delete
Deactivated module bundlesRetained for 90 days after deactivation, then deleted
Module data (PostgreSQL tables)Retained indefinitely
Auto-rollback health check timeout60 seconds after UPDATE
Health check response deadline5 seconds (MODULE_HEALTH_TIMEOUT_MS)

Rollback is code-only: Rollback switches the JavaScript bundle. It does not roll back database migrations. Migrations are forward-only. Module code must be backward-compatible with the schema of the newer version (new columns added as DEFAULT NULL, unknown fields ignored).


Module Data Migrations

Modules store data in PostgreSQL using their own tables, isolated by RLS per tenant_id. The Registry runs migrations automatically:

StageWhat happens
InstallRegistry validates manifest → runs migrations/ via goose → tables created with auto tenant_id RLS policy
UpdateNew version may include new migrations → Registry runs goose up incrementally
DeactivateTables and data remain. Module stops loading in UI Shell.
UninstallSoft delete. Data kept with marker. Full clean-up requires Platform Owner CLI.

Table namespace: Each module gets its own PostgreSQL schema: module_{moduleSlug}. Example: module_crm.contacts. Two modules can have a table named contacts without conflict.

Migration failure: If a migration fails, the module enters ERROR status. The UI Shell does not load it. Previous successful migrations remain. There is no automatic rollback (would risk data loss). Fix: developer publishes a hotfix → PATCH /modules/:id → Registry retries pending migrations.


SDK Major Upgrade Policy

When the kernel SDK releases a new major version (e.g. v1.x → v2.x):

StepWhat happens
1Registry checks kernelSdkVersion for ALL installed modules
2Incompatible modules → status INCOMPATIBLE. Not loaded in UI Shell but data intact
3Tenant notified: "Module X requires update. Contact the module developer."
4Deprecation period: 12 months — old major SDK version supported in parallel
5After deadline: incompatible modules stop loading. Data preserved. Update restores.

REST API

MethodEndpointDescription
POST/api/v1/modulesRegister a new module (submit manifest)
GET/api/v1/modulesList all modules (paginated)
GET/api/v1/modules/activeList active modules for this tenant
GET/api/v1/modules/:idGet module details and current status
PATCH/api/v1/modules/:idUpdate manifest (new version)
PATCH/api/v1/modules/:id/activateActivate a registered module
PATCH/api/v1/modules/:id/deactivateDeactivate module (data preserved)
DELETE/api/v1/modules/:idUninstall module (soft delete)
GET/api/v1/modules/:id/healthModule health check status
POST/api/v1/modules/installInstall module by package name or URL
GET/api/v1/modules/:id/versionsList all manifest versions
POST/api/v1/modules/:id/rollbackRollback to a specific version
POST/api/v1/modules/:id/retry-installRetry a failed installation

Kafka Events Published

EventPayloadDescription
module.registry.registered{ moduleId, name, version }New module registered
module.registry.activated{ moduleId }Module activated for tenant
module.registry.deactivated{ moduleId }Module deactivated
module.registry.updated{ moduleId, version }New version deployed — triggers UI Shell cache invalidation