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

Introduction​

Hook declarations cover plugin subscriptions to host framework extension point events. Source plugins register event handlers through pluginhost.Declarations.Hooks() and execute callback logic when specific events occur. Dynamic plugins declare HookSpec through backend/hooks/*.yaml files, and the build tool embeds them into the lina.plugin.backend.hooks custom section of the .wasm artifact.

Capability Phase: Declaration

Supported Plugin Types: Source plugins, dynamic plugins

Capability Design​

Extension Point Categories​

Extension points are divided into two types: hook extension points (Hook) for event notification, and registrar extension points (Registrar) for declaration collection.

Hook Extension Point List​

Extension PointDescription
auth.login.succeededUser login succeeded
auth.login.failedUser login failed
auth.logout.succeededUser logout succeeded
plugin.installedPlugin installation completed
plugin.enabledPlugin enabled
plugin.disabledPlugin disabled
plugin.uninstalledPlugin uninstallation completed
plugin.upgradedPlugin upgrade completed
system.startedSystem startup completed

The table above lists the hook extension points published by the pluginhost runtime. When linactl (the dynamic plugin builder) scans backend/hooks/*.yaml, it only publishes auth.login.succeeded, auth.login.failed, auth.logout.succeeded, plugin.installed, plugin.enabled, plugin.disabled, plugin.uninstalled, and system.started. Dynamic plugins should not declare plugin.upgraded in backend/hooks/*.yaml.

Execution Modes​

ModeDescriptionApplicable Extension Points
blockingBlocking execution; subsequent flow continues only after the callback completesAll extension points
asyncAsynchronous execution; the callback runs in a separate goroutineHook extension points

Hook extension points support both blocking and async modes. Registrar extension points only support blocking mode.

Hook Payload​

Hook handlers receive a HookPayload parameter containing event data:

MethodDescription
ExtensionPoint()Returns the current extension point
Value(key)Reads a payload value by key
Values()Returns all payload key-value pairs
Services()Returns Services for accessing host capabilities

Payload Keys​

KeyDescription
pluginIdPlugin identifier
nameName
versionVersion
statusStatus
userNameUsername
ipClient IP address
clientTypeClient type
browserBrowser
osOperating system
messageMessage
reasonReason

Authentication Event Reason Codes​

Reason CodeDescription
loginSuccessfulLogin succeeded
loginFailedLogin failed
logoutSuccessfulLogout succeeded
invalidCredentialsInvalid credentials
userDisabledUser disabled
ipBlacklistedIP blacklisted

Dynamic Plugin Hook Declaration (HookSpec)​

Dynamic plugins declare HookSpec hook contracts through backend/hooks/*.yaml files:

FieldTypeDescription
eventstringExtension point name; when built with linactl, the available hooks are determined by the dynamic builder's published hook list
actionstringAction type: insert, sleep, error
modestringExecution mode: blocking or async
tablestringAssociated table name
fieldsmapField mapping
timeoutMsintTimeout in milliseconds
sleepMsintSleep duration in milliseconds (sleep action)
errorMessagestringError message (error action)

Interface Definition​

Source Plugin Interface​

Source plugins register event handlers through Hooks():

MethodDescription
RegisterHookRegisters an event handler with the specified extension point, execution mode, and handler function

Dynamic Plugin Interface​

Dynamic plugins declare hook contracts through backend/hooks/*.yaml. After building, they are embedded in the lina.plugin.backend.hooks custom section of the .wasm artifact.

Usage​

Source Plugin Usage​

Source plugins register event handlers in init() through the hook declaration entry returned by pluginhost.NewDeclarations:

func init() {
plugin := pluginhost.NewDeclarations("my-author-my-domain-my-cap")
if err := plugin.Hooks().RegisterHook(
pluginhost.ExtensionPointAuthLoginSucceeded,
pluginhost.CallbackExecutionModeAsync,
func(ctx context.Context, payload pluginhost.HookPayload) error {
userName := payload.Value("userName")
ip := payload.Value("ip")
// Log login event
return logLoginEvent(ctx, userName.(string), ip.(string))
},
); err != nil {
panic(err)
}

if err := pluginhost.RegisterSourcePlugin(plugin); err != nil {
panic(err)
}
}

Registering a system startup hook:

err := plugin.Hooks().RegisterHook(
pluginhost.ExtensionPointSystemStarted,
pluginhost.CallbackExecutionModeBlocking,
func(ctx context.Context, payload pluginhost.HookPayload) error {
// Execute initialization after system startup
return initializePlugin(ctx)
},
)

Dynamic Plugin Usage​

Dynamic plugins declare hook contracts in backend/hooks/001-plugin-enabled.yaml:

hooks:
- event: plugin.enabled
action: insert
mode: blocking
table: plugin_events
fields:
plugin_id: "{{pluginId}}"
event: "enabled"
timestamp: "{{now}}"

The build tool scans backend/hooks/*.yaml, validates the HookSpec, and embeds it into the lina.plugin.backend.hooks custom section of the .wasm artifact.

Design Constraints​

  • Extension points must be registered in the host registry. Registering an undefined extension point returns an error.
  • Execution mode must match the extension point type. Registrar extension points only support blocking mode.
  • Blocking callbacks affect the main flow. blocking mode callbacks block subsequent processing and should return quickly.
  • Async callbacks execute independently. async mode callbacks run in separate goroutines; failures do not affect the main flow.
  • Payload values require type assertion. HookPayload.Value() returns interface{}, and callers must perform type assertion.
  • Dynamic plugin hooks are declared through contracts. Dynamic plugin hooks are declared at build time via backend/hooks/*.yaml and do not support runtime dynamic registration.