Skip to main content

Module Registry gRPC Service Reference

Package platform.module_registry.v1 contains the ModuleRegistryService — the gRPC interface for module lifecycle management.

The Module Registry is the control plane for all third-party modules that extend the Platform-Kernel. It validates module manifests, manages the MODULE_STATUS_* lifecycle FSM, and serves active module metadata to the UI Shell (Module Federation 2.0 host) and the API Gateway (tenant routing: modules:active:{tenantId} Valkey cache).

See the gRPC Overview for buf configuration, Go package conventions, deadlines, mTLS, and error mapping.

Proto file: proto/platform/module_registry/v1/module_registry_service.proto

Go package: kernel.internal/platform-kernel/gen/go/platform/module_registry/v1;module_registryv1


ModuleRegistryService — All RPCs

service ModuleRegistryService {
rpc RegisterModule(RegisterModuleRequest) returns (RegisterModuleResponse);
rpc GetModule(GetModuleRequest) returns (GetModuleResponse);
rpc ListModules(ListModulesRequest) returns (ListModulesResponse);
rpc ListActiveModules(ListActiveModulesRequest) returns (ListActiveModulesResponse);
rpc ActivateModule(ActivateModuleRequest) returns (ActivateModuleResponse);
rpc DeactivateModule(DeactivateModuleRequest) returns (DeactivateModuleResponse);
rpc DeleteModule(DeleteModuleRequest) returns (DeleteModuleResponse);
rpc ListModuleVersions(ListModuleVersionsRequest) returns (ListModuleVersionsResponse);
}
RPCDescription
RegisterModuleValidate and register a new module from its manifest
GetModuleRetrieve a module by ID
ListModulesPaginated list with status and category filters
ListActiveModulesAll ACTIVE modules for a tenant — used by UI Shell
ActivateModuleTransition a module to ACTIVE status
DeactivateModuleTransition a module to DISABLED status
DeleteModuleSoft-delete a module
ListModuleVersionsFull version history for a module

Enum — ModuleStatus

enum ModuleStatus {
MODULE_STATUS_UNSPECIFIED = 0;
MODULE_STATUS_PENDING = 1;
MODULE_STATUS_INSTALLING = 2;
MODULE_STATUS_ACTIVE = 3;
MODULE_STATUS_DISABLED = 4;
MODULE_STATUS_FAILED = 5;
MODULE_STATUS_ERROR = 6;
MODULE_STATUS_INCOMPATIBLE = 7;
}
StatusDescription
PENDINGRegistered but not yet installed. Awaiting activation.
INSTALLINGInstallation in progress (manifest validation, DB migrations).
ACTIVEFully operational. Accessible to tenant users. Gateway allows routing.
DISABLEDAdministratively deactivated. Not accessible to users.
FAILEDInstallation or migration failed. Requires manual intervention.
ERRORRuntime error detected by health check.
INCOMPATIBLEKernel version incompatibility detected during registration.

Module lifecycle FSM:

RegisterModule → PENDING

manifest valid


INSTALLING

migrations succeed ──────────── migrations fail
│ │
▼ ▼
ACTIVE FAILED

ActivateModule / DeactivateModule

┌───────────┴──────────┐
▼ ▼
ACTIVE DISABLED

Shared Message — Module

message Module {
string id = 1;
string tenant_id = 2;
string name = 3;
string slug = 4; // URL-safe identifier
string version = 5; // Semantic version (e.g. "1.2.0")
string description = 6;
ModuleStatus status = 7;
google.protobuf.Struct manifest = 8; // Full module.manifest.json contents
string entry_url = 9; // Module Federation 2.0 remote entry URL
string exposed_component = 10; // MF2 exposed component name (e.g. "./App")
string route = 11; // Base route (e.g. "/crm")
string icon = 12; // Icon URL or icon key
string health_check_path = 13; // Path for health probe (e.g. "/health")
string category = 14; // e.g. "crm", "analytics", "integrations"
google.protobuf.Timestamp created_at = 15;
google.protobuf.Timestamp updated_at = 16;
}
FieldDescription
slugURL-safe identifier. Unique per tenant. E.g. crm-pro.
versionSemantic version. Updated by RegisterModule on each version upload.
manifestFull module.manifest.json as a google.protobuf.Struct. Managed by Registry.
entry_urlModule Federation 2.0 remote entry: https://cdn.example.com/crm/remoteEntry.js.
exposed_componentMF2 component export path: "./App". Used by UI Shell for dynamic import.
routeBase path mounted in the UI Shell router. Must be unique per tenant.
health_check_pathGateway polls this path for module health. Default: /health.

Shared Message — ModuleVersion

Immutable version record. Each RegisterModule call with a new version creates a ModuleVersion entry. Previous versions are never deleted (audit trail).

message ModuleVersion {
string id = 1;
string module_id = 2;
string version = 3; // Semantic version
google.protobuf.Struct manifest = 4; // Manifest snapshot at registration time
string bundle_hash = 5; // SHA-256 of the JS bundle (integrity check)
string entry_url = 6; // Remote entry URL for this version
string changelog = 7; // Human-readable changelog (Markdown)
google.protobuf.Timestamp created_at = 8;
}

RegisterModule

Validates and registers a new module (or a new version of an existing module). The manifest is parsed and validated against the Module Registry schema. On success, the module enters PENDING status.

Request — RegisterModuleRequest

message RegisterModuleRequest {
bytes manifest = 1; // Required. Raw JSON manifest (module.manifest.json).
}

The manifest field must be valid JSON conforming to the Module Manifest schema (module.manifest.json). Key required fields:

Manifest fieldTypeDescription
namestringHuman-readable name
slugstringURL-safe identifier (unique per tenant)
versionstringSemantic version
entry_urlstringModule Federation 2.0 remote entry URL
exposed_componentstringExposed MF2 component (e.g. "./App")
routestringBase route (e.g. "/crm")
kernel_versionstringCompatible kernel version range (semver range)

Response — RegisterModuleResponse

message RegisterModuleResponse {
Module module = 1; // Module in PENDING status.
}

gRPC errors:

gRPC statusCondition
INVALID_ARGUMENTManifest fails JSON schema validation or missing required fields
ALREADY_EXISTSModule with the same slug and version already registered
FAILED_PRECONDITIONkernel_version range incompatible with current kernel — status: INCOMPATIBLE
RESOURCE_EXHAUSTEDTenant's modules billing limit exceeded

GetModule

Request — GetModuleRequest

message GetModuleRequest {
string id = 1; // Required. Module UUID.
}

Response — GetModuleResponse

message GetModuleResponse {
Module module = 1;
}

ListModules

Cursor-paginated list of modules for a tenant, with optional status and category filtering.

Request — ListModulesRequest

message ListModulesRequest {
string cursor = 1; // Opaque cursor from ListModulesResponse.next_cursor.
int32 limit = 2; // Page size. Default: 20. Max: 100.
ModuleStatus status_filter = 3; // 0 = all statuses.
string category_filter = 4; // Empty = all categories.
}

Response — ListModulesResponse

message ListModulesResponse {
repeated Module modules = 1;
string next_cursor = 2; // Empty string = no more pages.
bool has_more = 3;
}

ListActiveModules

Returns all modules in ACTIVE status for the calling tenant. This is the primary RPC consumed by the UI Shell to build the navigation menu and Module Federation 2.0 remote manifest.

Request — ListActiveModulesRequest

message ListActiveModulesRequest {}

Response — ListActiveModulesResponse

message ListActiveModulesResponse {
repeated Module modules = 1;
int32 count = 2; // Total number of active modules.
}

Results from ListActiveModules are cached in Valkey using key modules:active:{tenantId}. Cache is invalidated on every ActivateModule or DeactivateModule call. The Gateway uses this cache to block routing to uninstalled modules (404 Not Found when module not in active set).


ActivateModule

Transitions a module from PENDING, DISABLED, or FAILED to ACTIVE. Triggers the ModuleActivatedEvent on platform.module.events.

Request — ActivateModuleRequest

message ActivateModuleRequest {
string id = 1; // Required. Module UUID.
}

Response — ActivateModuleResponse

message ActivateModuleResponse {
Module module = 1; // Module in ACTIVE status.
}

gRPC errors:

gRPC statusCondition
NOT_FOUNDModule ID does not exist
FAILED_PRECONDITIONModule is in INCOMPATIBLE status — cannot be activated
RESOURCE_EXHAUSTEDTenant's modules billing limit exceeded

DeactivateModule

Transitions an ACTIVE module to DISABLED. The module's route is removed from the Gateway routing table and the Valkey modules:active:{tenantId} cache is invalidated. A ModuleDeactivatedEvent is published.

Request — DeactivateModuleRequest

message DeactivateModuleRequest {
string id = 1; // Required. Module UUID.
}

Response — DeactivateModuleResponse

message DeactivateModuleResponse {
Module module = 1; // Module in DISABLED status.
}

DeleteModule

Soft-deletes a module. The module record is retained in the database for audit purposes but becomes invisible to all List* and Get operations. Published modules in registered state cannot be permanently deleted.

Request — DeleteModuleRequest

message DeleteModuleRequest {
string id = 1; // Required. Module UUID.
}

Response — DeleteModuleResponse

message DeleteModuleResponse {}

gRPC errors:

gRPC statusCondition
NOT_FOUNDModule ID does not exist
FAILED_PRECONDITIONModule is in ACTIVE status — must DeactivateModule first

ListModuleVersions

Returns the complete immutable version history for a module. Used by the Admin UI for version rollback and changelog display.

Request — ListModuleVersionsRequest

message ListModuleVersionsRequest {
string module_id = 1; // Required. Module UUID.
}

Response — ListModuleVersionsResponse

message ListModuleVersionsResponse {
repeated ModuleVersion versions = 1; // Ordered newest-first by created_at.
}

Gateway Integration

The Module Registry drives two Gateway behaviours:

BehaviourMechanism
Tenant routingOn every request, Gateway checks modules:active:{tenantId} Valkey cache. If module is not in the active set → 404 Not Found
xDS config reloadActivateModule / DeactivateModule → Envoy xDS LDS/RDS update via xDS control plane. Zero-downtime config reload — no Envoy restart

Valkey cache key pattern:

modules:active:{tenantId} → SET of active module slugs
TTL: no expiry (invalidated on write via ActivateModule / DeactivateModule)