Plugin Shape
A plugin is a configuration-driven installer for Relay behavior. It is not the subscriber, guardrail, or intercept itself. The plugin gives a related set of runtime registrations a stable identity, validates one component’s JSON configuration, and installs those registrations through a component-scoped context.
The Shared Contract
The lifecycle hooks use the same logical shape in every binding. These implementations
all validate tag and install one component-owned subscriber. The callback spelling is
different, but the identity, validation, and ownership rules are the same.
Python
Node.js
Rust
Rust carries the kind on the Plugin implementation. Python and Node.js supply it when
the application calls plugin.register("audit", implementation). In all three cases,
events is a local registration name. Relay qualifies that name with the component
owner, records it during activation, and removes it during clear or rollback.
One component can install several kinds of middleware when the configuration tells a coherent story. For example, a policy component can observe calls, block configured tools, and add an audit mark. A plugin that combines an unrelated exporter, routing policy, and provider client is harder to validate, roll out, and remove safely; those behaviors should normally become separate components.
Activation Is Transactional
Activation begins with a complete plugin document, not an isolated callback. Relay validates the document and every component, including disabled components, so a staged configuration can be checked before it is enabled. An enabled component with error diagnostics cannot activate.
For each valid, enabled component, Relay creates a registration context and calls the plugin’s registration hook. A successful hook commits the registrations as active component state. If the hook fails after installing some behavior, Relay rolls back the partial registrations rather than leaving a half-active plugin. Dynamic loading adds a loader instance around the same component lifecycle; unloading does not begin until the component registrations have been cleared.
The runtime report is the observable record of that process. It identifies loaded components and diagnostics, and it lets an application or deployment test distinguish “configuration parsed” from “runtime behavior is active.”
Teardown Has an Owner
Registrations should be created only through the supplied context. Direct process-global registration escapes component ownership and prevents reliable rollback. The same rule applies to external resources: create clients, tasks, and file handles during registration only when their lifetime is tied to the component and they can be stopped when activation fails or configuration is cleared.
Language-binding applications remove active plugin configuration with the binding’s clear API and can deregister a plugin kind when the implementation itself is no longer available. Dynamic hosts clear component registrations before unloading a native library or stopping a worker. Worker shutdown also ends in-flight callback service, closes the authenticated endpoint, and terminates the managed process.
Verify the Lifecycle
Use the following sequence to verify validation, activation, ownership, and teardown:
- Register or load the plugin implementation under its stable identity.
- Validate one invalid component and confirm the report names the component, field, stable diagnostic code, and error level.
- Validate a disabled invalid component and confirm the same error is still visible.
- Validate and initialize a valid enabled component, then inspect the runtime report before sending application traffic.
- Execute a representative managed call and observe the registration’s effect rather than treating successful initialization as sufficient proof.
- Clear configuration and verify that the same call no longer observes the plugin.
- For a dynamic plugin, unload or stop the implementation only after registrations are gone.
Success means invalid configuration never changes runtime behavior, valid configuration produces an active report and an observable call-path effect, and teardown removes that effect without leaving a loaded dynamic instance or worker process behind.