<!--
SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: Apache-2.0
-->

# Plugin Configuration Files

Use `plugins.toml` when the `nemo-flow` 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.

:::{note}
NeMo Flow 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:

```toml
version = 1

[[components]]
kind = "observability"
enabled = true

[components.config]
version = 1

[components.config.atof]
enabled = true
output_directory = "logs"
filename = "events.jsonl"
mode = "append"

[policy]
unknown_component = "warn"
unknown_field = "warn"
unsupported_value = "error"
```

The top-level fields are:

| Field | Default | Notes |
|---|---|---|
| `version` | `1` | Plugin configuration format version. Non-`1` versions fail validation by default. |
| `components` | `[]` | Ordered plugin components to validate and activate. |
| `policy` | warn unknown components and fields, error on unsupported values | Global validation policy. |

Each component has:

| Field | Default | Notes |
|---|---|---|
| `kind` | Required | Registered plugin kind, such as `observability` or `adaptive`. |
| `enabled` | `true` | Disabled components are validated but not initialized. |
| `config` | `{}` | Component-local configuration object. The shape depends on `kind`. |

The gateway reads only files named `plugins.toml`.

## Discovery

The gateway can receive plugin configuration from three source classes:

| Source | Use case |
|---|---|
| `plugins.toml` | Normal operator- and project-managed gateway plugin configuration. |
| `[plugins].config` in `config.toml` | Inline gateway config for small or generated setups. |
| `--plugin-config '<json>'` | CI, tests, wrappers, or one-off automation. |

Use only one 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:

1. System: `/etc/nemo-flow/plugins.toml`
2. Project: the nearest `.nemo-flow/plugins.toml` found by walking upward from
   the current directory
3. User: `$XDG_CONFIG_HOME/nemo-flow/plugins.toml`, or
   `~/.config/nemo-flow/plugins.toml` when `XDG_CONFIG_HOME` is 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:

```bash
nemo-flow plugins edit
```

By default, the editor writes the user plugin file:

```text
$XDG_CONFIG_HOME/nemo-flow/plugins.toml
```

or:

```text
~/.config/nemo-flow/plugins.toml
```

Use a scope flag to edit another location:

```bash
nemo-flow plugins edit --project
nemo-flow plugins edit --global
```

Scope flags are mutually exclusive.

`--project` writes the nearest existing `.nemo-flow/plugins.toml`. If none
exists, it writes next to the nearest `.nemo-flow/config.toml`. If neither file
exists in the parent directories, it writes `./.nemo-flow/plugins.toml` from the
current directory.

`--global` writes `/etc/nemo-flow/plugins.toml` and usually requires elevated
filesystem permissions.

The editor menus support these controls:

| Key | Behavior |
|---|---|
| Arrow keys, `j`, `k` | Move through menu items. |
| `Enter`, `Space` | Select or toggle the highlighted item. |
| `Backspace`, `Delete` | Clear the highlighted optional field. |
| `r` | Reset the highlighted field or section to its default. |
| `p` | Preview TOML from the main menu. |
| `s` | Save from the main menu. |
| `?` | Show help. |
| `q`, `Esc` | Go back or cancel without saving. |

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:

```toml
# system plugins.toml
[[components]]
kind = "observability"

[components.config.atof]
enabled = true
output_directory = "/var/log/nemo-flow"
mode = "append"
```

```toml
# user plugins.toml
[[components]]
kind = "observability"

[components.config.atof]
mode = "overwrite"
```

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.

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.

## 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:

```toml
[[components]]
kind = "observability"

[components.config.atof]
enabled = false
mode = "append"
```

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-flow doctor` to inspect the resolved gateway configuration and plugin
diagnostics. For Observability, doctor also reports enabled exporter sections and
checks writable file exporter directories or reachable OTLP endpoints when those
settings are present.

## 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:

- [Observability Configuration](../plugins/observability/configuration.md)
- [Adaptive Configuration](../plugins/adaptive/configuration.md)
- [Adaptive Cache Governor (ACG)](../plugins/adaptive/acg.md)
- [Adaptive Hints](../plugins/adaptive/adaptive-hints.md)
