Package Discoverable Plugins

View as Markdown

A discoverable plugin is a package containing one relay-plugin.toml manifest, its native library or worker entrypoint, and an optional component JSON Schema. The manifest lets Relay validate compatibility and integrity before code is loaded or a process is started. Runtime activation still comes from a component in plugins.toml or an equivalent binding configuration.

Manifest Responsibilities

BlockWhat It Controls
[plugin]Stable package identity and plugin kind.
[compat]Supported Relay range plus the native manifest API or worker protocol contract. A typed native plugin that uses the 0.8 SDK should declare a Relay range beginning at 0.8.0.
[defaults] and [capabilities]Initial enabled state and the features the package declares, such as plugin_native, plugin_worker, and config_schema.
[config_schema]An optional JSON Schema path resolved relative to the manifest. Packages that use it also declare the config_schema capability.
[source]The artifact covered by integrity verification and, for managed Python workers, the package root used to create the environment.
[integrity]The SHA-256 digest of source.artifact, plus optional signature evidence.
[load]The native library and symbol, Python module entrypoint, or Rust or custom-command executable entrypoint that Relay starts.

For native packages, compat.native_api = "1" is the authored manifest contract. It is not the C host-table ABI number. The current SDK requests ABI v4 and the host retains frozen v3 and v2 compatibility for older compiled plugins. For workers, declare compat.worker_protocol = "grpc-v1"; the handshake still negotiates the exact protocol, surfaces, authentication token, and lifecycle at startup.

Start from a Complete Manifest

These are the complete manifest shapes used by the checked examples. The path in source.artifact is the file whose bytes the digest covers. The native loader opens that same library; a worker loader either starts the compiled program or imports the Python entrypoint from the managed environment.

1manifest_version = 1
2
3[plugin]
4id = "examples.python_grpc_worker"
5kind = "worker"
6
7[compat]
8relay = ">=0.8.0,<1.0"
9worker_protocol = "grpc-v1"
10
11[defaults]
12enabled = false
13
14[capabilities]
15items = ["plugin_worker", "config_schema"]
16
17[config_schema]
18path = "config.schema.json"
19
20[source]
21manifest_root = "."
22artifact = "nemo_relay_python_grpc_worker_example/worker.py"
23
24[integrity]
25sha256 = "sha256:<worker-source-sha256>"
26
27[load]
28runtime = "python"
29entrypoint = "nemo_relay_python_grpc_worker_example.worker:main"

The Python package has one extra responsibility: source.manifest_root identifies the directory Relay installs into its managed environment. The digest still covers only the declared artifact, so regenerate it whenever worker.py changes. A compiled worker has no managed Python environment and starts the executable named by load.entrypoint.

Relay 0.8 keeps grpc-v1 as the worker protocol identifier, but changes its tool-result messages. Both worker manifests therefore begin their Relay compatibility range at 0.8.0. Rebuild SDK workers and regenerate bindings in a custom worker before packaging; the unchanged protocol name does not make an earlier worker wire-compatible.

Package and Register the Artifact

Use the following procedure to turn the checked manifest template into an artifact that Relay can validate and activate:

  1. Build the shared library or worker from a clean checkout and place the entrypoint at the relative path declared by relay-plugin.toml.

  2. Validate the component schema against valid, invalid, and disabled example configurations. Keep unknown-field behavior aligned between the schema and the plugin’s validation callback.

  3. Generate the artifact digest with shasum -a 256 <artifact> on macOS, sha256sum <artifact> on Linux, or Get-FileHash -Algorithm SHA256 <artifact> in PowerShell. Do this after the final source.artifact bytes are in place; changing that artifact invalidates the digest.

    For example, this Linux command materializes a usable Rust worker manifest without changing the checked template:

    $cp relay-plugin.toml relay-plugin.local.toml
    $sed -i 's#<platform-worker-file>#nemo-relay-rust-grpc-worker-plugin-example#g' \
    > relay-plugin.local.toml
    $digest="$(sha256sum target/debug/nemo-relay-rust-grpc-worker-plugin-example | cut -d' ' -f1)"
    $sed -i "s#sha256:<artifact-sha256>#sha256:$digest#" relay-plugin.local.toml

    The local manifest is deliberately untracked. Rebuilding the executable changes its bytes, so recalculate the digest before every validation attempt.

  4. Validate the materialized manifest. For the compiled example above, run nemo-relay plugins validate ./relay-plugin.local.toml. The Python example keeps its checked digest and uses ./relay-plugin.toml directly. A successful command confirms manifest structure, compatibility syntax, entrypoint resolution, schema loading, and integrity metadata without activating the component.

  5. Register the same path with nemo-relay plugins add --user <materialized-manifest>, or add an explicit [[plugins.dynamic]] reference to the intended plugins.toml.

  6. Activate a valid component, inspect the runtime report, execute a representative managed call, then clear and unload or stop the plugin.

Success means package validation passes before activation, tampering causes integrity validation to fail, the runtime report identifies the dynamic component, and teardown removes its registrations before the library unloads or worker exits. The operator-side trust policy and file layout are documented in Configure Discoverable Plugins.