Skip to content

feat(l7): add JSON-RPC and MCP policy enforcement#1865

Merged
krishicks merged 1 commit into
mainfrom
hicks/push-nvuozlywzuwu
Jun 26, 2026
Merged

feat(l7): add JSON-RPC and MCP policy enforcement#1865
krishicks merged 1 commit into
mainfrom
hicks/push-nvuozlywzuwu

Conversation

@krishicks

@krishicks krishicks commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add first-class L7 policy support for protocol: json-rpc and protocol: mcp, so sandbox egress can be constrained at the JSON-RPC method and MCP tool layer rather than just host/path. The L7 forward proxy now parses JSON-RPC request bodies (single and batch), enforces per-call decisions, fails closed on ambiguous or response-shaped frames, and relays MCP SSE responses without prematurely closing streams.

Related Issue

Closes #1793

Changes

  • Policy schema, proto, and OPA wiring for json-rpc and mcp protocols (openshell-policy, proto/sandbox.proto, openshell-supervisor-network/opa.rs, data/sandbox-policy.rego, provider profiles).
    • JSON-RPC endpoints match exact method names only (method globs rejected at policy validation) and do not support params matchers.
    • MCP endpoints additionally support method globs and tool/params.name matching to narrow tools/call, enforced identically on the YAML and proto load paths.
  • L7 forward-proxy parsing (l7/jsonrpc.rs, l7/http.rs, l7/mod.rs, l7/proxy.rs): parse JSON-RPC bodies and batches with bounded reads (json_rpc_max_body_bytes), fail closed on invalid/ambiguous/response-shaped frames, deny a batch if any element is denied, and redact params in decision logs.
  • SSE relay (l7/relay.rs, l7/rest.rs): relay unframed or sparse MCP SSE responses without prematurely closing streams.
  • MCP-only options via a grouped McpOptions proto message (strict_tool_names, allow_all_known_mcp_methods).
  • Docs: new policy protocols and current params-matcher limitations (docs/reference/policy-schema.mdx, docs/sandboxes/policies.mdx, architecture/sandbox.md).
  • Tests: MCP conformance suite through the Docker gateway runner (e2e/mcp-conformance*), JSON-RPC L7 e2e coverage (e2e/rust/tests/forward_proxy_jsonrpc_l7.rs), plus extensive OPA/policy unit
    tests.
  • Bug fix: proto_to_opa_data_json now carries the per-rule params matcher map for both allow and deny rules. Previously it was dropped on the proto load path, silently degrading an MCP tool-scoped allow rule into allow-any-tool in production (the YAML path enforced correctly). Covered by a new from_proto regression test.

Testing

  • mise run pre-commit passes
  • Unit tests added/updated
  • E2E tests added/updated (if applicable)

Additional targeted checks:

  • cargo test -p openshell-sandbox jsonrpc
  • mise run e2e:rust -- --test forward_proxy_jsonrpc_l7

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs updated (if applicable)

@copy-pr-bot

copy-pr-bot Bot commented Jun 10, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@github-actions

Copy link
Copy Markdown

@krishicks krishicks force-pushed the hicks/push-nvuozlywzuwu branch 2 times, most recently from 62da29d to 8dc2a54 Compare June 11, 2026 15:20
@krishicks krishicks marked this pull request as ready for review June 12, 2026 16:35
@krishicks krishicks requested review from a team, derekwaynecarr and mrunalp as code owners June 12, 2026 16:35
@johntmyers johntmyers added the gator:in-review Gator is reviewing or awaiting PR review feedback label Jun 13, 2026
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

PR Review Status

Validation: This maintainer-authored PR is project-valid because it implements the JSON-RPC/MCP method-level policy work discussed in #1793, with documented v1 scope around sandbox-to-server HTTP request inspection.
Head SHA: 8dc2a54f9b99d2aa297ccfd49c102ea10ce982f4

Review findings:

  • Blocking: crates/openshell-sandbox/src/l7/jsonrpc.rs flattens JSON-RPC params into dot-separated keys without rejecting literal dotted keys or collisions. A request can present arguments.scope as a top-level param while sending a different nested arguments.scope object path to the upstream server, which can bypass params selectors. Please fail closed on ambiguous/dotted param keys or preserve nested params through policy evaluation.
  • Blocking: crates/openshell-sandbox/src/proxy.rs only force-denies GraphQL parse errors in the forward-proxy path. JSON-RPC parse errors are carried in request_info.jsonrpc.error but can still be allowed by generic REST-style method/path rules such as access: full or read-write. Please include JSON-RPC parse errors in the same force-deny path.
  • Warning: forward-proxy JSON-RPC audit logs use generic FORWARD_L7 / l7 output and omit RPC methods, params digest, and policy version, while the CONNECT path has richer JSON-RPC logging.
  • Warning: json_rpc.on_parse_error and json_rpc.batch_policy appear accepted by policy YAML but are not enforced or converted into proto behavior. Please either implement them or reject/remove them from the accepted schema.

Docs: Fern docs were updated for the new policy schema and sandbox policy behavior.

Next state: gator:in-review

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

PR Review Follow-Up

Head SHA: 8dc2a54f9b99d2aa297ccfd49c102ea10ce982f4

The required independent reviewer pass confirmed the two blocking findings from the previous gator review:

  • JSON-RPC params flattening can create a policy/upstream parser differential through dotted-key collisions.
  • Forward-proxy JSON-RPC parse errors are not force-denied, so invalid JSON-RPC can still be allowed by generic method/path rules.

Additional non-blocking warning from the independent review:

  • parse_jsonrpc_call accepts any object with a string method and does not require "jsonrpc": "2.0". If this endpoint is documented as JSON-RPC 2.0 enforcement, malformed objects should become parse errors and follow the same fail-closed path.

The earlier warnings also still apply: forward-proxy JSON-RPC audit logs are less detailed than CONNECT logs, and json_rpc.on_parse_error / json_rpc.batch_policy are accepted in YAML but not preserved or enforced in runtime policy semantics.

Next state: gator:in-review

@johntmyers johntmyers added the test:e2e Requires end-to-end coverage label Jun 15, 2026
@github-actions

Copy link
Copy Markdown

Label test:e2e applied for 8dc2a54. Open the existing run and click Re-run all jobs to execute with the label set. The run will execute the standard E2E suite after building the required gateway and supervisor images once. The matching required CI gate status on this PR will flip green automatically once the run finishes.

@ddurst-nvidia

ddurst-nvidia commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

I would recommend adding an Action to add a test the functionality from and to modelcontextprotocol/conformance and through OpenShell.

There is an action, but I don't think it's setup by default in a useful way for testing through OpenShell.

There everything-{client,server} stand ins that can be used to validate. If something isn't supported, we can just mark it as unsupported, but it reduces surprises for people using it.

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After Maintainer Update

I re-evaluated latest head 8dc2a54f9b99d2aa297ccfd49c102ea10ce982f4 after @ddurst-nvidia's 2026-06-15 comment recommending OpenShell-through-MCP conformance coverage.

Disposition: not resolved yet. There has not been a new commit or author response after the existing gator review feedback, and the maintainer testing recommendation is additional review feedback to address or have explicitly waived by a maintainer before this can advance.

Remaining items:

  • Blocking: JSON-RPC params matching still needs to fail closed on ambiguous dotted keys/collisions or preserve nested params through policy evaluation.
  • Blocking: JSON-RPC parse errors in the forward-proxy path still need the same fail-closed handling as GraphQL parse errors.
  • Review feedback: please address @ddurst-nvidia's recommendation for MCP conformance coverage through OpenShell, or get a maintainer decision that this does not need to block this PR.
  • Non-blocking warnings remain around forward-proxy JSON-RPC audit log detail, accepted-but-unenforced json_rpc.on_parse_error / json_rpc.batch_policy, and JSON-RPC 2.0 validation strictness.

Checks: required branch, Helm, DCO, docs preview, and standard Rust/Python checks are currently passing. OpenShell / E2E is still pending because the labeled E2E run needs execution.

Next state: gator:in-review

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After CI Update

I re-evaluated latest head 8dc2a54f9b99d2aa297ccfd49c102ea10ce982f4 after the labeled E2E run completed.

Disposition: not resolved. The PR still has unresolved review feedback, and the required OpenShell / E2E gate is now failing.

Remaining items:

  • Blocking: JSON-RPC params matching still needs to fail closed on ambiguous dotted keys/collisions or preserve nested params through policy evaluation.
  • Blocking: JSON-RPC parse errors in the forward-proxy path still need the same fail-closed handling as GraphQL parse errors.
  • Review feedback: please address @ddurst-nvidia's recommendation for MCP conformance coverage through OpenShell, or get a maintainer decision that this does not need to block this PR.
  • CI: OpenShell / E2E failed in run https://github.com/NVIDIA/OpenShell/actions/runs/27429202249. The failed jobs are e2e / E2E (rust-podman), e2e / E2E (rust-docker), e2e / E2E (rust-podman-rootless), e2e / E2E (python), and kubernetes-e2e / Kubernetes E2E (Rust smoke). The failed-log download was not available to this gator run, so a maintainer or author should inspect the failed Run tests steps in the Actions UI before deciding whether this is PR-caused or infrastructure-related.

Next state: gator:in-review

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After CI Log Review

I re-evaluated latest head 8dc2a54f9b99d2aa297ccfd49c102ea10ce982f4 after inspecting the failed OpenShell / E2E logs for run https://github.com/NVIDIA/OpenShell/actions/runs/27429202249.

Disposition: not resolved. The PR still has unresolved review feedback, and the failed E2E logs now show an actionable policy regression.

Remaining items:

  • Blocking: JSON-RPC params matching still needs to fail closed on ambiguous dotted keys/collisions or preserve nested params through policy evaluation.
  • Blocking: JSON-RPC parse errors in the forward-proxy path still need the same fail-closed handling as GraphQL parse errors.
  • Review feedback: please address @ddurst-nvidia's recommendation for MCP conformance coverage through OpenShell, or get a maintainer decision that this does not need to block this PR.
  • CI: OpenShell / E2E is failing. The logs show existing non-JSON-RPC L7 paths hitting an OPA evaluation error in crates/openshell-sandbox/data/sandbox-policy.rego: object.get(jsonrpc, "method", null) expects an object. This appears to make the new JSON-RPC matcher unsafe when request.jsonrpc is null/missing, causing GraphQL/HTTP L7 E2E regressions such as failed GraphQL forward/CONNECT tests, Python sandbox policy L7 tests returning status 0, and a Kubernetes smoke L7 bypass test returning 403 instead of 200.

Next state: gator:in-review

@krishicks krishicks force-pushed the hicks/push-nvuozlywzuwu branch 2 times, most recently from e9786e2 to 3516a0b Compare June 15, 2026 19:03
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After Author Update

I re-evaluated latest head 3516a0be911c2717d5789fde4ed6a70ea1a6a9c0 after the new commits addressing the prior JSON-RPC review feedback and adding MCP conformance coverage.

Disposition: partially resolved.

Resolved items:

  • JSON-RPC params matching now fails closed on literal dotted keys / flattened selector ambiguity.
  • Forward-proxy JSON-RPC parse errors now force-deny before generic L7 policy evaluation.
  • Unsupported json_rpc.on_parse_error / json_rpc.batch_policy YAML fields are now rejected instead of silently accepted.
  • MCP conformance coverage has been added, and the direct conformance runner command is wired in the expected client-mode shape.

Remaining items:

  • Blocking: crates/openshell-sandbox/data/sandbox-policy.rego still calls object.get on request.jsonrpc without first proving it is an object. The Rust L7 evaluator serializes non-JSON-RPC requests as "jsonrpc": null, so object.get(request, "jsonrpc", {}) can still return null and then object.get(jsonrpc, "method", null) can hit the same OPA type error seen in the prior E2E logs. Please guard with is_object(jsonrpc) and add a regression test for the actual "jsonrpc": null input shape.
  • Warning: .github/workflows/mcp-conformance.yml still checks out modelcontextprotocol/conformance by mutable tag v0.1.11 and runs npm ci / npm run build in a privileged Docker-socket job with job-level secrets. Please pin the upstream checkout to an immutable commit SHA for that release. Also consider narrowing/removing job-level credentials from the third-party build step.

Checks: current branch and E2E checks are queued or pending for this head, with test:e2e still applied.

Next state: gator:in-review

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Blocked

Gator is blocked because GitHub reports this PR is not mergeable against main (mergeable_state: dirty) at head 3516a0be911c2717d5789fde4ed6a70ea1a6a9c0.

Next action: @krishicks, please rebase or merge main and resolve the conflicts so review and CI monitoring can continue. The earlier JSON-RPC review feedback and the current CI status still need follow-up after the merge conflict is resolved.

@johntmyers johntmyers added gator:blocked Gator is blocked by process or repository gates and removed gator:in-review Gator is reviewing or awaiting PR review feedback labels Jun 15, 2026
@krishicks krishicks force-pushed the hicks/push-nvuozlywzuwu branch from 3516a0b to 6d61408 Compare June 15, 2026 19:25
Comment thread .github/workflows/mcp-conformance.yml Outdated
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After Author and Reviewer Updates

I re-evaluated latest head 6d614085a82404c0491de0af757e77f234d6631c after the new author commit and @ddurst-nvidia's 2026-06-15 review comment about MCP conformance coverage.

Disposition: still blocked. GitHub still reports this PR is not mergeable against main (mergeable_state: dirty), so review and CI monitoring cannot advance until the merge conflict is resolved.

Remaining items:

  • Blocker: @krishicks, please rebase or merge main and resolve the conflicts.
  • Review feedback: @ddurst-nvidia noted that the workflow still uses the older MCP conformance v0.1.11 release while v0.1.16 is available. Please address that or get a maintainer decision that the older release is intentional.
  • Earlier review feedback should be re-checked after the conflict is resolved and the final diff is stable.

Checks: required branch, Helm, DCO, Rust, Python, and license/header checks are passing for this head. OpenShell / E2E is currently pending with test:e2e applied, including the new MCP conformance job.

Next state: gator:blocked

@krishicks krishicks force-pushed the hicks/push-nvuozlywzuwu branch from 6d61408 to 9af3648 Compare June 15, 2026 20:31
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After Author Update

I re-evaluated latest head 9af36485ca1b8d81799338db56df340075b3b75b after @krishicks force-pushed new commits on 2026-06-15.

Disposition: still blocked. GitHub still reports this PR is not mergeable against main (mergeable_state: dirty), so review and CI monitoring cannot advance until the merge conflict is resolved.

Remaining items:

  • Blocker: @krishicks, please rebase or merge main and resolve the conflicts.
  • Review feedback to re-check after the conflict is resolved: the JSON-RPC Rego matcher still appears to call object.get on request.jsonrpc without proving it is an object, so the prior "jsonrpc": null regression concern may still apply.
  • Maintainer review feedback: the workflow now uses modelcontextprotocol/conformance v0.1.16, but the checkout is still by mutable tag rather than an immutable commit SHA.

Checks: branch, DCO, Rust, Python, license/header, and Helm checks are currently passing or skipped for this head. OpenShell / E2E is still in progress with test:e2e applied, but gator remains blocked on mergeability first.

Next state: gator:blocked

@krishicks krishicks force-pushed the hicks/push-nvuozlywzuwu branch from 9af3648 to 7101a37 Compare June 15, 2026 21:26
Comment thread docs/reference/policy-schema.mdx
@krishicks krishicks force-pushed the hicks/push-nvuozlywzuwu branch from f7027d7 to c489300 Compare June 26, 2026 20:06
@krishicks krishicks changed the title feat(l7): add JSON-RPC policy enforcement feat(l7): add JSON-RPC and MCP policy enforcement Jun 26, 2026
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

PR Review Status

Validation: This PR remains project-valid because it implements the JSON-RPC/MCP method-level policy work tracked by #1793, with docs and E2E coverage for the current policy schema and conformance path.
Head SHA: c4893000ccf7ff271810f48b4ac1d0d54fcebf16

Review findings:

  • No blocking findings remain from the current independent review. The previous JSON-RPC receive-stream authorization blocker is resolved: generic protocol: json-rpc receive-stream GET requests are denied, while the receive-stream allowance is restricted to protocol: mcp.
  • Non-blocking docs follow-up: the MCP example in docs/reference/policy-schema.mdx now uses jsonrpc.example.com and /rpc despite protocol: mcp; using mcp.example.com and /mcp would keep the MCP/generic JSON-RPC distinction clearer.
  • Non-blocking docs follow-up: docs/sandboxes/policies.mdx could also state that generic JSON-RPC accepts exact method names or method: "*" only.

Docs: Fern docs are present and the generic JSON-RPC allow/deny docs now describe exact method names or method: "*" only.

Checks: test:e2e remains applied. Current Branch Checks, Helm Lint, DCO, Rust, Python, docs preview, build, and image jobs are passing; OpenShell / E2E is still pending/in progress for this head.

Next state: gator:watch-pipeline

@johntmyers johntmyers added gator:watch-pipeline Gator is monitoring PR CI/CD status and removed gator:in-review Gator is reviewing or awaiting PR review feedback labels Jun 26, 2026
@rpelevin

Copy link
Copy Markdown

I agree the split between rules and deny_rules is awkward if the allow side already uses an allow wrapper. The product model should probably be one visible rule grammar and one explicit precedence rule, rather than two different containers that users have to mentally merge.

The invariant I would preserve is not the current field shape; it is deny precedence plus a typed target for each rule. A migration-safe shape could be:

  1. accepted input may keep rules plus deny_rules for compatibility;
  2. normalized policy lowers both forms into one ordered internal rule set with effect, protocol, method, tool, and params fields;
  3. deny effects are evaluated before allow effects, regardless of authoring order;
  4. MCP tool selectors remain valid only for tools/call, and generic JSON-RPC still matches method only until params matching is intentionally designed.

That gives the docs a simpler story: users write allow and deny entries with the same fields, while the engine records the effect and applies deny-before-allow deterministically.

The regression I would add is a dual-shape parity test. Feed the old sibling shape and the new unified shape into the loader, assert the same normalized policy graph, then prove one denied tool in an otherwise allowed MCP batch blocks the whole batch. That catches both migration drift and precedence drift.

Boundary: architecture and regression-test feedback only; no claim about running this branch, validating implementation behavior, implementation correctness, merge readiness, security review, production readiness, partnership, customer interest, official alignment, NVIDIA usage, OpenShell usage, conformance certification, or Neura usage.

@krishicks krishicks force-pushed the hicks/push-nvuozlywzuwu branch from c489300 to adbfacd Compare June 26, 2026 20:36
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

PR Review Status

Validation: This PR remains project-valid because it implements the JSON-RPC/MCP method-level policy work tracked by #1793, with docs and E2E coverage for the policy schema, JSON-RPC/MCP enforcement, and conformance path.
Head SHA: adbfacd90b11f5bc88d50a3cb35b8d98e77234d1

Review findings:

  • No blocking findings remain from the current independent review.
  • Non-blocking follow-up: proto/sandbox.proto still describes the params matcher map as a generic JSON-RPC dot-separated nested selector. The current runtime/docs intentionally reject generic JSON-RPC params matchers and support only MCP params.name / tool selection, so those proto comments should be narrowed when convenient.

Docs: Fern docs under docs/ are updated for the current JSON-RPC and MCP policy behavior; no navigation update appears necessary.

Checks: test:e2e remains applied. OpenShell / Branch Checks and OpenShell / Helm Lint are passing; OpenShell / E2E is still pending/in progress for this head.

Next state: gator:watch-pipeline

@krishicks krishicks force-pushed the hicks/push-nvuozlywzuwu branch from adbfacd to c594e2d Compare June 26, 2026 20:51
@johntmyers johntmyers added gator:in-review Gator is reviewing or awaiting PR review feedback and removed gator:watch-pipeline Gator is monitoring PR CI/CD status labels Jun 26, 2026
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

PR Review Status

Validation: This PR remains project-valid because it implements the JSON-RPC/MCP method-level policy work tracked by #1793, with docs and E2E coverage for the policy schema, JSON-RPC/MCP enforcement, and conformance path.
Head SHA: c594e2d52f5993006d99207f8cf734abfb447f8f

Review findings:

  • Blocking: crates/openshell-providers/src/profiles.rs moves endpoint.mcp out of a borrowed &NetworkEndpoint while converting proto endpoint profiles. Generated prost messages are not Copy, so this should fail to compile. Please convert through a reference, e.g. endpoint.mcp.as_ref().map(mcp_options_from_proto), and update mcp_options_from_proto to take &McpOptions.
  • Warning: crates/openshell-supervisor-network/src/opa.rs normalizes and removes json_rpc / mcp config stanzas before validation on the raw YAML-to-OPA path. Malformed or unknown nested config such as json_rpc: { on_parse_error: allow } or mcp: { max_body_bytes: "large" } can be accepted and ignored there even though the typed parser rejects it. Please either validate the stanza shape and allowed keys before normalization, or make normalization return validation errors.
  • Non-blocking docs follow-up: docs/sandboxes/policies.mdx says full policy YAML supports graphql and json-rpc, but omits mcp in that table row.

Docs: Fern docs under docs/ are updated for the direct policy UX changes; the only docs item above is a small consistency follow-up.

Checks: test:e2e remains applied. Current Branch Checks and Helm Lint are passing; OpenShell / E2E is still pending/in progress for this head.

Next state: gator:in-review

@krishicks krishicks force-pushed the hicks/push-nvuozlywzuwu branch from c594e2d to 470f882 Compare June 26, 2026 21:37
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

PR Review Status

Validation: This PR remains project-valid because it implements the JSON-RPC/MCP method-level policy work tracked by #1793, with docs and E2E coverage for the policy schema, JSON-RPC/MCP enforcement, and conformance path.
Head SHA: 470f882cba468ea0e8aeef1b5789172e51f04803

Review findings:

  • Blocking: crates/openshell-providers/src/profiles.rs still moves endpoint.mcp out of a borrowed &NetworkEndpoint while converting proto endpoint profiles. Generated prost message fields are not Copy, so this should fail to compile. Please convert through a reference, for example endpoint.mcp.as_ref().map(mcp_options_from_proto), and update mcp_options_from_proto to take &McpOptions.
  • Non-blocking cleanup: crates/openshell-supervisor-network/src/opa.rs calls normalize_l7_config_aliases twice in the YAML preprocessing path. The second call appears harmless after the first removes aliases, but it is redundant and makes the validation flow harder to read.

Docs: Fern docs under docs/ are updated for the direct policy UX changes; no navigation update appears necessary.

Checks: test:e2e remains applied. Branch Checks, Helm Lint, and E2E are currently pending or in progress for this head.

Next state: gator:in-review

@rpelevin

Copy link
Copy Markdown

The remaining blocker looks like a useful compile-time version of the same policy-boundary problem: the proto path has to preserve MCP endpoint identity without taking ownership of a borrowed endpoint object.

I would make the conversion contract explicit:

  1. endpoint profile conversion reads the MCP options by reference;
  2. the helper that lowers MCP options takes a borrowed MCP options value;
  3. YAML and proto endpoint profiles produce the same internal MCP policy shape;
  4. malformed MCP and JSON-RPC config stanzas fail before normalization can erase the evidence.

That last point matters because the policy loader should not make unsupported configuration disappear before validation. Alias normalization is fine as a compatibility layer, but it should happen after the parser has decided whether the original config shape is valid enough to normalize. Otherwise an invalid stanza can become invisible on one load path while the typed parser rejects it on another.

The regression I would add is two-part. First, compile-level coverage or a focused unit test for the borrowed endpoint conversion path so endpoint.mcp cannot be moved out of a shared endpoint again. Second, YAML-versus-proto parity coverage where the same MCP endpoint profile lowers to the same internal policy record, including max body size, tool identity support, and unsupported-field rejection.

Boundary: architecture and regression-test feedback only; no claim about running this branch, validating implementation behavior, implementation correctness, merge readiness, security review, production readiness, partnership, customer interest, official alignment, NVIDIA usage, OpenShell usage, conformance certification, or Neura usage.

@krishicks

Copy link
Copy Markdown
Collaborator Author
  • Blocking: crates/openshell-providers/src/profiles.rs still moves endpoint.mcp out of a borrowed &NetworkEndpoint while converting proto endpoint profiles. Generated prost message fields are not Copy, so this should fail to compile. Please convert through a reference, for example endpoint.mcp.as_ref().map(mcp_options_from_proto), and update mcp_options_from_proto to take &McpOptions.

This feedback was not applied because it generated a linter error:

› error: this argument (2 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
     --> crates/openshell-providers/src/profiles.rs:846:36
      |
  846 | fn mcp_options_from_proto(options: &McpOptions) -> McpOptionsProfile {
      |                                    ^^^^^^^^^^^ help: consider passing by value instead: `McpOptions`

Add policy schema, proto, provider profile, OPA, and L7 proxy support for
`protocol: json-rpc` and `protocol: mcp`. Generic JSON-RPC endpoints match
exact method names only, with `method: "*"` as the all-method sentinel;
wildcard/glob methods and params matchers are rejected.

Parse JSON-RPC request bodies and batches in the forward proxy, deny
response-shaped client frames, limit receive-stream GET allowance to MCP
endpoints, and redact params in decision logs. Preserve L7 rule params on the
proto load path so MCP `tools/call` tool filters behave like YAML-loaded
policies.

Add MCP conformance coverage, JSON-RPC L7 e2e coverage, and docs for the new
protocols and current matcher limitations.

Co-authored-by: ddurst <267424412+ddurst-nvidia@users.noreply.github.com>
Signed-off-by: Kris Hicks <khicks@nvidia.com>
@krishicks krishicks force-pushed the hicks/push-nvuozlywzuwu branch from 470f882 to f5dbcc5 Compare June 26, 2026 22:06
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

PR Review Status

Validation: This PR remains project-valid because it implements the JSON-RPC/MCP method-level policy work tracked by #1793, with docs and E2E coverage for the policy schema, JSON-RPC/MCP enforcement, and conformance path.
Head SHA: f5dbcc50553a05f0b9083dd35789c89d1ce08371

Review findings:

  • Blocking: crates/openshell-providers/src/profiles.rs still moves endpoint.mcp out of a borrowed &NetworkEndpoint while converting proto endpoint profiles: mcp: endpoint.mcp.map(mcp_options_from_proto). Generated prost message fields are not Copy, so this fails to compile. To preserve the by-value helper that satisfies clippy, clone at the call site instead: mcp: endpoint.mcp.clone().map(mcp_options_from_proto).

Docs: Fern docs under docs/ are updated for the direct policy UX changes; no navigation update appears necessary.

Checks: test:e2e remains applied. Current OpenShell / Branch Checks is failing, with Rust (linux-amd64-cpu8) failed; OpenShell / E2E is still pending/in progress for this head.

Next state: gator:in-review

@rpelevin

Copy link
Copy Markdown

The useful distinction here is between ownership transfer and representation lowering. If the proto helper wants a by-value MCP options object for lint and ergonomics, the call site still has to make the ownership boundary explicit.

I would keep the invariant as:

  1. endpoint profile conversion never moves fields out of a borrowed endpoint;
  2. optional MCP options are copied or cloned at the call site before lowering;
  3. the lowering helper owns the small MCP options value it receives;
  4. YAML and proto load paths still produce the same internal MCP policy record.

That keeps the code honest about where ownership changes. Passing by reference hides the move but can fight the local lint rule. Moving directly from endpoint.mcp violates the borrowed endpoint boundary. Cloning the optional proto field at the conversion boundary is the clearest contract: shared endpoint in, owned policy profile out.

The regression I would add is targeted and small. Build or unit coverage should exercise a borrowed NetworkEndpoint containing MCP options and prove conversion succeeds without moving out of the borrowed endpoint. A parity test should then assert that the YAML and proto versions of the same MCP endpoint produce the same max body size and tool identity policy. That catches both the compile failure and the subtler policy-widening failure if MCP options disappear during conversion.

Boundary: architecture and regression-test feedback only; no claim about running this branch, validating implementation behavior, implementation correctness, merge readiness, security review, production readiness, partnership, customer interest, official alignment, NVIDIA usage, OpenShell usage, conformance certification, or Neura usage.

@johntmyers

Copy link
Copy Markdown
Collaborator

@rpelevin do you have specific interest around this PR?

@johntmyers

Copy link
Copy Markdown
Collaborator

Gator - tests are passing, are you sure on the compilation claims you're making?

@rpelevin

Copy link
Copy Markdown

Yes. My interest is specifically the policy and approval boundary around agent network egress, not a claim about using OpenShell or representing a customer.

This PR is one of the cleaner places where that boundary becomes concrete: generic JSON-RPC, MCP tool calls, proto and YAML parity, and conformance all meet at the point where an agent request leaves the sandbox. The part I care about is whether the runtime can prove what was allowed before the request crosses that boundary, and whether denial or unsupported cases stay visible instead of being normalized away.

The useful product invariant, in my view, is:

  1. a policy rule should bind to a stable method or tool identity and the exact argument envelope it evaluated;
  2. unsupported or ambiguous request shapes should fail closed before generic allow rules can absorb them;
  3. YAML and proto profiles should produce the same internal policy record;
  4. audit or review output should preserve enough evidence to explain why a request was allowed, denied, or not evaluated.

That is why I have been focusing on compile and test boundaries rather than feature preference. Ownership bugs, selector ambiguity, and YAML/proto drift are not just implementation details here. They decide whether the eventual authority record can be trusted by another operator, reviewer, or system.

Boundary: architecture and regression-test feedback only; no claim about running this branch, validating implementation behavior, implementation correctness, merge readiness, security review, production readiness, partnership, customer interest, official alignment, NVIDIA usage, OpenShell usage, conformance certification, or Neura usage.

@johntmyers johntmyers added gator:approval-needed Gator completed review; maintainer approval needed and removed gator:in-review Gator is reviewing or awaiting PR review feedback labels Jun 26, 2026
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After Maintainer Update

Thanks @johntmyers. I re-checked latest head f5dbcc50553a05f0b9083dd35789c89d1ce08371 after your question about the compilation claim, and also considered @krishicks' earlier note that changing the helper to take &McpOptions hit a lint rule.

What I checked: the current crates/openshell-providers/src/profiles.rs still uses mcp: endpoint.mcp.map(mcp_options_from_proto), and the exact-head Rust jobs for this SHA completed successfully on both linux-amd64 and linux-arm64. The required Branch Checks and E2E gates are also green.

Disposition: resolved. The prior compile-failure blocker was not valid for this head, so I am retracting it. No blocking gator review findings remain.

Review: no blocking findings remain from gator for this SHA.
Docs: Fern docs under docs/ are updated for the JSON-RPC/MCP policy behavior.
Checks: Branch Checks, Helm Lint, DCO, docs preview, Rust, Python, build/image jobs, and OpenShell / E2E are passing. test:e2e remains applied and passed, including the MCP E2E job.
Approval: maintainer approval is still required; GitHub reports REVIEW_REQUIRED.

Next state: gator:approval-needed

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Monitoring Complete

Monitoring is complete because this PR has merged.

Final status: gator had moved this PR to gator:approval-needed at head f5dbcc50553a05f0b9083dd35789c89d1ce08371; required Branch Checks, Helm Lint, DCO, docs preview, and OpenShell / E2E were passing, test:e2e passed including MCP E2E, and maintainer approval was present before merge.

I removed the active gator:* label because there is nothing left for gator to monitor on this PR.

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

Labels

test:e2e Requires end-to-end coverage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support method-level governance for MCP tool calls (JSON-RPC) in sandbox policy

5 participants