Skip to content

Module System

James Daley edited this page Mar 8, 2026 · 4 revisions

Module System

Module Types

Type Interface Description
Service IForsettiModule Background services that run concurrently without limit
UI IForsettiUIModule Modules that contribute UI elements. Only one may be active at a time
App IForsettiAppModule Application-level modules (marker interface)

Module Lifecycle

discover  →  validate  →  activate  →  deactivate
   │            │            │              │
   │            │            │              └── module.stop(context)
   │            │            └── entitlement check + module.start(context)
   │            └── compatibility check (platform, version, capabilities)
   └── ManifestLoader scans directory for JSON manifests

Module Manifest Format

Manifests are JSON files with camelCase keys. Each module must provide a manifest file.

Required Fields

Field Type Description
schemaVersion string Must be "1.0"
moduleID string Unique identifier (e.g. "com.example.mymodule")
displayName string Human-readable name
moduleVersion object SemVer object: { "major": 1, "minor": 0, "patch": 0 }
moduleType string One of: "service", "ui", "app"
supportedPlatforms array Must include "windows"
minForsettiVersion object Minimum framework version (SemVer)
entryPoint string Factory key registered in ModuleRegistry

Optional Fields

Field Type Description
maxForsettiVersion object Maximum framework version (SemVer)
capabilitiesRequested array Capability strings (snake_case): "networking", "storage", "secure_storage", "file_export", "telemetry", "routing_overlay", "toolbar_items", "view_injection", "event_publishing"
iapProductID string In-app purchase product ID for entitlement gating

Example Manifest

{
  "schemaVersion": "1.0",
  "moduleID": "com.forsetti.example.service",
  "displayName": "Example Service Module",
  "moduleVersion": { "major": 1, "minor": 0, "patch": 0 },
  "moduleType": "service",
  "supportedPlatforms": ["windows"],
  "minForsettiVersion": { "major": 0, "minor": 1, "patch": 0 },
  "capabilitiesRequested": ["storage", "event_publishing"],
  "entryPoint": "ExampleServiceModule"
}

Compatibility Checking

The CompatibilityChecker validates each manifest against:

  1. Schema version — must be "1.0"
  2. Platform support — supportedPlatforms must include the current platform
  3. Minimum Forsetti version — minForsettiVersion must be ≤ current framework version
  4. Maximum Forsetti version — if set, current framework version must be ≤ maxForsettiVersion
  5. Capabilities — each requested capability is checked against the ICapabilityPolicy

Issues are classified by severity:

Severity Effect
Error Module cannot be activated
Warning Module can be activated but the issue is logged

Reserved Capabilities

  • UIThemeMask — reserved for the framework. Modules requesting this capability receive a warning, and the capability is stripped by UISurfaceManager.

Entitlement Gating

Modules with an iapProductID in their manifest require entitlement validation before activation.

  • IEntitlementProvider.isUnlocked(moduleID) is called before activation
  • If entitlements change at runtime, onEntitlementsChanged triggers reconciliation
  • Reconciliation deactivates modules whose entitlements have been revoked

Module Communication

Modules communicate through the framework's event bus, never directly.

Rules

  • Empty source or target IDs — rejected with ForsettiContextError::InvalidModuleID
  • Self-messaging — rejected with ForsettiContextError::SelfMessageNotAllowed
  • Reserved namespace — forsetti.internal.* namespace is reserved for framework events
  • Framework events bypass the communication guard

Event Bus

The InMemoryEventBus provides pub/sub messaging:

  • subscribe(eventType, handler) — returns a SubscriptionToken (RAII, cancels on destruction)
  • publish(event) — delivers to all matching subscribers
  • Events carry: eventType, sourceModuleID, targetModuleID (optional), payload (JSON)

Clone this wiki locally