Plugin Configuration Files
Use plugins.toml when the nemo-relay CLI gateway should activate plugins at
startup. The file contains the same generic plugin configuration document used
by the Rust, Python, and Node.js plugin APIs, but encoded as TOML at the file
root.
This page documents file discovery, precedence, merge behavior, editor behavior, and conflict rules for the CLI gateway. Component-specific fields are documented in the guide for each plugin component.
NeMo Relay plugin configuration keys use snake_case regardless of language or
file format. Node.js helper APIs can have camelCase function names, but the
generic plugin document and component-local config objects use canonical
snake_case keys.
File Shape
plugins.toml uses the canonical plugin document shape:
The top-level fields are:
Each component has:
The gateway reads only files named plugins.toml.
Discovery
The gateway resolves plugin configuration using two kinds of input: file and CLI sources and an optional code-driven layer.
File and CLI configuration comes from one of three mutually exclusive source classes:
Use only one file and CLI source class for a given gateway run. The gateway fails
clearly if file-based plugin config and --plugin-config are both present, or if
plugins.toml and [plugins].config are both present.
When --config path/to/config.toml is supplied, plugin file discovery is scoped
to path/to/plugins.toml. Implicit system, project, and user plugin files are
not loaded for that run.
When no explicit --config path is supplied, the gateway checks these
plugins.toml locations from lowest to highest precedence:
- System:
/etc/nemo-relay/plugins.toml - Project: the nearest
.nemo-relay/plugins.tomlfound by walking upward from the current directory - User:
$XDG_CONFIG_HOME/nemo-relay/plugins.toml, or~/.config/nemo-relay/plugins.tomlwhenXDG_CONFIG_HOMEis not set
Missing files are skipped. If no plugin config source exists, the gateway starts without process-level plugin activation.
Editing Files
Use the interactive editor for Observability and Adaptive plugin configuration:
By default, the editor writes the user plugin file:
or:
Use a scope flag to edit another location:
Scope flags are mutually exclusive.
--project writes the nearest existing .nemo-relay/plugins.toml. If none
exists, it writes next to the nearest .nemo-relay/config.toml. If neither file
exists in the parent directories, it writes ./.nemo-relay/plugins.toml from the
current directory.
--global writes /etc/nemo-relay/plugins.toml and usually requires elevated
filesystem permissions.
The editor menus support these controls:
Text and JSON value prompts use normal line editing. Use the surrounding field menu to reset, clear, preview, or save.
Precedence And Merge Behavior
When more than one plugins.toml file is discovered, later files have higher
precedence. User config overrides project config, and project config overrides
system config.
TOML tables merge recursively:
The effective Agent Trajectory Observability Format (ATOF) config keeps
enabled and output_directory from the system file and uses
mode = "overwrite" from the user file.
The top-level components array is special. Components are matched by kind
across files. A higher-precedence component with the same kind merges into the
lower-precedence component. A component with a different kind is added to the
effective configuration.
The pricing component has one additional merge rule: when both lower and
higher-precedence layers define components.config.sources, the
higher-precedence sources are placed before lower-precedence sources instead of
replacing them. This lets a user or project file override one model while still
falling back to fleet-managed pricing from /etc/nemo-relay/plugins.toml.
Declare each kind at most once inside one plugins.toml file. Duplicate
component kinds in the same file fail before merge. Duplicate singleton
components that reach plugin validation also fail validation.
Arrays inside component config are replaced by the higher-precedence value. Tables inside component config merge recursively.
Configuration Layering
Plugin settings can come from files and the code. The files are the configuration base layer and the code sits on top of it. If there is a conflict, the code will always take precedence over the files. The layering process is as follows:
- Discover and merge the
plugins.tomlfiles from lowest to highest precedence (system → project → user), using the Precedence And Merge Behavior rules above. - Layer the config object you pass to
initializeover that merged base. Any setting it specifies overrides the file value, and the result is the effective config that gets validated and activated.
Files and code differ only in how they treat a setting you omit:
In other words, a file’s gaps are filled by the layers beneath it, but code’s
typed fields are never gaps — they always carry a value, so they always take precedence.
Only the open-ended parts of your code config (which components you include, and
the keys within each component’s config) merge with the files.
Without a filesystem — for example, a WebAssembly (wasm) build — no files are
read, so the base is empty and only your initialize config applies.
Explicit Defaults And Overrides
The editor writes explicit defaults for edited Observability and Adaptive sections. This is intentional. In a layered config model, omitting a field means “inherit a lower precedence value”; it does not mean “delete that value.”
For example, this user file disables ATOF even if a project file enables it:
The merged config may still contain inherited ATOF sibling fields such as
output_directory, but the runtime ignores the section because enabled = false.
To override an inherited non-default field with its default value, write the
default explicitly in the higher-precedence file. For example, use
mode = "append" to override a lower-precedence mode = "overwrite".
There is no tombstone syntax for deleting an inherited nested field while
keeping the rest of the lower-precedence component. To remove inherited settings
entirely, edit the lower-precedence file or override the behavior with another
field such as enabled = false.
Validation
Plugin validation runs before activation. Invalid plugin config blocks gateway startup instead of starting with a partially installed plugin set.
Common validation failures include:
- Unknown component kinds when policy treats them as errors.
- Unknown fields when policy treats them as errors.
- Unsupported field values, such as an invalid exporter mode or transport.
- Duplicate singleton components.
- Enabled components whose build-time features are unavailable.
- Component-specific semantic failures, such as an Agent Trajectory Interchange
Format (ATIF) filename template that does not contain
{session_id}.
Use nemo-relay doctor to inspect the resolved gateway configuration and plugin
diagnostics. For Observability, doctor also reports enabled exporter sections,
checks writable file exporter directories, probes configured ATOF streaming
endpoints, and checks reachable OTLP endpoints when those settings are present.
For Pricing, doctor validates enabled file and inline sources and fails when a
source is unreadable or the catalog schema is invalid.
Relationship To config.toml
config.toml owns gateway and agent setup, such as upstream provider base URLs
and agent command configuration. plugins.toml owns reusable runtime behavior
installed by the plugin system.
Keep long-lived plugin setup in plugins.toml. Use [plugins].config in
config.toml only when a generated or embedded config must keep all gateway
settings in one file. Use --plugin-config for automation that should not write
files.
Legacy observability config sections in config.toml, such as [exporters],
[observability], and [export.openinference], are not supported. Configure
Observability exporters through plugins.toml.
Component Guides
Use the component guides for field-level configuration: