feat: implement dynamic plugin host policy and attestation gates - #302
Conversation
Signed-off-by: Alex Fournier <afournier@nvidia.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Enterprise Run ID: 📒 Files selected for processing (1)
📜 Recent review details🧰 Additional context used📓 Path-based instructions (10)**/*.rs📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)
Files:
{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)
Files:
**/{Cargo.toml,**/*.rs}📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)
Files:
**/*.{h,hpp,c,cpp,rs}📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)
Files:
**/*.{rs,toml}📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)
Files:
**/*.{rs,py,js,ts,tsx,jsx,go,sh,toml,yaml,yml,md}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{rs,py,go,js,ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
crates/**/*.rs📄 CodeRabbit inference engine (AGENTS.md)
Files:
**⚙️ CodeRabbit configuration file
Files:
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}⚙️ CodeRabbit configuration file
Files:
🔇 Additional comments (2)
WalkthroughCLI config now parses ChangesDynamic plugin policy and trust
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
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>
|
/ok to test 38efcab |
1 similar comment
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (17)
crates/cli/Cargo.tomlcrates/cli/src/config.rscrates/cli/src/error.rscrates/cli/src/plugins.rscrates/cli/src/plugins/lifecycle.rscrates/cli/src/plugins/lifecycle/responses.rscrates/cli/src/plugins/lifecycle/state.rscrates/cli/src/plugins/lifecycle/trust.rscrates/cli/src/plugins/policy.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/config_tests.rscrates/cli/tests/coverage/launcher_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/manifest.rscrates/core/src/plugin/dynamic/registry.rscrates/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_casenaming convention for Rust identifiers (e.g.,nemo_relay_tool_call)
**/*.rs: Any Rust change must runjust test-rust
Any Rust change must runcargo fmt --all
Any Rust change must runcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Runcargo fmt --allfor all FFI work since it is Rust work
Runjust test-rustto validate FFI changes
Runcargo clippy --workspace --all-targets -- -D warningsto enforce strict linting on FFI workWhen Rust files changed as part of Go work, also run
cargo fmt --all,just test-rust, andcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Runcargo fmt --allwhen Rust files are changed as part of Node work
Runcargo clippy --workspace --all-targets -- -D warningswhen Rust files are changed as part of Node work
Runjust test-rustwhen Rust files are changed as part of Node work
**/*.rs: Runcargo fmt --allto format all Rust code
Runcargo clippy --workspace --all-targets -- -D warningsto enforce all clippy lints as errors
**/*.rs: Runcargo fmt --allwhen Rust files changed as part of WebAssembly work
Runcargo clippy --workspace --all-targets -- -D warningswhen Rust files changed as part of WebAssembly work
**/*.rs: If any Rust code changed, always runjust test-rust
If any Rust code changed, also runcargo fmt --all
If any Rust code changed, also runcargo clippy --workspace --all-targets -- -D warnings
Run Rust formatting withcargo fmt --all
Run Rust linting withcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Usecargo fmtfor Rust code formatting
Runcargo clippy -- -D warningsto 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 withuv run pre-commit run --all-filesto enforce cargo fmt formatting check, cargo clippy lints, and cargo deny aud...
Files:
crates/cli/src/plugins.rscrates/cli/tests/coverage/launcher_tests.rscrates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/manifest.rscrates/cli/src/plugins/lifecycle/state.rscrates/cli/src/error.rscrates/cli/src/plugins/policy.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/config_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/core/src/plugin/dynamic/registry.rscrates/core/tests/unit/plugin_dynamic_tests.rscrates/cli/src/config.rscrates/cli/src/plugins/lifecycle.rscrates/cli/src/plugins/lifecycle/trust.rscrates/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.tomland their actual usage across the codebase
Files:
crates/cli/src/plugins.rscrates/cli/Cargo.tomlcrates/cli/tests/coverage/launcher_tests.rscrates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/manifest.rscrates/cli/src/plugins/lifecycle/state.rscrates/cli/src/error.rscrates/cli/src/plugins/policy.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/config_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/core/src/plugin/dynamic/registry.rscrates/core/tests/unit/plugin_dynamic_tests.rscrates/cli/src/config.rscrates/cli/src/plugins/lifecycle.rscrates/cli/src/plugins/lifecycle/trust.rscrates/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.rscrates/cli/tests/coverage/launcher_tests.rscrates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/manifest.rscrates/cli/src/plugins/lifecycle/state.rscrates/cli/src/error.rscrates/cli/src/plugins/policy.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/config_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/core/src/plugin/dynamic/registry.rscrates/core/tests/unit/plugin_dynamic_tests.rscrates/cli/src/config.rscrates/cli/src/plugins/lifecycle.rscrates/cli/src/plugins/lifecycle/trust.rscrates/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.rscrates/cli/Cargo.tomlcrates/cli/tests/coverage/launcher_tests.rscrates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/manifest.rscrates/cli/src/plugins/lifecycle/state.rscrates/cli/src/error.rscrates/cli/src/plugins/policy.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/config_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/core/src/plugin/dynamic/registry.rscrates/core/tests/unit/plugin_dynamic_tests.rscrates/cli/src/config.rscrates/cli/src/plugins/lifecycle.rscrates/cli/src/plugins/lifecycle/trust.rscrates/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.rscrates/cli/Cargo.tomlcrates/cli/tests/coverage/launcher_tests.rscrates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/manifest.rscrates/cli/src/plugins/lifecycle/state.rscrates/cli/src/error.rscrates/cli/src/plugins/policy.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/config_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/core/src/plugin/dynamic/registry.rscrates/core/tests/unit/plugin_dynamic_tests.rscrates/cli/src/config.rscrates/cli/src/plugins/lifecycle.rscrates/cli/src/plugins/lifecycle/trust.rscrates/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 prefixednemo_relay_, Go usesPascalCasefor public APIs, Node.js usescamelCase.
Files:
crates/cli/src/plugins.rscrates/cli/tests/coverage/launcher_tests.rscrates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/manifest.rscrates/cli/src/plugins/lifecycle/state.rscrates/cli/src/error.rscrates/cli/src/plugins/policy.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/config_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/core/src/plugin/dynamic/registry.rscrates/core/tests/unit/plugin_dynamic_tests.rscrates/cli/src/config.rscrates/cli/src/plugins/lifecycle.rscrates/cli/src/plugins/lifecycle/trust.rscrates/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.
UseJson = serde_json::Valuein Rust-facing runtime APIs for JSON payload handling.
Files:
crates/cli/src/plugins.rscrates/cli/tests/coverage/launcher_tests.rscrates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/manifest.rscrates/cli/src/plugins/lifecycle/state.rscrates/cli/src/error.rscrates/cli/src/plugins/policy.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/config_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/core/src/plugin/dynamic/registry.rscrates/core/tests/unit/plugin_dynamic_tests.rscrates/cli/src/config.rscrates/cli/src/plugins/lifecycle.rscrates/cli/src/plugins/lifecycle/trust.rscrates/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:
- Scope stacks decide where work belongs and which scope-local behavior is visible.
- Middleware registries decide what guardrails and intercepts run around managed calls.
- Plugins install reusable runtime behavior from configuration.
- Events record runtime behavior in ATOF form.
- 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.rscrates/cli/Cargo.tomlcrates/cli/tests/coverage/launcher_tests.rscrates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/manifest.rscrates/cli/src/plugins/lifecycle/state.rscrates/cli/src/error.rscrates/cli/src/plugins/policy.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/config_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/core/src/plugin/dynamic/registry.rscrates/core/tests/unit/plugin_dynamic_tests.rscrates/cli/src/config.rscrates/cli/src/plugins/lifecycle.rscrates/cli/src/plugins/lifecycle/trust.rscrates/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 asrelease/<major>.<minor>.
**/Cargo.toml: MaintainCargo.toml[workspace.package].versionas the source of truth for the Rust workspace and Python build versioning
KeepCargo.toml[workspace.dependencies]self-references aligned with the workspace version when the workspace version changes
After updating workspace package entries, runcargo check --workspaceto refreshCargo.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.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/config_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/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.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/config_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/core/tests/unit/plugin_dynamic_tests.rs
{crates/core,crates/adaptive}/**/*
📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)
Changes to
crates/coreorcrates/adaptivemust run the full language matrix
Files:
crates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/manifest.rscrates/core/src/plugin/dynamic/registry.rscrates/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/coreor shared runtime semantics, also usevalidate-changefor broader validation
crates/core/**/*.rs: UseJson = serde_json::Valuein Rust-facing runtime APIs where the existing code expects JSON payloads.
UseResult<T>withFlowErrorin core runtime paths. Keep errors explicit and binding-appropriate at the wrapper layer.
Files:
crates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/manifest.rscrates/core/src/plugin/dynamic/registry.rscrates/core/tests/unit/plugin_dynamic_tests.rs
crates/{core,adaptive}/**
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
If
crates/coreorcrates/adaptivechanged, run the full matrix across Rust, Python, Go, Node.js, and WebAssembly
Files:
crates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/manifest.rscrates/core/src/plugin/dynamic/registry.rscrates/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.rscrates/core/src/plugin/dynamic/manifest.rscrates/core/src/plugin/dynamic/registry.rscrates/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.rscrates/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 QualityRun the required Rust checks
cargo fmt --all,cargo clippy --workspace --all-targets -- -D warnings, andjust test-ruststill need to pass for this Rust change.crates/cli/Cargo.toml (1)
33-48: 📐 Maintainability & Code QualityConfirm 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, andjust test-rust.As per coding guidelines, “Any Rust change must run
just test-rust”, “Any Rust change must runcargo fmt --all”, and “Any Rust change must runcargo 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 QualityConfirm 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, andjust test-rust.As per coding guidelines, “Any Rust change must run
just test-rust”, “Any Rust change must runcargo fmt --all”, and “Any Rust change must runcargo 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!
…mplement-host-policy-capabilities-and-attestation
Signed-off-by: Alex Fournier <afournier@nvidia.com>
|
/ok to test b94fcd5 |
There was a problem hiding this comment.
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 winDo not validate trust before disabling a plugin.
plugins disablenow 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 winUpdate 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
📒 Files selected for processing (7)
crates/cli/src/plugins/lifecycle.rscrates/cli/src/plugins/lifecycle/trust.rscrates/cli/src/plugins/policy.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/config_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/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_casenaming convention for Rust identifiers (e.g.,nemo_relay_tool_call)
**/*.rs: Any Rust change must runjust test-rust
Any Rust change must runcargo fmt --all
Any Rust change must runcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Runcargo fmt --allfor all FFI work since it is Rust work
Runjust test-rustto validate FFI changes
Runcargo clippy --workspace --all-targets -- -D warningsto enforce strict linting on FFI workWhen Rust files changed as part of Go work, also run
cargo fmt --all,just test-rust, andcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Runcargo fmt --allwhen Rust files are changed as part of Node work
Runcargo clippy --workspace --all-targets -- -D warningswhen Rust files are changed as part of Node work
Runjust test-rustwhen Rust files are changed as part of Node work
**/*.rs: Runcargo fmt --allto format all Rust code
Runcargo clippy --workspace --all-targets -- -D warningsto enforce all clippy lints as errors
**/*.rs: Runcargo fmt --allwhen Rust files changed as part of WebAssembly work
Runcargo clippy --workspace --all-targets -- -D warningswhen Rust files changed as part of WebAssembly work
**/*.rs: If any Rust code changed, always runjust test-rust
If any Rust code changed, also runcargo fmt --all
If any Rust code changed, also runcargo clippy --workspace --all-targets -- -D warnings
Run Rust formatting withcargo fmt --all
Run Rust linting withcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Usecargo fmtfor Rust code formatting
Runcargo clippy -- -D warningsto 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 withuv run pre-commit run --all-filesto enforce cargo fmt formatting check, cargo clippy lints, and cargo deny aud...
Files:
crates/core/tests/unit/plugin_dynamic_tests.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/cli/src/plugins/policy.rscrates/cli/tests/coverage/config_tests.rscrates/cli/src/plugins/lifecycle.rscrates/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.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/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.tomland their actual usage across the codebase
Files:
crates/core/tests/unit/plugin_dynamic_tests.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/cli/src/plugins/policy.rscrates/cli/tests/coverage/config_tests.rscrates/cli/src/plugins/lifecycle.rscrates/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.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/cli/src/plugins/policy.rscrates/cli/tests/coverage/config_tests.rscrates/cli/src/plugins/lifecycle.rscrates/cli/src/plugins/lifecycle/trust.rs
{crates/core,crates/adaptive}/**/*
📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)
Changes to
crates/coreorcrates/adaptivemust 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.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/cli/src/plugins/policy.rscrates/cli/tests/coverage/config_tests.rscrates/cli/src/plugins/lifecycle.rscrates/cli/src/plugins/lifecycle/trust.rs
crates/core/**/*.rs
📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)
If the change touched
crates/coreor shared runtime semantics, also usevalidate-changefor broader validation
crates/core/**/*.rs: UseJson = serde_json::Valuein Rust-facing runtime APIs where the existing code expects JSON payloads.
UseResult<T>withFlowErrorin 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/coreorcrates/adaptivechanged, 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.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/cli/src/plugins/policy.rscrates/cli/tests/coverage/config_tests.rscrates/cli/src/plugins/lifecycle.rscrates/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 prefixednemo_relay_, Go usesPascalCasefor public APIs, Node.js usescamelCase.
Files:
crates/core/tests/unit/plugin_dynamic_tests.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/cli/src/plugins/policy.rscrates/cli/tests/coverage/config_tests.rscrates/cli/src/plugins/lifecycle.rscrates/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.
UseJson = serde_json::Valuein Rust-facing runtime APIs for JSON payload handling.
Files:
crates/core/tests/unit/plugin_dynamic_tests.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/cli/src/plugins/policy.rscrates/cli/tests/coverage/config_tests.rscrates/cli/src/plugins/lifecycle.rscrates/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:
- Scope stacks decide where work belongs and which scope-local behavior is visible.
- Middleware registries decide what guardrails and intercepts run around managed calls.
- Plugins install reusable runtime behavior from configuration.
- Events record runtime behavior in ATOF form.
- 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.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/cli/src/plugins/policy.rscrates/cli/tests/coverage/config_tests.rscrates/cli/src/plugins/lifecycle.rscrates/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.rscrates/cli/tests/cli_tests.rscrates/cli/tests/coverage/plugins_lifecycle_tests.rscrates/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 QualityConfirm 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 thecrates/corepath change.As per coding guidelines, “Any Rust change must run
just test-rust”, “Any Rust change must runcargo fmt --all”, and “Any Rust change must runcargo clippy --workspace --all-targets -- -D warnings.” As per path instructions, “Changes tocrates/coreorcrates/adaptivemust run the full language matrix.”Sources: Coding guidelines, Path instructions
Signed-off-by: Alex Fournier <afournier@nvidia.com>
|
/ok to test e3a39d7 |
Signed-off-by: Alex Fournier <afournier@nvidia.com>
|
/ok to test ecbba98 |
License DiffCompared against Lockfile license changesLockfile License ChangesRustAdded
Removed
Updated/Changed
NodeAdded
Removed
Updated/Changed
PythonAdded
Removed
Updated/Changed
Status output |
|
/merge |
#### 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
#### 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
#### 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
#### 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
#### 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
#### 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
#### 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
#### 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
Overview
Implement RELAY-339 host policy, capability enforcement, startup classification, and attestation gating for dynamic plugins.
Details
Where should the reviewer start?
Start in
crates/cli/src/plugins/policy.rs, then review the trust and startup enforcement changes incrates/cli/src/plugins/lifecycle.rs, then the config/startup coverage incrates/cli/tests/coverage/config_tests.rs.Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)
Summary by CodeRabbit
New Features
Bug Fixes
policy_blocked) and more accurate “valid”/failure-state derivation across policy + trust.