Skip to main content
Version: 0.4.x(Latest)

Overview​

services.Plugins() returns the plugin governance domain capability namespace. Source plugins access it through services.Plugins(), while dynamic plugins declare service: plugins in plugin.yaml and access it via pluginbridge.Default().Plugins(). It is not a single method set but an aggregation of multiple plugin governance sub-capabilities:

Sub-capabilityDescription
Registry()Reads plugin governance views, such as plugin version, installation status, enable state, and tenant-controllable plugin list
Config()Reads the current plugin's own static configuration
State()Queries plugin enable state, authoritative enable state, and capability provider status
Lifecycle()Orchestrates pre-checks and post-notifications for tenant-level plugin disabling and tenant deletion

Plugins() also implements registry read methods, so callers can use services.Plugins().BatchGetPlugins and services.Plugins().ListTenantPlugins to read plugin views directly, or use Registry() to express dependency intent explicitly.

Capability Phase: Runtime

Supported Types: Source plugins, dynamic plugins

Capability Design​

Sub-capability Architecture​

Registry Sub-capability​

The registry capability reads plugin governance views without exposing the host's sys_plugin table or internal runtime cache state. Plugin views typically include plugin ID, version, installation status, enable state, and lifecycle status. Tenant views additionally include name, type, description, installation mode, scope nature, and tenant-level enable state.

Config Sub-capability​

Plugins().Config() reads the current plugin's own static configuration. Its responsibility differs from HostConfig():

CapabilityRead Scope
Plugins().Config()config.yaml under the current plugin's scope
HostConfig()Host configuration values; dynamic plugins must declare authorized keys to read

Plugin configuration read priority:

PrioritySourceDescription
1plugins/<plugin-id>/config.yaml under the production config rootOps override, production environment takes precedence
2manifest/config/config.yaml during developmentDefault config for source plugin development
3manifest/config/config.yaml bundled in dynamic plugin artifactsDefault config from published artifacts

manifest/config/config.example.yaml is only a configuration template and does not participate in runtime default reads.

State Sub-capability​

Plugins().State() provides three query semantics, each serving different consistency and performance requirements:

MethodData Source and SemanticsUse Case
IsEnabledReads the in-process local enable cache state, preserving tenant, request scope, and runtime gatesHigh-frequency checks such as menu filtering, route visibility, and permission filtering
IsEnabledAuthoritativeBypasses local cache state, forces a read of persisted plugin governance stateGlobal middleware, write protection, controls that need immediate response to admin state changes
IsProviderEnabledChecks whether the plugin is platform-enabled and can accept framework capability provider callsPre-checks before calling AI, org, tenant, and other capability providers

Lifecycle Sub-capability​

Plugins().Lifecycle() targets governance modules such as tenant management and plugin management. It is used to ask registered plugins whether an operation may proceed before governance actions, and to notify plugins to perform cleanup or observation logic after operations complete.

It differs from pluginhost.SourcePlugin.Lifecycle():

Entry PointPurpose
pluginhost.SourcePlugin.Lifecycle()A single source plugin registers its own callbacks for install, upgrade, disable, uninstall, tenant disable, tenant delete, and installation mode changes
services.Plugins().Lifecycle()Governance modules orchestrate cross-plugin tenant-level pre-checks and post-notifications

Interface Definitions​

Registry Sub-capability Interface​

MethodDescription
BatchGetPluginsBatch-reads visible plugin views; missing results do not reveal specific reasons
ListTenantPluginsReads the current tenant's controllable plugin list, including global and tenant-level enable states
RegistryReturns the same registry read interface, useful for dependency injection to expose only registry capability

Config Sub-capability Interface​

MethodDescription
GetReturns the raw configuration value
ExistsChecks whether a configuration key exists
ScanScans a configuration section into a target struct
StringReads a string value, returning the default if missing or blank
BoolReads a boolean value, returning the default if missing
IntReads an integer value, returning the default if missing
DurationReads a duration value, returning the default if missing or blank

State Sub-capability Interface​

MethodDescription
IsEnabledReads the in-process local enable cache state, suitable for high-frequency checks
IsEnabledAuthoritativeForces a read of persisted state, suitable for global controls
IsProviderEnabledChecks whether a capability provider is available

Lifecycle Sub-capability Interface​

MethodDescription
EnsureTenantPluginDisableAllowedRuns a pre-check before disabling a plugin for a tenant; blocks if any plugin rejects
NotifyTenantPluginDisabledBest-effort notification after a tenant disables a plugin
EnsureTenantDeleteAllowedRuns a pre-check before tenant deletion
NotifyTenantDeletedBest-effort notification after tenant deletion

Admin Command Interface​

Entry PointMethodDescription
Admin().Plugins()SetPluginEnabledChanges plugin enable state, subject to tenant, lifecycle, and state machine checks
Admin().Plugins()ProvisionTenantDefaultsBackfills default plugin provisioning state for a specified tenant

Dynamic Plugin Interface​

Dynamic MethodDescription
plugins.batch_getBatch-reads visible plugin views
plugins.tenant.listReads the current tenant's controllable plugin list
plugins.enabled.checkChecks whether a plugin is enabled
plugins.provider_enabled.checkChecks whether a capability provider is available
plugins.enabled_authoritative.checkForces a read of persisted enable state
config.getReads the current plugin-scope configuration
lifecycle.tenant_plugin_disable.ensurePre-check for tenant-level plugin disabling
lifecycle.tenant_plugin_disabled.notifyPost-notification for tenant-level plugin disabling
lifecycle.tenant_delete.ensurePre-check for tenant deletion
lifecycle.tenant_deleted.notifyPost-notification for tenant deletion

Dynamic plugin lifecycle is orchestrated through the lifecycle methods listed above in the bridge contract.

Usage​

Source Plugin Usage​

Source plugins access sub-capabilities through services.Plugins() and explicitly pass the domain-required CapabilityContext when reading plugin views:

// Read plugin views
result, err := services.Plugins().BatchGetPlugins(ctx, capabilityCtx, pluginIDs)

// Read the current tenant's controllable plugin list
tenantPlugins, err := services.Plugins().ListTenantPlugins(ctx, capabilityCtx)

// Read plugin configuration
endpoint, err := services.Plugins().Config().String(ctx, "api.endpoint", "")

// High-frequency check for plugin enable state
if !services.Plugins().State().IsEnabled(ctx, pluginID) {
return errors.New("plugin is not enabled")
}

// Global control using authoritative read
if !services.Plugins().State().IsEnabledAuthoritative(ctx, pluginID) {
return errors.New("plugin is disabled")
}

// Capability provider check
if services.Plugins().State().IsProviderEnabled(ctx, "linapro-ai-core") {
// Use AI capability
}

Trusted source plugins execute admin commands:

err := services.Admin().Plugins().SetPluginEnabled(ctx, capabilityCtx, pluginID, true)
err := services.Admin().Plugins().ProvisionTenantDefaults(ctx, capabilityCtx, tenantID)

Dynamic Plugin Usage​

Dynamic plugins declare plugin governance capabilities through hostServices.plugins:

hostServices:
- service: plugins
methods:
- config.get
- plugins.batch_get
- plugins.enabled.check

Dynamic plugin lifecycle is managed through the bridge contract, including callback registration for install, upgrade, disable, and uninstall phases. Usage on the dynamic plugin side:

// Read plugin configuration
value, err := pluginbridge.Default().Plugins().Config().String(ctx, "api.endpoint", "")

Design Constraints​

  • Prefer IsEnabled for high-frequency checks. It is designed for menu, route, and permission filtering to avoid frequent access to persisted state.
  • Use IsEnabledAuthoritative for global controls. When stale cache state could cause security or governance errors, use the authoritative read.
  • Provider status is independent of business entry visibility. Some plugin business entries may not be visible to the current tenant but may still be available as platform capability providers.
  • Ensure* can block. When a lifecycle pre-check returns an error, the governance operation should not proceed.
  • Notify* is best-effort. Post-notifications do not return errors; plugin callback failures should only be logged and not roll back completed governance operations.
  • Dynamic plugin lifecycle uses the bridge contract. The lifecycle contract for dynamic plugin artifacts resides in pluginbridge/contract and is not exposed as a standard callable service through dynamic hostServices.