Configure Discoverable Plugins

View as Markdown

Use this guide to add a manifest-backed plugin that another author has packaged. Discoverable plugins are separate from built-in [[components]]: their relay-plugin.toml manifest describes a native shared library or a grpc-v1 worker, while plugins.toml records where Relay can find it and stores its component configuration.

Discoverable plugins are trusted extensions. Native plugins run in the gateway process. Worker plugins run in a separate process, but process isolation is not a security sandbox. Install manifests and artifacts only from sources you trust.

Add and Enable a Plugin

Install a GitHub Release Bundle

Install the GitHub CLI and sign in with gh auth login. You can also set GH_TOKEN or GITHUB_TOKEN. GitHub CLI checks GH_TOKEN first, then GITHUB_TOKEN, then your saved sign-in. Relay does not accept a token as a command argument or save it with the plugin.

Replace <release-tag> with the exact tag of a published release:

nemo-relay plugins install "github:NVIDIA/NeMo-Relay-Plugins@<release-tag>" --user
nemo-relay plugins list --user

Relay downloads the bundle for your operating system, CPU, and CLI libc target. On Linux, musl CLI builds select linux-musl-x86_64 or linux-musl-arm64 bundles; GNU CLI builds select linux-x86_64 or linux-arm64 bundles. Relay checks the archive’s .sha256 checksum and .json release metadata. It also checks the plugin manifest, supported Relay versions, and the runtime file’s digest. If the checks pass, Relay registers the plugin and tries to enable it. Enabled plugins load when the gateway starts.

Use --no-enable to install and register the plugin without enabling it. Only exact release tags work. The installer does not choose the latest release, install from a source tree, or replace an existing plugin. It does not support --config or --plugin-config-path.

Choose Where to Install

Without a scope flag, Relay installs the plugin for your user account. It stores plugins.toml and the bundle in $XDG_CONFIG_HOME/nemo-relay/ if XDG_CONFIG_HOME is set, or in ~/.config/nemo-relay/ otherwise. The bundle goes in installed-plugins/. A Python worker’s environment goes in .dynamic-plugin-environments/.

Use --global to install for the system. The configuration file is /etc/nemo-relay/plugins.toml on Unix or %ProgramData%\nemo-relay\plugins.toml on Windows. The bundle and any Python environment use the same subdirectory names beside that file. Run the command with an account that can write to the system directory. Sign in to GitHub CLI as that account, or set a token in its environment.

On Unix, a global install makes the installed bundle and Python environment readable by other local users. It sets their directories to mode 755 and regular files to 644, or 755 when a file is executable. It sets /etc/nemo-relay/ to 755, and sets plugins.toml and .dynamic-plugins.json to 644. Relay removes the temporary download after installation.

The installing account does not need to be root. Relay keeps the Python environment’s verification key with the environment, so other users can verify the same install. While the plugin is registered, its bundle and environment remain available across Relay runs and user sessions.

When Relay updates global plugins.toml or .dynamic-plugins.json, it sets the file to mode 644, even if it was private before. Other users also need access through each parent directory to use the global plugin. Do not put secrets in global plugins.toml; other local users can read it. Relay does not change the targets of symbolic links in the installed bundle or Python environment.

The bundle and Python environment permission changes apply to release installs. plugins add --global keeps the permissions of local plugin files. On Windows, access follows the directory’s Windows permissions.

Enable and Manage the Plugin

If the bundle has no signature, Relay registers it but cannot enable it under the default trust policy. The install command reports an activation error, and the plugin stays disabled. Python workers are an exception: their package setup can run code, so Relay rejects the install before setup if the host does not trust the bundle. Set the trust rule first, then retry the install.

To allow an unsigned plugin, add this rule to the selected plugins.toml. Replace <plugin-id> with the ID in relay-plugin.toml. Registered plugins also show their IDs in nemo-relay plugins list:

[plugins.policy.overrides."<plugin-id>"]
attestation = "integrity_only"

You can also use signature_if_present, which checks a signature when the plugin has one. Use either setting only for a source you trust. Then run nemo-relay plugins enable <plugin-id>. To require a signature, use a signed bundle and add its public key to trusted_public_keys. Relay never changes your trust policy during install.

nemo-relay plugins list --user shows user plugins. nemo-relay plugins list --global shows system plugins. Without either flag, nemo-relay plugins list shows both. The list shows the source and release tag for managed installs. Use nemo-relay plugins inspect <plugin-id> for details.

Use these commands to remove a plugin:

  • nemo-relay plugins remove <plugin-id> --user removes the registration but keeps the bundle. It deletes a managed Python environment, if one exists.
  • nemo-relay plugins uninstall <plugin-id> --user removes the registration, bundle, and managed Python environment. You can run it after remove. It refuses plugins added from local paths.

Both commands accept --global for a system plugin. Without a scope, remove asks you to choose one if the ID appears in both scopes; uninstall uses the user scope.

Register a Local Manifest

Validate the manifest before registering it in user configuration:

nemo-relay plugins validate ./acme-plugin/relay-plugin.toml
nemo-relay plugins add --user ./acme-plugin/relay-plugin.toml
nemo-relay plugins inspect acme.plugin
nemo-relay plugins enable acme.plugin
nemo-relay plugins validate acme.plugin

add writes a [[plugins.dynamic]] reference to the selected plugins.toml scope and stores CLI lifecycle state next to it. enable changes that lifecycle state; it does not load code immediately. Relay validates and loads enabled plugins when the gateway starts. Use plugins list, plugins inspect, and plugins validate to review current state and diagnostics. Use disable or remove to stop loading a registered plugin.

plugins list and plugins inspect calculate current validation and policy status in memory and do not write .dynamic-plugins.json. plugins validate persists its result for a registered plugin; add, enable, disable, and remove persist their explicit lifecycle changes. Gateway startup also checks plugin status without writing shared lifecycle state.

To share a Python worker across users, register it in the system scope with nemo-relay plugins add --global <manifest> from an account that can write the system configuration directory. Ensure the system plugins.toml, lifecycle state, managed environment, and manifest are readable and traversable by every Relay daemon user, while keeping them writable only by administrators. Startup validates the managed environment without writing the shared lifecycle state.

You can also add the reference directly when configuration is provisioned by automation:

[[plugins.dynamic]]
manifest = "./acme-plugin/relay-plugin.toml"
[plugins.dynamic.config]
mode = "audit"

The manifest path resolves relative to the plugins.toml file. The config table supplies the synthesized component configuration. Before enabling or running a plugin, use nemo-relay plugins validate <plugin-id> to check the manifest, trust evidence, and optional static JSON Schema. During gateway activation, Relay loads the enabled adapter before it validates the synthesized component; a worker runs its Validate call after its process starts.

Do not add a Python worker only with this TOML record. Run nemo-relay plugins add <path> to register a Python worker because Relay must create and retain its managed Python environment before it can activate the worker.

Validate Before Loading Code

Relay validates a manifest before it activates plugin code. The manifest must declare a supported manifest_version, plugin ID and kind, Relay compatibility, the lane-specific ABI or worker protocol, supported capabilities, a disabled default, and one load contract. A manifest that declares config_schema must also declare the config_schema capability.

Every discoverable plugin needs source.artifact and an integrity.sha256 digest so Relay can verify the artifact. A host can also require an Ed25519 signature and trusted public key. Use nemo-relay plugins validate <plugin-id> to evaluate the resolved host policy and artifact trust evidence before you run the gateway.

The following policy requires a valid signature from one trusted Ed25519 public key:

[plugins.policy.defaults]
startup = "required"
attestation = "signature_required"
trusted_public_keys = ["ed25519:<base64-public-key>"]

startup = "required" makes lifecycle preflight failures for an enabled plugin fatal. When a policy field is omitted, the host defaults to startup = "required" and attestation = "signature_required". An unsigned dynamic plugin is therefore refused unless you sign its artifact and configure a trusted public key, or explicitly select integrity_only or signature_if_present. A later native or worker load or activation failure still stops gateway startup; correct or disable the plugin to start without it. attestation accepts integrity_only, signature_if_present, or signature_required; integrity verification always checks the declared artifact digest.

Use startup = "required" when failed integrity or signature verification must prevent worker startup. An optional trust failure records failed lifecycle state, but does not itself prevent an enabled worker from launching. The native loader also verifies load.library; the worker loader relies on lifecycle trust evaluation.

Use [[plugins.policy.rules]] to apply an effect by match_kind or match_plugin_id, and [plugins.policy.overrides."plugin.id"] for one specific plugin. Rules and overrides can set allowed, startup, attestation, and trusted_public_keys.

Ask the plugin author for the correct manifest and artifact. If you also need to inspect or rebuild the package, the native plugin guide explains in-process Rust shared libraries, while the worker plugin guide covers Relay-managed Rust, Python, and custom-command workers. Protocol implementers can use the grpc-v1 protocol reference to inspect the complete transport contract.