Skip to content

feat: implement dynamic plugin host policy and attestation gates - #302

Merged
rapids-bot[bot] merged 11 commits into
NVIDIA:mainfrom
afourniernv:afournier/relay-339-implement-host-policy-capabilities-and-attestation
Jun 25, 2026
Merged

feat: implement dynamic plugin host policy and attestation gates#302
rapids-bot[bot] merged 11 commits into
NVIDIA:mainfrom
afourniernv:afournier/relay-339-implement-host-policy-capabilities-and-attestation

Conversation

@afourniernv

@afourniernv afourniernv commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Overview

Implement RELAY-339 host policy, capability enforcement, startup classification, and attestation gating for dynamic plugins.

  • I confirm this contribution is my own work, or I have the right to submit it under this project's license.
  • I searched existing issues and open pull requests, and this does not duplicate existing work.

Details

  • add layered dynamic plugin host policy parsing and merge behavior for defaults, rules, and plugin-ID overrides
  • enforce the dynamic plugin capability taxonomy through host policy evaluation
  • evaluate and persist effective startup classification and attestation mode in dynamic plugin lifecycle state
  • gate dynamic plugin add, enable, validate, and required startup preflight on host policy and trust outcomes
  • implement integrity verification plus signature verification with host-owned trusted public keys
  • persist refreshed policy and trust outcomes into lifecycle state during startup preflight
  • add CLI, lifecycle, config, and core coverage for policy precedence, capability gating, integrity failures, signature-required failures, and malformed signature evidence

Where should the reviewer start?

Start in crates/cli/src/plugins/policy.rs, then review the trust and startup enforcement changes in crates/cli/src/plugins/lifecycle.rs, then the config/startup coverage in crates/cli/tests/coverage/config_tests.rs.

Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

  • Relates to RELAY-339

Summary by CodeRabbit

  • New Features

    • Added dynamic plugin host policy support (defaults, rules, per-plugin overrides), surfaced across dynamic plugin add/validate/enable and required-startup enforcement.
    • Added integrity + attestation trust evaluation using SHA-256 and signature verification against trusted keys.
    • Enhanced CLI outputs (plugin lifecycle/registry views and JSON/TTY responses) with policy/trust state, startup class, attestation mode, and richer lifecycle failure metadata.
  • Bug Fixes

    • Improved lifecycle error reporting with explicit refusal/error codes (e.g., policy_blocked) and more accurate “valid”/failure-state derivation across policy + trust.

Signed-off-by: Alex Fournier <afournier@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Jun 25, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions github-actions Bot added size:XL PR is extra large Feature a new feature labels Jun 25, 2026
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 166908e5-3e61-41f5-b69a-57bf3cb69990

📥 Commits

Reviewing files that changed from the base of the PR and between e3a39d7 and ecbba98.

📒 Files selected for processing (1)
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (10)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Use snake_case naming convention for Rust identifiers (e.g., nemo_relay_tool_call)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

**/*.rs: Run cargo fmt --all to format all Rust code
Run cargo clippy --workspace --all-targets -- -D warnings to enforce all clippy lints as errors

**/*.rs: Run cargo fmt --all when Rust files changed as part of WebAssembly work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files changed as part of WebAssembly work

**/*.rs: If any Rust code changed, always run just test-rust
If any Rust code changed, also run cargo fmt --all
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings
Run Rust formatting with cargo fmt --all
Run Rust linting with cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Use cargo fmt for Rust code formatting
Run cargo clippy -- -D warnings to lint Rust code and treat all warnings as errors
Use Rust snake_case naming convention for Rust identifiers
Include SPDX license header in all Rust source files using double-slash comment syntax
Validate Rust code with uv run pre-commit run --all-files to enforce cargo fmt formatting check, cargo clippy lints, and cargo deny aud...

Files:

  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
{crates/adaptive/**/*.rs,**/*test*.{rs,py,go,ts,js},**/*adaptive*test*.{rs,py,go,ts,js},docs/plugins/adaptive/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Maintain documented and tested validation and report behavior for adaptive surfaces

Files:

  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
**/{Cargo.toml,**/*.rs}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Maintain consistency between Rust package names in Cargo.toml and their actual usage across the codebase

Files:

  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
**/*.{h,hpp,c,cpp,rs}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Ensure FFI header and library naming follows consistent conventions across platform-specific builds

Files:

  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update Rust crate names and module prefixes during coordinated rename operations

Files:

  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
**/*.{rs,py,js,ts,tsx,jsx,go,sh,toml,yaml,yml,md}

📄 CodeRabbit inference engine (AGENTS.md)

Keep SPDX headers on source, docs, scripts, and configuration files. The project is Apache-2.0.

Files:

  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
**/*.{rs,py,go,js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions: Rust and Python use snake_case, C FFI exports prefixed nemo_relay_, Go uses PascalCase for public APIs, Node.js uses camelCase.

Files:

  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
crates/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

crates/**/*.rs: Keep async behavior on the existing tokio-based model. Bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.
Use Json = serde_json::Value in Rust-facing runtime APIs for JSON payload handling.

Files:

  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
**

⚙️ CodeRabbit configuration file

**:

AGENTS.md

This file provides guidance to agents, including Claude Code and OpenAI Codex, when working in this repository.

Project Overview

NeMo Relay is a multi-language agent runtime framework for execution scopes, lifecycle events, middleware, plugins, and observability around tool and LLM calls. The core runtime is Rust. Primary supported bindings are Rust, Python, and Node.js. Go, WebAssembly, and the raw C FFI are experimental and source-first.

The shared runtime model is:

  1. Scope stacks decide where work belongs and which scope-local behavior is visible.
  2. Middleware registries decide what guardrails and intercepts run around managed calls.
  3. Plugins install reusable runtime behavior from configuration.
  4. Events record runtime behavior in ATOF form.
  5. Subscribers and exporters consume events in-process or export them to ATIF, OpenTelemetry, OpenInference, or other backends.

Repository Structure

The repository layout separates the Rust runtime, language bindings, documentation,
integration patches, and agent-facing skills.

crates/
  core/       # Rust core runtime crate, published as nemo-relay
  adaptive/   # Adaptive runtime primitives and plugin components
  python/     # PyO3 native extension for the Python package
  ffi/        # Raw C ABI layer used by downstream bindings such as Go
  node/       # NAPI Node.js binding and JavaScript/TypeScript entry points
  wasm/       # wasm-bindgen WebAssembly binding and JS wrappers
python/
  nemo_relay/  # Python wrapper package: scopes, tools, LLM, middleware, typed helpers, plugins, adaptive helpers
  tests/      # Python tests
go/
  nemo_relay/  # Experimental Go CGo binding and tests
fern/         # Fern documentation site
scripts/      # Stable wrappers and helper scripts; build/test/docs entry points live in justfile
third_party/  # P...

Files:

  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
🔇 Additional comments (2)
crates/cli/tests/coverage/plugins_lifecycle_tests.rs (2)

14-14: 📐 Maintainability & Code Quality

Confirm Rust validation was run.

This Rust test change needs the required validation: just test-rust, cargo fmt --all, and cargo clippy --workspace --all-targets -- -D warnings.

As per coding guidelines, "**/*.rs: Any Rust change must run just test-rust; Any Rust change must run cargo fmt --all; Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings."

Source: Coding guidelines


183-305: LGTM!

Also applies to: 1224-1235


Walkthrough

CLI config now parses [plugins].policy, evaluates plugin integrity and Ed25519 trust, enforces required startup during config resolution, updates lifecycle state and structured outputs, and expands tests for the new policy/trust paths.

Changes

Dynamic plugin policy and trust

Layer / File(s) Summary
Policy config and resolution
crates/cli/Cargo.toml, crates/cli/src/plugins.rs, crates/cli/src/plugins/policy.rs, crates/cli/src/config.rs
Adds policy and crypto dependencies, defines host-policy types and merge behavior, parses [plugins].policy, and stores the merged policy in resolved CLI config.
Trust evaluation primitives
crates/cli/src/plugins/lifecycle/trust.rs, crates/cli/tests/cli_tests.rs, crates/cli/tests/coverage/config_tests.rs, crates/cli/tests/coverage/plugins_lifecycle_tests.rs, crates/core/tests/unit/plugin_dynamic_tests.rs
Evaluates artifact integrity and Ed25519 trust, records integrity/authenticity states, and adds manifest/signature test helpers and coverage.
Lifecycle enforcement and persistence
crates/cli/src/plugins/lifecycle.rs, crates/core/src/plugin/dynamic/registry.rs, crates/cli/src/plugins/lifecycle/state.rs
Lifecycle commands and config resolution evaluate policy and trust, enforce required startup, update registry state, and persist hydrated validation fields.
Response schemas and rendered output
crates/cli/src/error.rs, crates/cli/src/plugins/lifecycle/responses.rs
Lifecycle errors carry explicit codes, JSON responses use typed status fields, and list, inspect, validate, and summary output render the new policy and trust data.
Test coverage
crates/cli/tests/coverage/*, crates/cli/tests/cli_tests.rs, crates/core/tests/unit/plugin_dynamic_tests.rs
CLI, coverage, and unit tests generate signed manifests and assert host-policy, trust, startup-preflight, and structured output behavior.

Sequence Diagram(s)

sequenceDiagram
  participant resolve_server_config
  participant evaluate_dynamic_plugin_host_policy
  participant evaluate_dynamic_plugin_trust
  participant enforce_required_dynamic_plugin_startup
  participant DynamicPluginRegistry
  resolve_server_config->>evaluate_dynamic_plugin_host_policy: resolve [plugins].policy
  resolve_server_config->>evaluate_dynamic_plugin_trust: evaluate manifest integrity and authenticity
  resolve_server_config->>enforce_required_dynamic_plugin_startup: require startup for enabled plugins
  enforce_required_dynamic_plugin_startup->>DynamicPluginRegistry: persist evaluated policy and validation status
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • NVIDIA/NeMo-Relay#292: Extends the same CLI lifecycle surfaces in crates/cli/src/config.rs, crates/cli/src/error.rs, and crates/cli/src/plugins/lifecycle.rs.
  • NVIDIA/NeMo-Relay#290: Also updates crates/cli/src/config.rs for dynamic-plugin loading and ResolvedConfig shape.
  • NVIDIA/NeMo-Relay#279: Shares the dynamic plugin registry and lifecycle groundwork used by update_policy_status.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.59% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title follows Conventional Commits and accurately summarizes the main dynamic plugin policy and attestation changes.
Description check ✅ Passed The description matches the template well, with overview, details, review start, checklist items, and related issue reference filled in.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the lang:rust PR changes/introduces Rust code label Jun 25, 2026
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
@afourniernv

Copy link
Copy Markdown
Contributor Author

/ok to test 38efcab

@afourniernv
afourniernv marked this pull request as ready for review June 25, 2026 17:10
@afourniernv
afourniernv requested a review from a team as a code owner June 25, 2026 17:10
@github-actions

Copy link
Copy Markdown

1 similar comment
@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/cli/src/plugins/lifecycle.rs`:
- Around line 200-216: The installed-plugin validation flow in
lifecycle::update_registry_policy_status is only persisting policy state, so
trust failures from the preceding validation update are lost when policy passes.
Replace the manual validation/policy split in the lifecycle path with the
centralized helper that already persists both policy and trust state via
policy.or(trust), and make sure the registry write for
DynamicPluginValidationStatus keeps the trust failure details in
last_error/message for the plugin_id entry.
- Around line 382-401: Update the refusal handling in lifecycle.rs so `plugins
enable` returns the same specific lifecycle refusal codes as `plugins add`
instead of the generic `refused`. In the `enabled && !policy.policy_satisfied`
and `enabled && let Some(failure) = trust.failure()` branches inside the enable
path, use the existing policy/trust failure classification logic to emit
`policy_blocked`, `integrity_failed`, or `attestation_failed` as appropriate,
while keeping `plugin_refused` as the shared helper if needed. Locate the enable
flow around `policy.policy_satisfied`, `trust.failure()`, and `plugin_refused`,
and make sure JSON callers can distinguish each failure type.

In `@crates/cli/src/plugins/lifecycle/trust.rs`:
- Around line 197-205: The error code classification in the match block (lines
197-205) currently uses the attestation_mode to determine whether to return
"integrity_verification_failed" or "attestation_verification_failed", but it
should instead inspect the actual kind or type of failure contained in
self.failure to determine the correct code. Integrity-related failures should
always map to "integrity_verification_failed" regardless of the attestation
mode, and attestation-specific failures should map to
"attestation_verification_failed". Examine the failure object structure to
determine its kind and match the error code accordingly.
- Around line 212-228: Short-circuit evaluate_dynamic_plugin_trust when the host
policy is already unsatisfied, instead of calling verify_integrity first. In
evaluate_dynamic_plugin_trust, check policy.policy_satisfied up front and return
an EvaluatedDynamicPluginTrust failure driven by the policy state without
touching manifest-controlled artifact paths. Keep verify_integrity and
evaluate_authenticity only on the allowed path so blocked plugins cannot cause
artifact reads before rejection.

In `@crates/cli/tests/coverage/config_tests.rs`:
- Around line 1015-1017: The policy-only layering test in
load_plugin_toml_config_from_paths should also assert that the resolved plugin
config has no runtime value, so add a check that resolved.value is None
alongside the existing unwraps. Use the resolved symbol in config_tests.rs to
lock down that plugins.policy does not flow into the runtime plugin merge path
and to prevent future config-source conflict regressions.

In `@crates/cli/tests/coverage/plugins_lifecycle_tests.rs`:
- Around line 441-445: The policy-state check in the lifecycle test is too loose
because `list.contains("valid")` can also match `"invalid"`. Update the
assertion in `plugins_lifecycle_tests` to verify the exact rendered
policy/validation state from the `list` output, using the relevant row/column
text rather than a substring match, so the test fails on incorrect validation
output.

In `@crates/core/src/plugin/dynamic.rs`:
- Around line 52-77: The DynamicPluginCapability wire rename breaks existing
plugin_native and plugin_worker values in allowed_capabilities and persisted
.dynamic-plugins.json data. Update DynamicPluginCapability in
crates/core/src/plugin/dynamic.rs to keep backward compatibility by adding serde
aliases (or an equivalent migration path) for the old names while preserving
plugin.native and plugin.worker as the canonical serialized forms, and make sure
the same handling applies to any deserialize/parse logic tied to this enum.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 5b5800f6-e8c1-4574-9802-a51dde647168

📥 Commits

Reviewing files that changed from the base of the PR and between 2d42dc0 and 38efcab.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (17)
  • crates/cli/Cargo.toml
  • crates/cli/src/config.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/core/src/plugin/dynamic.rs
  • crates/core/src/plugin/dynamic/manifest.rs
  • crates/core/src/plugin/dynamic/registry.rs
  • crates/core/tests/unit/plugin_dynamic_tests.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Check / Run
  • GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (20)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Use snake_case naming convention for Rust identifiers (e.g., nemo_relay_tool_call)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

**/*.rs: Run cargo fmt --all to format all Rust code
Run cargo clippy --workspace --all-targets -- -D warnings to enforce all clippy lints as errors

**/*.rs: Run cargo fmt --all when Rust files changed as part of WebAssembly work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files changed as part of WebAssembly work

**/*.rs: If any Rust code changed, always run just test-rust
If any Rust code changed, also run cargo fmt --all
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings
Run Rust formatting with cargo fmt --all
Run Rust linting with cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Use cargo fmt for Rust code formatting
Run cargo clippy -- -D warnings to lint Rust code and treat all warnings as errors
Use Rust snake_case naming convention for Rust identifiers
Include SPDX license header in all Rust source files using double-slash comment syntax
Validate Rust code with uv run pre-commit run --all-files to enforce cargo fmt formatting check, cargo clippy lints, and cargo deny aud...

Files:

  • crates/cli/src/plugins.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/core/src/plugin/dynamic.rs
  • crates/core/src/plugin/dynamic/manifest.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/core/src/plugin/dynamic/registry.rs
  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
**/{Cargo.toml,**/*.rs}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Maintain consistency between Rust package names in Cargo.toml and their actual usage across the codebase

Files:

  • crates/cli/src/plugins.rs
  • crates/cli/Cargo.toml
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/core/src/plugin/dynamic.rs
  • crates/core/src/plugin/dynamic/manifest.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/core/src/plugin/dynamic/registry.rs
  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
**/*.{h,hpp,c,cpp,rs}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Ensure FFI header and library naming follows consistent conventions across platform-specific builds

Files:

  • crates/cli/src/plugins.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/core/src/plugin/dynamic.rs
  • crates/core/src/plugin/dynamic/manifest.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/core/src/plugin/dynamic/registry.rs
  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update Rust crate names and module prefixes during coordinated rename operations

Files:

  • crates/cli/src/plugins.rs
  • crates/cli/Cargo.toml
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/core/src/plugin/dynamic.rs
  • crates/core/src/plugin/dynamic/manifest.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/core/src/plugin/dynamic/registry.rs
  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
**/*.{rs,py,js,ts,tsx,jsx,go,sh,toml,yaml,yml,md}

📄 CodeRabbit inference engine (AGENTS.md)

Keep SPDX headers on source, docs, scripts, and configuration files. The project is Apache-2.0.

Files:

  • crates/cli/src/plugins.rs
  • crates/cli/Cargo.toml
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/core/src/plugin/dynamic.rs
  • crates/core/src/plugin/dynamic/manifest.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/core/src/plugin/dynamic/registry.rs
  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
**/*.{rs,py,go,js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions: Rust and Python use snake_case, C FFI exports prefixed nemo_relay_, Go uses PascalCase for public APIs, Node.js uses camelCase.

Files:

  • crates/cli/src/plugins.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/core/src/plugin/dynamic.rs
  • crates/core/src/plugin/dynamic/manifest.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/core/src/plugin/dynamic/registry.rs
  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
crates/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

crates/**/*.rs: Keep async behavior on the existing tokio-based model. Bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.
Use Json = serde_json::Value in Rust-facing runtime APIs for JSON payload handling.

Files:

  • crates/cli/src/plugins.rs
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/core/src/plugin/dynamic.rs
  • crates/core/src/plugin/dynamic/manifest.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/core/src/plugin/dynamic/registry.rs
  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
**

⚙️ CodeRabbit configuration file

**:

AGENTS.md

This file provides guidance to agents, including Claude Code and OpenAI Codex, when working in this repository.

Project Overview

NeMo Relay is a multi-language agent runtime framework for execution scopes, lifecycle events, middleware, plugins, and observability around tool and LLM calls. The core runtime is Rust. Primary supported bindings are Rust, Python, and Node.js. Go, WebAssembly, and the raw C FFI are experimental and source-first.

The shared runtime model is:

  1. Scope stacks decide where work belongs and which scope-local behavior is visible.
  2. Middleware registries decide what guardrails and intercepts run around managed calls.
  3. Plugins install reusable runtime behavior from configuration.
  4. Events record runtime behavior in ATOF form.
  5. Subscribers and exporters consume events in-process or export them to ATIF, OpenTelemetry, OpenInference, or other backends.

Repository Structure

The repository layout separates the Rust runtime, language bindings, documentation,
integration patches, and agent-facing skills.

crates/
  core/       # Rust core runtime crate, published as nemo-relay
  adaptive/   # Adaptive runtime primitives and plugin components
  python/     # PyO3 native extension for the Python package
  ffi/        # Raw C ABI layer used by downstream bindings such as Go
  node/       # NAPI Node.js binding and JavaScript/TypeScript entry points
  wasm/       # wasm-bindgen WebAssembly binding and JS wrappers
python/
  nemo_relay/  # Python wrapper package: scopes, tools, LLM, middleware, typed helpers, plugins, adaptive helpers
  tests/      # Python tests
go/
  nemo_relay/  # Experimental Go CGo binding and tests
fern/         # Fern documentation site
scripts/      # Stable wrappers and helper scripts; build/test/docs entry points live in justfile
third_party/  # P...

Files:

  • crates/cli/src/plugins.rs
  • crates/cli/Cargo.toml
  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/core/src/plugin/dynamic.rs
  • crates/core/src/plugin/dynamic/manifest.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/core/src/plugin/dynamic/registry.rs
  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
**/*.{py,txt,toml,cfg,yaml,yml}

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update Python package names and top-level module imports during coordinated rename operations

Files:

  • crates/cli/Cargo.toml
**/Cargo.toml

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update WebAssembly crate names and generated package names during coordinated rename operations

Confirm or infer the target release version from upstream/main:Cargo.toml. Derive the release branch as release/<major>.<minor>.

**/Cargo.toml: Maintain Cargo.toml [workspace.package].version as the source of truth for the Rust workspace and Python build versioning
Keep Cargo.toml [workspace.dependencies] self-references aligned with the workspace version when the workspace version changes
After updating workspace package entries, run cargo check --workspace to refresh Cargo.lock

Files:

  • crates/cli/Cargo.toml
{docs/**,README.md,**/Cargo.toml,**/package.json,**/*.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Ensure renamed public surfaces are reflected consistently in manifests and docs for large or public-facing changes

Files:

  • crates/cli/Cargo.toml
**/*.{md,mdx,py,sh,yaml,yml,toml,json}

📄 CodeRabbit inference engine (.agents/skills/contribute-docs/SKILL.md)

Keep package names, repo references, and build commands current

Files:

  • crates/cli/Cargo.toml
**/*.toml

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Include SPDX license header in TOML configuration files using hash comment syntax

Files:

  • crates/cli/Cargo.toml
{crates/adaptive/**/*.rs,**/*test*.{rs,py,go,ts,js},**/*adaptive*test*.{rs,py,go,ts,js},docs/plugins/adaptive/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Maintain documented and tested validation and report behavior for adaptive surfaces

Files:

  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/core/tests/unit/plugin_dynamic_tests.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/cli/tests/coverage/launcher_tests.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/core/tests/unit/plugin_dynamic_tests.rs
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/core/src/plugin/dynamic.rs
  • crates/core/src/plugin/dynamic/manifest.rs
  • crates/core/src/plugin/dynamic/registry.rs
  • crates/core/tests/unit/plugin_dynamic_tests.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

crates/core/**/*.rs: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths. Keep errors explicit and binding-appropriate at the wrapper layer.

Files:

  • crates/core/src/plugin/dynamic.rs
  • crates/core/src/plugin/dynamic/manifest.rs
  • crates/core/src/plugin/dynamic/registry.rs
  • crates/core/tests/unit/plugin_dynamic_tests.rs
crates/{core,adaptive}/**

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full matrix across Rust, Python, Go, Node.js, and WebAssembly

Files:

  • crates/core/src/plugin/dynamic.rs
  • crates/core/src/plugin/dynamic/manifest.rs
  • crates/core/src/plugin/dynamic/registry.rs
  • crates/core/tests/unit/plugin_dynamic_tests.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/core/src/plugin/dynamic.rs
  • crates/core/src/plugin/dynamic/manifest.rs
  • crates/core/src/plugin/dynamic/registry.rs
  • crates/core/tests/unit/plugin_dynamic_tests.rs
**/*config*.{rs,ts,py,go,js,json,yaml,yml}

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Ensure dynamic config shape still matches the documented canonical model

Files:

  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/src/config.rs
🔇 Additional comments (13)
crates/core/src/plugin/dynamic/manifest.rs (1)

468-487: LGTM!

crates/core/src/plugin/dynamic/registry.rs (1)

173-189: LGTM!

crates/core/tests/unit/plugin_dynamic_tests.rs (1)

578-621: LGTM!

Also applies to: 748-759

crates/cli/src/plugins/lifecycle/responses.rs (2)

14-27: LGTM!

Also applies to: 31-41, 66-86, 89-108, 112-127, 157-178, 230-235, 250-291, 296-331


14-331: 📐 Maintainability & Code Quality

Run the required Rust checks

cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings, and just test-rust still need to pass for this Rust change.

crates/cli/Cargo.toml (1)

33-48: 📐 Maintainability & Code Quality

Confirm the dependency graph and required Rust checks were refreshed.

These new crypto/base64 dependencies should be reflected in the lockfile, and this Rust PR needs the required validation pass: cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings, and just test-rust.

As per coding guidelines, “Any Rust change must run just test-rust”, “Any Rust change must run cargo fmt --all”, and “Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings”.

Source: Coding guidelines

crates/cli/src/plugins.rs (1)

26-26: LGTM!

crates/cli/src/plugins/policy.rs (1)

1-312: LGTM!

crates/cli/src/config.rs (1)

14-19: LGTM!

Also applies to: 591-595, 607-607, 734-738, 753-779, 1046-1058, 1075-1131, 1134-1222, 1244-1255

crates/cli/tests/cli_tests.rs (1)

12-16: LGTM!

Also applies to: 41-144, 211-240, 280-337, 339-743, 746-868, 1451-1488, 1491-1541

crates/cli/tests/coverage/launcher_tests.rs (1)

578-578: 📐 Maintainability & Code Quality

Confirm the required Rust validation commands ran.

Please confirm this Rust change set was validated with cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings, and just test-rust.

As per coding guidelines, “Any Rust change must run just test-rust”, “Any Rust change must run cargo fmt --all”, and “Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings”.

Source: Coding guidelines

crates/cli/src/error.rs (1)

21-82: LGTM!

crates/cli/src/plugins/lifecycle/state.rs (1)

10-24: LGTM!

Comment thread crates/cli/src/plugins/lifecycle.rs Outdated
Comment thread crates/cli/src/plugins/lifecycle.rs Outdated
Comment thread crates/cli/src/plugins/lifecycle/trust.rs Outdated
Comment thread crates/cli/src/plugins/lifecycle/trust.rs
Comment thread crates/cli/tests/coverage/config_tests.rs
Comment thread crates/cli/tests/coverage/plugins_lifecycle_tests.rs Outdated
Comment thread crates/core/src/plugin/dynamic.rs
…mplement-host-policy-capabilities-and-attestation
Signed-off-by: Alex Fournier <afournier@nvidia.com>
@afourniernv

Copy link
Copy Markdown
Contributor Author

/ok to test b94fcd5

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
crates/cli/src/plugins/lifecycle.rs (1)

366-392: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not validate trust before disabling a plugin.

plugins disable now loads the manifest and evaluates policy/trust before reaching the disable branch. If the manifest path is missing or unreadable, operators can be blocked from disabling a broken plugin—the safe remediation path. Gate only the enable path.

Proposed fix
-    let manifest_ref = manifest_ref_from_record(&entry.record)?;
-    let (manifest, manifest_ref) = load_manifest_for_action(command, &manifest_ref)?;
-    let policy = evaluate_dynamic_plugin_host_policy(&resolved.dynamic_plugin_policy, &manifest);
-    let trust = evaluate_dynamic_plugin_trust(&manifest, &manifest_ref, &policy);
-    update_registry_validation_status(&mut scopes[entry.scope_index], &plugin_id, &policy, &trust)?;
-    if enabled && !policy.policy_satisfied {
-        scopes[entry.scope_index].save()?;
-        return Err(plugin_refused_with_code(
-            command,
-            Some(plugin_id.clone()),
-            "policy_blocked",
-            policy
-                .failure()
-                .map(|failure| failure.display(&plugin_id).to_string())
-                .unwrap_or_else(|| {
-                    format!("dynamic plugin '{}' is blocked by host policy", plugin_id)
-                }),
-        ));
-    }
-    if enabled && let Some(failure) = trust.failure() {
-        scopes[entry.scope_index].save()?;
-        return Err(plugin_refused_with_code(
-            command,
-            Some(plugin_id.clone()),
-            trust_refusal_code(&trust),
-            failure.display(&plugin_id).to_string(),
-        ));
+    if enabled {
+        let manifest_ref = manifest_ref_from_record(&entry.record)?;
+        let (manifest, manifest_ref) = load_manifest_for_action(command, &manifest_ref)?;
+        let policy = evaluate_dynamic_plugin_host_policy(&resolved.dynamic_plugin_policy, &manifest);
+        let trust = evaluate_dynamic_plugin_trust(&manifest, &manifest_ref, &policy);
+        update_registry_validation_status(&mut scopes[entry.scope_index], &plugin_id, &policy, &trust)?;
+        if !policy.policy_satisfied {
+            scopes[entry.scope_index].save()?;
+            return Err(plugin_refused_with_code(
+                command,
+                Some(plugin_id.clone()),
+                "policy_blocked",
+                policy
+                    .failure()
+                    .map(|failure| failure.display(&plugin_id).to_string())
+                    .unwrap_or_else(|| {
+                        format!("dynamic plugin '{}' is blocked by host policy", plugin_id)
+                    }),
+            ));
+        }
+        if let Some(failure) = trust.failure() {
+            scopes[entry.scope_index].save()?;
+            return Err(plugin_refused_with_code(
+                command,
+                Some(plugin_id.clone()),
+                trust_refusal_code(&trust),
+                failure.display(&plugin_id).to_string(),
+            ));
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cli/src/plugins/lifecycle.rs` around lines 366 - 392, The disable flow
in lifecycle plugin handling is validating manifest/policy/trust too early,
which can block remediation when the plugin is broken. In the logic around
manifest_ref_from_record, load_manifest_for_action,
evaluate_dynamic_plugin_host_policy, and evaluate_dynamic_plugin_trust, move
those checks so they only run when enabling a plugin, and let the disable branch
proceed without requiring manifest access. Keep
update_registry_validation_status and the refusal paths for enabled operations,
but ensure plugins disable can always complete even if the manifest is missing
or unreadable.
crates/cli/tests/coverage/plugins_lifecycle_tests.rs (1)

393-422: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Update the stale host-config label expectation.

host_config_label(...) now reports absent host config as "missing", but this test still expects "absent" in both the list and inspect assertions.

Proposed fix
-    assert!(list.contains("absent"));
+    assert!(list.contains("missing"));
@@
-    assert_eq!(inspect_value["host_config_status"].as_str(), Some("absent"));
+    assert_eq!(inspect_value["host_config_status"].as_str(), Some("missing"));

As per path instructions, “Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cli/tests/coverage/plugins_lifecycle_tests.rs` around lines 393 - 422,
Update the stale host-config label expectations in the plugin lifecycle coverage
test to match the current `host_config_label(...)` behavior, which now returns
`"missing"` for absent host config instead of `"absent"`. Adjust the assertions
in the `plugins_lifecycle_tests` flow around `PluginInspectView` and the list
output checks so they verify `"missing"` for `host_config_status` and any
related rendered text, keeping the test aligned with the updated API surface.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/cli/src/plugins/lifecycle.rs`:
- Around line 366-392: The disable flow in lifecycle plugin handling is
validating manifest/policy/trust too early, which can block remediation when the
plugin is broken. In the logic around manifest_ref_from_record,
load_manifest_for_action, evaluate_dynamic_plugin_host_policy, and
evaluate_dynamic_plugin_trust, move those checks so they only run when enabling
a plugin, and let the disable branch proceed without requiring manifest access.
Keep update_registry_validation_status and the refusal paths for enabled
operations, but ensure plugins disable can always complete even if the manifest
is missing or unreadable.

In `@crates/cli/tests/coverage/plugins_lifecycle_tests.rs`:
- Around line 393-422: Update the stale host-config label expectations in the
plugin lifecycle coverage test to match the current `host_config_label(...)`
behavior, which now returns `"missing"` for absent host config instead of
`"absent"`. Adjust the assertions in the `plugins_lifecycle_tests` flow around
`PluginInspectView` and the list output checks so they verify `"missing"` for
`host_config_status` and any related rendered text, keeping the test aligned
with the updated API surface.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: b0e3b7c0-5d01-4e20-b2d4-4329b3febe57

📥 Commits

Reviewing files that changed from the base of the PR and between 38efcab and b94fcd5.

📒 Files selected for processing (7)
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/core/tests/unit/plugin_dynamic_tests.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (15)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Use snake_case naming convention for Rust identifiers (e.g., nemo_relay_tool_call)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

**/*.rs: Run cargo fmt --all to format all Rust code
Run cargo clippy --workspace --all-targets -- -D warnings to enforce all clippy lints as errors

**/*.rs: Run cargo fmt --all when Rust files changed as part of WebAssembly work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files changed as part of WebAssembly work

**/*.rs: If any Rust code changed, always run just test-rust
If any Rust code changed, also run cargo fmt --all
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings
Run Rust formatting with cargo fmt --all
Run Rust linting with cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Use cargo fmt for Rust code formatting
Run cargo clippy -- -D warnings to lint Rust code and treat all warnings as errors
Use Rust snake_case naming convention for Rust identifiers
Include SPDX license header in all Rust source files using double-slash comment syntax
Validate Rust code with uv run pre-commit run --all-files to enforce cargo fmt formatting check, cargo clippy lints, and cargo deny aud...

Files:

  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
{crates/adaptive/**/*.rs,**/*test*.{rs,py,go,ts,js},**/*adaptive*test*.{rs,py,go,ts,js},docs/plugins/adaptive/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Maintain documented and tested validation and report behavior for adaptive surfaces

Files:

  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/tests/coverage/config_tests.rs
**/{Cargo.toml,**/*.rs}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Maintain consistency between Rust package names in Cargo.toml and their actual usage across the codebase

Files:

  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
**/*.{h,hpp,c,cpp,rs}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Ensure FFI header and library naming follows consistent conventions across platform-specific builds

Files:

  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/core/tests/unit/plugin_dynamic_tests.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update Rust crate names and module prefixes during coordinated rename operations

Files:

  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

crates/core/**/*.rs: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths. Keep errors explicit and binding-appropriate at the wrapper layer.

Files:

  • crates/core/tests/unit/plugin_dynamic_tests.rs
crates/{core,adaptive}/**

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full matrix across Rust, Python, Go, Node.js, and WebAssembly

Files:

  • crates/core/tests/unit/plugin_dynamic_tests.rs
**/*.{rs,py,js,ts,tsx,jsx,go,sh,toml,yaml,yml,md}

📄 CodeRabbit inference engine (AGENTS.md)

Keep SPDX headers on source, docs, scripts, and configuration files. The project is Apache-2.0.

Files:

  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
**/*.{rs,py,go,js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions: Rust and Python use snake_case, C FFI exports prefixed nemo_relay_, Go uses PascalCase for public APIs, Node.js uses camelCase.

Files:

  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
crates/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

crates/**/*.rs: Keep async behavior on the existing tokio-based model. Bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.
Use Json = serde_json::Value in Rust-facing runtime APIs for JSON payload handling.

Files:

  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
**

⚙️ CodeRabbit configuration file

**:

AGENTS.md

This file provides guidance to agents, including Claude Code and OpenAI Codex, when working in this repository.

Project Overview

NeMo Relay is a multi-language agent runtime framework for execution scopes, lifecycle events, middleware, plugins, and observability around tool and LLM calls. The core runtime is Rust. Primary supported bindings are Rust, Python, and Node.js. Go, WebAssembly, and the raw C FFI are experimental and source-first.

The shared runtime model is:

  1. Scope stacks decide where work belongs and which scope-local behavior is visible.
  2. Middleware registries decide what guardrails and intercepts run around managed calls.
  3. Plugins install reusable runtime behavior from configuration.
  4. Events record runtime behavior in ATOF form.
  5. Subscribers and exporters consume events in-process or export them to ATIF, OpenTelemetry, OpenInference, or other backends.

Repository Structure

The repository layout separates the Rust runtime, language bindings, documentation,
integration patches, and agent-facing skills.

crates/
  core/       # Rust core runtime crate, published as nemo-relay
  adaptive/   # Adaptive runtime primitives and plugin components
  python/     # PyO3 native extension for the Python package
  ffi/        # Raw C ABI layer used by downstream bindings such as Go
  node/       # NAPI Node.js binding and JavaScript/TypeScript entry points
  wasm/       # wasm-bindgen WebAssembly binding and JS wrappers
python/
  nemo_relay/  # Python wrapper package: scopes, tools, LLM, middleware, typed helpers, plugins, adaptive helpers
  tests/      # Python tests
go/
  nemo_relay/  # Experimental Go CGo binding and tests
fern/         # Fern documentation site
scripts/      # Stable wrappers and helper scripts; build/test/docs entry points live in justfile
third_party/  # P...

Files:

  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/policy.rs
  • crates/cli/tests/coverage/config_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/trust.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/core/tests/unit/plugin_dynamic_tests.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/core/tests/unit/plugin_dynamic_tests.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/tests/coverage/config_tests.rs
**/*config*.{rs,ts,py,go,js,json,yaml,yml}

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Ensure dynamic config shape still matches the documented canonical model

Files:

  • crates/cli/tests/coverage/config_tests.rs
🔇 Additional comments (1)
crates/core/tests/unit/plugin_dynamic_tests.rs (1)

579-615: 📐 Maintainability & Code Quality

Confirm the required Rust/core validation ran.

Please confirm this PR ran cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings, just test-rust, and the full affected binding matrix for the crates/core path change.

As per coding guidelines, “Any Rust change must run just test-rust”, “Any Rust change must run cargo fmt --all”, and “Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings.” As per path instructions, “Changes to crates/core or crates/adaptive must run the full language matrix.”

Sources: Coding guidelines, Path instructions

Signed-off-by: Alex Fournier <afournier@nvidia.com>
@afourniernv

Copy link
Copy Markdown
Contributor Author

/ok to test e3a39d7

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

@willkill07 willkill07 added this to the 0.5 milestone Jun 25, 2026
Signed-off-by: Alex Fournier <afournier@nvidia.com>
@afourniernv

Copy link
Copy Markdown
Contributor Author

/ok to test ecbba98

@github-actions

Copy link
Copy Markdown

License Diff

Compared against origin/main.

Lockfile license changes

Lockfile License Changes

Rust

Added

  • None

Removed

  • None

Updated/Changed

  • None

Node

Added

  • None

Removed

  • None

Updated/Changed

  • None

Python

Added

  • None

Removed

  • None

Updated/Changed

  • None
Status output
[license-diff] selected languages: rust, node, python
[license-diff] generating current inventory
[license-diff] current: generating Rust inventory
[license-diff] current: Rust inventory complete (344 packages)
[license-diff] current: generating Node inventory
[license-diff] current: Node inventory complete (389 packages)
[license-diff] current: generating Python inventory
[license-diff] current: Python inventory complete (104 packages)
[license-diff] current inventory complete
[license-diff] checking out base ref origin/main into a temporary worktree
[license-diff] base: generating Rust inventory
[license-diff] base: Rust inventory complete (344 packages)
[license-diff] base: generating Node inventory
[license-diff] base: Node inventory complete (389 packages)
[license-diff] base: generating Python inventory
[license-diff] base: Python inventory complete (104 packages)
[license-diff] base inventory complete
[license-diff] removing temporary base worktree
[license-diff] comparing inventories
[license-diff] rendering Markdown output
[license-diff] done

@afourniernv

Copy link
Copy Markdown
Contributor Author

/merge

@rapids-bot
rapids-bot Bot merged commit 8c24c05 into NVIDIA:main Jun 25, 2026
72 checks passed
rapids-bot Bot pushed a commit that referenced this pull request Jun 25, 2026
#### Overview

Add the shared serializable Relay DTO crate that the rest of the dynamic plugin stack builds on.

Stack dependency: this is the root PR in the dynamic plugin stack. It has no parent stacked PR. Later PRs in the stack cannot be merged until this PR is merged.

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- Add `nemo-relay-types` as the shared DTO source of truth.
- Re-export moved JSON, event, scope, tool, LLM, codec, and plugin diagnostic types from existing `nemo_relay` paths.
- Keep runtime behavior, registries, dynamic loading, and pricing helpers outside the shared DTO crate.

Validation run across the completed stack:

- `cargo test -p nemo-relay-types`
- `cargo test -p nemo-relay-plugin`
- `cargo test -p nemo-relay-worker-proto`
- `cargo test -p nemo-relay-worker`
- `cargo check -p nemo-relay-cli`

#### Where should the reviewer start?

Start with `crates/types/src/lib.rs` and the core re-export changes in `crates/core/src/api/event.rs`.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to: #302
- Relates to: #230



## Summary by CodeRabbit

* **New Features**
  * Introduced shared, workspace-wide DTOs for ATOF events, scopes, tools, LLM requests, and normalized LLM responses.
  * Added normalized event helpers for accessing annotated LLM request/response payloads.
  * Added structured category/profile support and richer normalized usage/cost modeling.

* **Bug Fixes**
  * Preserved existing public type paths and stable attribute/coding behavior while standardizing types.
  * Improved serialization round-tripping to keep unknown fields and nested details.

* **Tests**
  * Added integration and serialization compatibility tests covering shared re-exports and event/profile round-trips.

Authors:
  - Will Killian (https://github.com/willkill07)
  - Alex Fournier (https://github.com/afourniernv)

Approvers:
  - Alex Fournier (https://github.com/afourniernv)
  - Bryan Bednarski (https://github.com/bbednarski9)

URL: #305
rapids-bot Bot pushed a commit that referenced this pull request Jun 26, 2026
#### Overview

Add the Rust native plugin authoring SDK on top of the shared DTO crate.

Stack dependency: this PR cannot be merged until parent stacked PR #305 is merged: #305

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- Add `nemo-relay-plugin` with the stable native C ABI v1.
- Add the high-level typed Rust authoring layer and callback trampoline tests.
- Add the standalone native Rust plugin example beside the SDK.

Validation run across the completed stack:

- `cargo test -p nemo-relay-types`
- `cargo test -p nemo-relay-plugin`
- `cargo test -p nemo-relay-worker-proto`
- `cargo test -p nemo-relay-worker`
- `cargo check -p nemo-relay-cli`

#### Where should the reviewer start?

Start with `crates/plugin/src/lib.rs`, then check `crates/plugin/tests/typed_callbacks.rs`.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to: #302
- Relates to: #230



## Summary by CodeRabbit

* **New Features**
  * Added a Rust native plugin SDK with a stable ABI and typed callback interfaces for plugin lifecycle, tool/LLM guardrails, and intercepts (including LLM streaming).
  * Added a dynamic Rust native plugin example with configurable JSON validation, request/execution/stream interception, and optional isolated-scope event emission.

* **Tests**
  * Added a comprehensive typed-callback test suite covering ABI checks, JSON/error handling, registration cleanup, stream behavior, and panic safety.

* **Documentation**
  * Added example README plus plugin build/config manifests.

* **Chores**
  * Updated the workspace to include the plugin crate and extended local clean rules for example artifacts.

Authors:
  - Will Killian (https://github.com/willkill07)

Approvers:
  - Alex Fournier (https://github.com/afourniernv)

URL: #306
rapids-bot Bot pushed a commit that referenced this pull request Jun 26, 2026
#### Overview

Add the host-side `rust_dynamic` native plugin loader and gateway activation path.

Stack dependency: this PR cannot be merged until parent stacked PR #306 is merged: #306

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- Load trusted in-process native dynamic plugins through the stable ABI.
- Register native plugin kinds into the existing plugin registry and clear them before library unload.
- Add native plugin gateway activation, fixture coverage, and integration tests.

Validation run across the completed stack:

- `cargo test -p nemo-relay-types`
- `cargo test -p nemo-relay-plugin`
- `cargo test -p nemo-relay-worker-proto`
- `cargo test -p nemo-relay-worker`
- `cargo check -p nemo-relay-cli`

#### Where should the reviewer start?

Start with `crates/core/src/plugin/dynamic/native.rs`, then review the activation wiring in `crates/cli/src/server.rs`.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to: #302
- Relates to: #230



## Summary by CodeRabbit

* **New Features**
  * Transparent runs now automatically activate dynamic plugins (including daemon-mode startup).
  * Added end-to-end support for native dynamic plugins from manifests, including loading validation and runtime activation.
* **Bug Fixes**
  * Improved dynamic-plugin lifecycle handling so activation is correctly initialized/cleared, including proper failure/rollback behavior.
  * Dry-run no longer hydrates dynamic plugin lifecycle state.
* **Tests**
  * Expanded coverage for active dynamic plugin resolution and native plugin integration, including negative/error scenarios and lifecycle transitions.
* **Refactor**
  * Gateway startup was updated to use a dynamic-plugin-aware serving path.

Authors:
  - Will Killian (https://github.com/willkill07)

Approvers:
  - Alex Fournier (https://github.com/afourniernv)

URL: #307
rapids-bot Bot pushed a commit that referenced this pull request Jun 26, 2026
#### Overview

Add the out-of-process worker protocol and Rust worker SDK layer.

Stack dependency: this PR cannot be merged until parent stacked PR #307 is merged: #307

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- Add `nemo-relay-worker-proto` with `nemo.relay.worker.v1` protobuf services and messages.
- Add `nemo-relay-worker` as the Rust SDK for gRPC worker plugins.
- Add protocol stability tests and worker SDK helpers for registration, continuations, streaming, and host runtime calls.

Validation run across the completed stack:

- `cargo test -p nemo-relay-types`
- `cargo test -p nemo-relay-plugin`
- `cargo test -p nemo-relay-worker-proto`
- `cargo test -p nemo-relay-worker`
- `cargo check -p nemo-relay-cli`

#### Where should the reviewer start?

Start with `crates/worker-proto/proto/nemo/relay/worker/v1/plugin_worker.proto`, then review `crates/worker/src/lib.rs`.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to: #302
- Relates to: #230



## Summary by CodeRabbit

* **New Features**
  * Added out-of-process worker plugins with a new gRPC-based SDK/protocol.
  * Expanded supported worker runtimes to include Rust and generic command-based workers.
  * Introduced JSON envelope helpers and added scope + streaming callback support for worker execution flows.
* **Bug Fixes**
  * Updated plugin compatibility validation to require the `grpc-v1` worker protocol and improved rejection messaging for unsupported values.
* **Tests**
  * Expanded unit and end-to-end worker SDK coverage for new runtimes, protocol validation, and streaming behavior.
* **Chores**
  * Added new workspace crates and updated licensing, CI triggers, and release/publishing configuration.

Authors:
  - Will Killian (https://github.com/willkill07)

Approvers:
  - Alex Fournier (https://github.com/afourniernv)
  - https://github.com/Salonijain27

URL: #308
rapids-bot Bot pushed a commit that referenced this pull request Jun 27, 2026
#### Overview

Add the host activation and proxy layer for `grpc-v1` worker dynamic plugins.

Stack dependency: this PR cannot be merged until parent stacked PR #308 is merged: #308

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- Add the core worker plugin activation/proxy implementation behind `worker-grpc`.
- Start local workers, perform handshake/validation/registration, and proxy every supported registration surface.
- Add worker fixture and integration tests for callbacks, continuations, streaming, cancellation, marks, scopes, and teardown.

Validation run across the completed stack:

- `cargo test -p nemo-relay-types`
- `cargo test -p nemo-relay-plugin`
- `cargo test -p nemo-relay-worker-proto`
- `cargo test -p nemo-relay-worker`
- `cargo check -p nemo-relay-cli`

#### Where should the reviewer start?

Start with `crates/core/src/plugin/dynamic/worker.rs`, then review `crates/core/tests/integration/worker_plugin_tests.rs`.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to: #302
- Relates to: #230



## Summary by CodeRabbit

* **New Features**
  * Added dynamic worker plugin support over gRPC (**worker-grpc**) and enabled it in both core and the CLI.
  * Worker plugins can now be dynamically loaded and participate in tool, LLM, and streaming request handling.
  * Added a worker runtime helper to run code within a specific scope stack.
* **Bug Fixes**
  * Improved worker plugin activation/teardown, including clearer validation and compatibility handling.
  * Enhanced tool execution marking to reflect isolated and restored scope-stack phases.
* **Tests**
  * Added comprehensive unit and integration coverage, including a dedicated worker plugin fixture and streaming/error scenarios.

Authors:
  - Will Killian (https://github.com/willkill07)

Approvers:
  - Alex Fournier (https://github.com/afourniernv)

URL: #309
rapids-bot Bot pushed a commit that referenced this pull request Jun 29, 2026
#### Overview

Relay runs a native Rust plugin by loading its compiled `.so`, `.dylib`, or `.dll` file into the Relay process. This PR updates the official example so Relay verifies that exact compiled file before it is accepted and loaded.

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

The example previously declared only `load.library`. That told Relay which file to execute, but it did not identify an artifact for the default integrity policy to verify or provide the artifact's expected digest. As a result, the documented `plugins add` flow was rejected before the plugin could be enabled.

This change adds the missing pieces:

- `source.artifact` identifies the compiled plugin file Relay should verify.
- `integrity.sha256` records the expected fingerprint of that file.
- `load.library` points to the same file, ensuring the verified artifact is the artifact Relay executes.
- The README explains how to build the plugin, calculate the platform-specific digest, and create a local manifest.
- A CLI lifecycle test materializes the tracked example manifest with an artifact and matching digest, then verifies that `plugins add` succeeds under the default trust policy.
- A companion test tampers the generated artifact, verifies `plugins add` returns `integrity_failed`, and confirms the plugin is not registered.

The digest must be generated after compilation because the library bytes vary by platform and build. This is the expected packaging flow for native dynamic plugins whether they are maintained in this repository or built and distributed by another contributing project.

This PR does not weaken the trust policy, change the native ABI, or change the example plugin's runtime behavior.

#### Where should the reviewer start?

Start with `examples/rust-native-plugin/relay-plugin.toml`, then review the lifecycle regression in `crates/cli/tests/coverage/plugins_lifecycle_tests.rs`.

#### Testing

- `cargo test -p nemo-relay-cli`
- `cargo fmt --all -- --check`
- `cargo clippy -p nemo-relay-cli --all-targets -- -D warnings`
- `pre-commit run --from-ref github/main --to-ref HEAD`

Manual native-example E2E on macOS arm64:

1. Built `examples/rust-native-plugin` into the real Mach-O `.dylib`.
2. Copied the example manifest, substituted the platform filename, and inserted the library's computed SHA-256 digest.
3. Ran `plugins add`, `plugins validate`, and `plugins enable` in an isolated user configuration. Validation reported `policy_state: valid` and `integrity_state: valid`.
4. Started the gateway with the enabled plugin and confirmed `/healthz` returned `{"status":"ok"}`.
5. Routed an OpenAI-compatible request through the gateway to a local mock upstream. The upstream received `native_llm_request_intercept: true` and `native_llm_execution_request: true`, confirming the compiled example loaded and registered live middleware.

The manual run also caught and corrected the example's stale `nemo-relay gateway` command; daemon startup now uses the supported `nemo-relay --bind 127.0.0.1:4040` form.

The repository-wide `pre-commit run --all-files` also completed all checks except the existing `attributions-rust` drift, which rewrites the unrelated `md-5` license entry. That generated change is not included here.

#### Breaking changes

None.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to #302



## Summary by CodeRabbit

* **New Features**
  * Native plugin setup now supports local manifests that require an explicit SHA-256 integrity check for the referenced artifact.

* **Documentation**
  * Updated the Rust native plugin README with clearer local-manifest steps, platform-specific library path substitution, and how to supply the `sha256:` digest.
  * Updated the example commands to bind the gateway to `127.0.0.1:4040` and to use the local manifest (`relay-plugin.local.toml`).

* **Tests**
  * Added end-to-end coverage for accepting the default-trust native plugin example.
  * Added coverage to verify tampered artifacts are rejected with an integrity failure and no dynamic plugins are loaded.

Authors:
  - Bryan Bednarski (https://github.com/bbednarski9)

Approvers:
  - Will Killian (https://github.com/willkill07)

URL: #326
rapids-bot Bot pushed a commit that referenced this pull request Jun 30, 2026
#### Overview

Add focused maintainer Skills guidance for dynamic plugin development and route related validation, packaging, CI, Python, Rust, and documentation work to the right repository workflows.

Dependency: PR #310 should merge first because several Skills updates describe its Python worker SDK paths and commands: #310

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- Add `.agents/skills/maintain-dynamic-plugins/SKILL.md`.
- Update the existing docs, CI, packaging, Python, Rust, and change-validation Skills with dynamic plugin guidance.
- Align Rust validation with the actual integration-test targets and route focused Python SDK testing through PR #310's `just test-python-plugin` recipe.
- Update skill discovery descriptions, distinguish top-level `doctor` from `plugins` subcommands, keep unmerged detailed-doc guidance conditional, and correct stale docs/workflow references.
- Keep the PR limited to seven files under `.agents/skills/`; it contains no runtime, SDK, workflow, packaging configuration, or docs-site content changes.

Validation:

- `cargo test -p nemo-relay --features worker-grpc --test native_plugin_integration --test worker_plugin_integration --no-run`
- `uv run pre-commit run --files <seven changed SKILL.md files>`
- `uv run pre-commit run --all-files`
- Verified the Python plugin recipe and all updated paths against PR #310's head.

#### Where should the reviewer start?

Start with `.agents/skills/maintain-dynamic-plugins/SKILL.md`, then review the routing additions in `.agents/skills/validate-change/SKILL.md`.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to: #302
- Relates to: #230




## Summary by CodeRabbit

* **Documentation**
  * Added new guidance for maintaining dynamic plugins, including clearer documentation structure, validation steps, and references.
  * Updated existing guidance to cover Python worker plugin packaging, testing, CI checks, and release expectations.
  * Refined docs rules so related native, worker, and protocol examples are easier to find and follow.

Authors:
  - Will Killian (https://github.com/willkill07)

Approvers:
  - Maryam Najafian (https://github.com/mnajafian-nv)

URL: #312
rapids-bot Bot pushed a commit that referenced this pull request Jun 30, 2026
#### Overview

Add the pure-Python `nemo-relay-plugin` worker SDK package and its packaging/test support.

Stack dependency: this PR cannot be merged until parent stacked PR #309 is merged: #309

- [x] I confirm this contribution is my own work, or I have the right to submit it under this project's license.
- [x] I searched existing issues and open pull requests, and this does not duplicate existing work.

#### Details

- Add `python/plugin` with high-level worker plugin APIs, generated gRPC stubs, runtime helpers, continuations, and streaming helpers.
- Add Python SDK tests and a Python gRPC worker example.
- Update Python test, coverage, packaging, CI wheel upload, attribution, and pre-commit configuration for the new package.

Validation run across the completed stack:

- `cargo test -p nemo-relay-types`
- `cargo test -p nemo-relay-plugin`
- `cargo test -p nemo-relay-worker-proto`
- `cargo test -p nemo-relay-worker`
- `cargo check -p nemo-relay-cli`

#### Where should the reviewer start?

Start with `python/plugin/src/nemo_relay_plugin/_api.py`, then review `python/tests/plugin/test_worker_sdk.py`.

#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

- Relates to: #302
- Relates to: #230



## Summary by CodeRabbit

* **New Features**
  * Added a Python plugin SDK for gRPC v1 dynamic worker plugins, plus a working Python gRPC worker example.
  * CI can now build and publish an additional Linux-only Python plugin SDK wheel artifact.
* **Documentation**
  * Added authoring and usage guides for the SDK and the example plugin.
* **Tests**
  * Expanded plugin end-to-end, example integration, public API docstring coverage, and packaging/build (sdist rebuild) tests.
* **Bug Fixes**
  * Improved platform-aware CI/test skipping and coverage handling for generated protobuf sources.

Authors:
  - Will Killian (https://github.com/willkill07)

Approvers:
  - https://github.com/Salonijain27
  - Bryan Bednarski (https://github.com/bbednarski9)

URL: #310
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature a new feature lang:rust PR changes/introduces Rust code size:XL PR is extra large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants