Skip to content

Engine - Modules #35

Description

@ollieread

The module system is the extension mechanism for the engine. It allows first-party and third-party modules to register bindings, declare permissions, and participate in the engine lifecycle. Core features of TGP, including auth, servers, and permissions, are themselves modules built on the same system available to third parties.

Module sources

The module system supports two sources of modules:

  • Bundled modules — first-party modules shipped inside the binary as part of the panel's own codebase and autoloader. Their manifests are declared in code.
  • External modules — third-party or optional modules installed into the modules directory ($paths->modules), which is a self-contained Composer project with its own vendor/. Metadata is declared in extra.tgp in each module's composer.json.

Both sources produce the same ModuleManifest and ModuleRegistrar structures. The registry does not distinguish between them once loaded.

Module source drivers

How module metadata is loaded depends on the panel's mode. A ModuleSource interface abstracts this, with two implementations:

  • CachedModuleSource (production) — loads bundled module metadata from a prebuilt file shipped with the binary, and external module metadata from a JSON cache file generated during discovery. No reflection at runtime.
  • LiveModuleSource (debug) — discovers and reflects all modules at boot, both bundled and external. Changes to module code are picked up immediately without rebuilding or re-running discovery.

Bootstrap selects the implementation based on mode and provides it to the registry. The registry does not know or care which source it was given.

External module structure

The external modules directory is a Composer project (type: project) with its own composer.json and vendor/. The panel includes the modules autoloader alongside its own. The project requires each installed module individually. A provide block is generated from the panel's own lock file and kept in sync whenever modules are added, removed, or updated to prevent Composer from installing dependencies already satisfied by the panel. Composer is used as a library rather than a standalone tool, giving full programmatic control over repository resolution and installation.

Module-specific metadata not available in composer.json is declared in the extra.tgp key, including the module name, capabilities, registrar class, icon, and definition.

Capabilities

Modules declare capabilities in their manifest. Capabilities are checked at runtime by the parts of the system that enforce them, not by the module system itself. Capabilities include things like CrossRoutes, ExtendSchema, ModifyUi, and DaemonAccess.

Lifecycle

The module lifecycle has two sequential phases, driven by the bootstrapper:

  1. Register — modules register DI bindings and custom resolvers via EngineBuilder
  2. Boot — called after all modules have registered and the container is built; handles cross-module concerns that do not require collectors

Collection is pull-based and not part of the lifecycle. Components trigger collection themselves when they need data from modules.

Subissues

This epic is broken into four focused subissues:

  • Engine: Module discovery and manifest building — Composer integration, manifest and registrar DTOs, registrar attributes, the ModuleSource driver pattern, JSON cache and prebuilt file generation
  • Engine: Module registry — loading from a ModuleSource, the immutable ModuleRegistry, enabled/disabled tracking
  • Engine: Module lifecycle — the register and boot phases driven by EngineBuilder
  • Engine: Collectors — collector and collector handler contracts, #[Collect] and #[Unscoped] attributes, and collection phase mechanics

Out of scope

  • Module API package and SDK for third-party development — handled separately

Metadata

Metadata

Assignees

Labels

layer: engineBase framework and engine work

Type

Projects

Status
Up next

Relationships

None yet

Development

No branches or pull requests

Issue actions