Skip to content

fix(mxc): reject unsupported live policy updates - #3480

Merged
shailendra-nv merged 10 commits into
NVIDIA:windowsfrom
prekshivyas:fix/nvbug-6782891-mxc-policy-update-gate
Sep 22, 2026
Merged

shailendra-nv merged 10 commits into
NVIDIA:windowsfrom
prekshivyas:fix/nvbug-6782891-mxc-policy-update-gate

Conversation

@prekshivyas

@prekshivyas prekshivyas commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add an explicit compute-driver capability for live policy updates; MXC reports unsupported.
  • Reject operator-authored MXC policy set/merge requests with FAILED_PRECONDITION before creating a pending revision, while preserving sandbox-authored startup sync.
  • Make openshell policy get query the persisted status for the exact sandbox revision and report the real active version instead of hard-coding Effective; global effective policy remains workspace-readable without querying the admin-only global history endpoint.

Internal tracking issue.

Root cause and scope

The gateway deliberately allowed additive filesystem changes because they are safe to store for a later restart, but MXC cannot reload policy into an already-created sandbox. The handler therefore accepted an operator update and persisted a pending revision that could not become active. Separately, the CLI's latest-policy path hard-coded the status and active version, contradicting policy list and the persisted policy history.

The fix is capability-based rather than a handler special case. Older/external drivers that omit the optional capability retain the existing behavior; the in-tree MXC driver explicitly opts out. Settings updates, sandbox-authored startup synchronization, and workspace access to an effective global policy are unchanged.

Reproduction on the unmodified tip

  • Base: upstream/windows at fb2980e077288b61ef03a2e6187e162d158526aa
  • Host: Windows ARM64 (Yukon), Rust 1.95, VS 2022 ARM64 tools
  • Existing validation command: cargo test -p openshell-server --lib --features bundled-z3 --target aarch64-pc-windows-msvc validate_static_fields_allows_additive_filesystem -- --nocapture
  • Before: passed, confirming additive filesystem changes were accepted.
  • Handler-level reproduction, production code unchanged:
    • live sandbox active version was v1;
    • additive filesystem update returned success as v2;
    • v2 persisted as pending while the sandbox remained active on v1;
    • GetSandboxConfig served v2, and the CLI latest-policy path labeled it effective.
    • Reproduction test completed in 0.06 s after build.

After

  • An operator-authored MXC policy update returns FAILED_PRECONDITION with delete/recreate guidance.
  • No pending policy revision is written.
  • Sandbox-authored startup sync remains accepted.
  • For any existing pending sandbox revision, policy get now reports Pending and the actual active version, matching policy history/list semantics.
  • Sandbox views of synchronously loaded global policy retain their existing workspace-readable authorization path.

Validation

  • cargo test -p openshell-server --lib --features bundled-z3 --target aarch64-pc-windows-msvc mxc_ -- --nocapture --test-threads=1 — 3 passed (including both new gate/sync regressions)
  • cargo test -p openshell-cli --test sandbox_name_fallback_integration --target aarch64-pc-windows-msvc policy_get_ -- --nocapture --test-threads=1 — 5 passed, including pending/active consistency and global-policy access regression coverage
  • cargo test -p openshell-driver-mxc --lib --target aarch64-pc-windows-msvc ui_policy_capability_tracks_configured_backend -- --nocapture — passed
  • cargo fmt --all -- --check — passed
  • git diff --check — passed
  • cargo clippy -p openshell-server --lib --features bundled-z3 --target aarch64-pc-windows-msvc — passed; only pre-existing warnings
  • cargo clippy -p openshell-cli --test sandbox_name_fallback_integration --target aarch64-pc-windows-msvc — passed; only a pre-existing dependency warning
  • cargo clippy -p openshell-driver-mxc --lib --target aarch64-pc-windows-msvc — passed; only pre-existing warnings

A combined check of Unix-only compute drivers cannot run on Windows ARM64 because their openssh dependency intentionally emits This crate can only be used on unix; their changes are the mechanical initialization of the new optional protobuf field. Server, CLI, and MXC Windows targets compile and pass.

Security impact and residual risk

The gateway now fails closed for the shipped MXC driver instead of accepting a filesystem grant that the live sandbox cannot enforce. Existing sandbox policy is not weakened, and rejected updates produce no pending state. Drivers that do not report the new optional capability preserve legacy behavior for protocol compatibility; MXC explicitly reports false. Testing used native Windows ARM64; no x64-only input was required.

Commit cbe9e23806ba7802a8fe50de63a779810c89e325 is SSH-signed and GitHub reports verified: true (reason: valid).

@prekshivyas

prekshivyas commented Sep 19, 2026

Copy link
Copy Markdown
Contributor Author

Verification record (Windows ARM64)

Base: fb2980e077288b61ef03a2e6187e162d158526aa (upstream/windows)

Before (unmodified tip): validate_static_fields_allows_additive_filesystem passed, confirming the gateway accepted the reported update shape. The handler-level reproduction then returned success as v2, persisted v2 as pending, left the sandbox active on v1, and served v2 through GetSandboxConfig; the CLI latest-policy path independently hard-coded effective and active v2.

After: the operator update returns FAILED_PRECONDITION before persistence, no pending revision is created, and the error gives delete/recreate guidance. Sandbox-authored startup sync still succeeds. CLI policy-get coverage now receives latest v9 / active v7 / pending and emits that same status in JSON and table output. A separate regression proves that a workspace user can still read a synchronously loaded global effective policy without calling the platform-admin-only global history endpoint.

cargo test -p openshell-server --lib --features bundled-z3 --target aarch64-pc-windows-msvc mxc_ -- --nocapture --test-threads=1
test result: ok. 3 passed; 0 failed; 1556 filtered out

cargo test -p openshell-cli --test sandbox_name_fallback_integration --target aarch64-pc-windows-msvc policy_get_ -- --nocapture --test-threads=1
test result: ok. 5 passed; 0 failed; 4 filtered out

The MXC capability test, format, diff, and focused server/CLI/MXC clippy checks also passed (pre-existing warnings only). Unix-only driver packages cannot be checked on this native Windows target because the upstream openssh crate intentionally rejects non-Unix compilation; their edits only initialize the new optional protobuf field.

Commit cbe9e23806ba7802a8fe50de63a779810c89e325: GitHub verified: true, reason valid.

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
@prekshivyas
prekshivyas force-pushed the fix/nvbug-6782891-mxc-policy-update-gate branch from 02f2c49 to cbe9e23 Compare September 19, 2026 21:33
@prekshivyas

prekshivyas commented Sep 19, 2026

Copy link
Copy Markdown
Contributor Author

CI base-state note

The mise Lockfile failure is independent of this PR. The workflow runs mise lock and then requires mise.lock to remain unchanged; a clean generation from the exact base SHA (fb2980e0) updates existing Skaffold checksums and uv Linux artifact metadata. This PR does not change mise.toml or mise.lock.

I have intentionally not imported that unrelated generated lockfile refresh into this narrowly scoped change. All other completed checks are green; the three shared Rust matrix jobs are still in progress.

@prekshivyas prekshivyas changed the title NVBug 6782891: reject unsupported MXC live policy updates fix(mxc): reject unsupported live policy updates Sep 20, 2026
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
@gburachas

Copy link
Copy Markdown
Contributor

Independent Windows ARM64 MXC ProcessContainer validation completed on testpc: the sandbox reached Ready with AgentRunning and ConfigurationReady; a v2 filesystem-policy update returned FailedPrecondition; the sandbox remained at revision 1; and policy history retained only v1 as Loaded.

@shailendra-nv shailendra-nv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Requesting changes for two remaining gaps in the live-policy safety boundary:

  1. The capability check only guards sandbox-scoped UpdateConfig; global writes and policy-advisor approval and undo paths can still persist revisions for MXC.
  2. policy get treats a persisted global revision as loaded and active without runtime acknowledgement.

Please centralize the capability check across every operator-authored mutation path, avoid reporting global activation without per-sandbox confirmation, and add regression coverage for global updates plus manual, bulk, automatic approval, and undo. The direct set and update gate and compatibility-preserving capability shape are otherwise sound.

Comment thread crates/openshell-server/src/grpc/policy.rs Outdated
Comment thread crates/openshell-cli/src/run.rs
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
@prekshivyas

Copy link
Copy Markdown
Contributor Author

Review changes are pushed in 9fb6fe9.

Qualification:

  • cargo test -p openshell-server mxc_rejects_ --lib -- --nocapture: 3 passed
  • mise run --skip-tools --jobs 1 pre-commit: passed on Windows ARM64 (Python 3.13 and x64 Biome binary used to avoid the known native ARM64 tool crashes)
  • Both review threads have commit-specific replies and are resolved.

The regressions cover global replacement/deletion, direct operator updates, manual approval, bulk approval, automatic approval, approved-chunk rejection, and undo, and verify rejected operations do not persist or advance policy state. Sandbox-authored startup sync remains supported. @shailendra-nv ready for re-review.

@shailendra-nv shailendra-nv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Requesting changes for two remaining gaps in the MXC live-policy invariant:

  1. The global no-sandbox check is not serialized with provider-free sandbox creation, so a concurrent create and global update can both succeed with different effective policies.
  2. Provider attachment/detachment and provider-profile fanout still mutate composed effective policy without consulting the live-update capability.

Resolution: make sandbox creation atomic with the global-policy transition for drivers without live updates; apply the capability guard to every provider-driven effective-policy mutation affecting existing sandboxes; add concurrency and provider-path regressions. Please also document the new optional capability contract in architecture/compute-runtimes.md.

The direct set/merge, global sequential, manual/bulk/automatic approval, reject, and undo paths added in 9fb6fe9 are otherwise correctly gated, and current CI is green.

Comment thread crates/openshell-server/src/grpc/policy.rs
Comment thread crates/openshell-server/src/grpc/policy.rs Outdated
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
@prekshivyas

Copy link
Copy Markdown
Contributor Author

Addressed both capability-boundary/race threads in 5e1d3f0c8: every sandbox create now serializes with global policy changes, and unsupported MXC attach/detach plus attached provider-profile updates reject before persistence. Added focused race and persistence regressions. Formatting/diff checks and cargo check -p openshell-server --tests pass; executable test linking is locally blocked only by the ARM64 host lacking libz3.lib.

@prekshivyas

Copy link
Copy Markdown
Contributor Author

@shailendra-nv All requested updates are pushed, the addressed threads are resolved, and validation evidence is posted above. Could you please re-review?

@prekshivyas

Copy link
Copy Markdown
Contributor Author

Follow-up CI fix pushed in b91c58b34: rewrote the provider-profile gate as the Clippy-requested expression; the exact package Clippy gate now passes locally. @shailendra-nv please re-review the new head.

@prekshivyas

Copy link
Copy Markdown
Contributor Author

Second CI lint follow-up pushed in 95c64487a: reordered the equivalent validation branches to satisfy clippy::if_not_else on the CI Rust toolchain. @shailendra-nv the latest head is ready for re-review; fresh matrix is running.

@prekshivyas

Copy link
Copy Markdown
Contributor Author

Test follow-up pushed in 2f7dd1827: replaced the scheduler-sensitive combined race assertion with deterministic shared-guard tests for both sides of the race: global-policy transition and provider-free sandbox creation. Production locking is unchanged. @shailendra-nv please re-review the latest head; the fresh matrix is running.

@prekshivyas

Copy link
Copy Markdown
Contributor Author

CI confirmation: the complete Branch Checks matrix is green on 2f7dd1827, including all 6,298 Rust tests on x86_64 Linux and both ARM64 Rust jobs. @shailendra-nv the latest head is ready for re-review.

@shailendra-nv shailendra-nv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Requesting changes at head 2f7dd18 for one synchronization regression and the remaining contract documentation gap:

  1. The provider-free create fix now holds the process-wide sandbox synchronization guard across the complete driver CreateSandbox RPC for every driver. A slow provision therefore serializes otherwise independent creates and blocks global/provider mutations even when live policy reload is supported.
  2. The optional live-update capability and its expanded MXC behavior remain incompletely documented: attachment, detachment, and attached-profile updates are now rejected, but the operator docs and CLI skill describe only policy replacement/merge rejection.

Resolution: preserve the MXC create/global-policy invariant with a capability-conditional or narrower critical section, add a live-update-driver concurrency regression, and document the optional capability plus every affected mutation workflow.

The earlier policy-boundary findings are otherwise resolved: provider-free creation is synchronized, and provider attachment, detachment, and profile fanout are gated with regression coverage. Current CI is green.

Comment thread crates/openshell-server/src/grpc/sandbox.rs Outdated
Comment thread crates/openshell-server/src/grpc/sandbox.rs
@prekshivyas

Copy link
Copy Markdown
Contributor Author

Addressed the latest requested changes in dbf328bef:

  • Sandbox creation takes the synchronization guard only for drivers with supports_live_policy_updates = false, preserving the MXC create/global-policy invariant without serializing capable drivers.
  • Added a supporting-driver concurrency regression that proves a provider-free create completes while the synchronization guard is held.
  • Documented the optional capability, legacy None => true compatibility, and every newly rejected MXC mutation path: direct replace/merge, global updates, advisor approval/undo, provider attach/detach, and attached-profile updates.

Local validation on Windows ARM64:

  • cargo fmt --check
  • git diff --check
  • cargo check -p openshell-server --tests

The two review threads are resolved against the pushed commit. @shailendra-nv, ready for re-review after CI completes.

@shailendra-nv shailendra-nv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Requesting changes at head dbf328b for a provider-backed creation race introduced while narrowing the synchronization scope.

The new capability-only condition correctly restores concurrent provider-free creates for live-update drivers, but it also removes the pre-existing sandbox_sync_guard from live-update-driver creates that start with providers. A concurrent profile mutation can scan before the sandbox is persisted, commit after create-time provider policy is snapshotted, and leave the new runtime starting from stale provider-composed policy without identifying that sandbox for reload.

Resolution: keep provider-backed creates serialized while allowing provider-free creates to bypass the guard on capable drivers—for example, acquire it when live updates are unsupported OR the requested provider list is non-empty. Retain the new provider-free concurrency test and restore regression coverage proving a provider-backed create still waits for the guard.

The capability, operator documentation, and prior broad-serialization finding are otherwise resolved. Current CI has no failures; the three Rust jobs are still running.

Comment thread crates/openshell-server/src/grpc/sandbox.rs Outdated
Comment thread crates/openshell-server/src/grpc/sandbox.rs
@prekshivyas

Copy link
Copy Markdown
Contributor Author

Follow-up review fix pushed in b17199ccd:

  • Capable-driver provider-free creates still bypass sandbox_sync_guard.
  • Provider-backed creates now always retain the guard, preventing a profile mutation from committing between the create-time policy snapshot and sandbox persistence.
  • Restored create_sandbox_with_providers_waits_for_sandbox_sync_guard alongside the provider-free capable-driver concurrency test.

Revalidated with cargo fmt --check, git diff --check, and cargo check -p openshell-server --tests on Windows ARM64. Both new review threads are resolved. @shailendra-nv, ready for re-review at this head after the restarted CI completes.

@prekshivyas

Copy link
Copy Markdown
Contributor Author

CI follow-up pushed in 1b4df5d83. The restored regression used sandbox name provider-backed-create (22 characters), so request validation rejected it before the synchronization guard because sandbox names are limited to 19 characters. Renamed the fixture to provider-create; production synchronization logic is unchanged.

Focused Windows ARM64 proof with the qualification-kit Z3 library:

cargo test -p openshell-server create_sandbox_with_providers_waits_for_sandbox_sync_guard -- --nocapture

Result: 1 passed, 0 failed. Formatting and git diff --check also pass. CI has restarted at the corrected head.

@shailendra-nv
shailendra-nv merged commit b031dc0 into NVIDIA:windows Sep 22, 2026
64 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants