feat(driver-mxc): native Windows MXC compute driver + server wiring - #2721
feat(driver-mxc): native Windows MXC compute driver + server wiring#2721jamieknvidia wants to merge 18 commits into
Conversation
Introduces the openshell-driver-mxc crate implementing ComputeDriver backed by Microsoft MXC isolation sessions (Windows only). Wires the new driver into the server's build_compute_runtime dispatch and adds the Mxc variant to ComputeDriverKind. Also adds a local protobuf-src stub (tools/protobuf-src-local) to unblock Windows builds that lack MSYS2/MinGW, and pins the zig Windows x64 toolchain in mise.lock. (cherry picked from commit 4f70122) Signed-off-by: Jamie King <jamiek@nvidia.com>
…ing, demo artifacts) Safety checkpoint of uncommitted work from the background agent run that stalled mid-Step-7. Includes: mxc-driver-recon.md (Step 0.5), policy_map.rs (~876L embedded mapper), A1 policy-threading edits across driver.rs/policy.rs/mxc.rs/compute/mod.rs, and examples/ (demo.yaml + mxc-gateway.toml). Not yet verified to compile end-to-end; to be reorganized into the skill's Step 11 commit sequence. (cherry picked from commit 38e42c0) Signed-off-by: Jamie King <jamiek@nvidia.com>
- Bring futures::StreamExt into scope for the watch-stream `.next()` call in driver::lifecycle_tests so the negative policy proof test compiles. - Bind a local `mapper` and drop the unused/deprecated NetworkBinary in the embedded-mapper network-policy rejection test. Signed-off-by: Jamie King <jamiek@nvidia.com> (cherry picked from commit 039b0ba) Signed-off-by: Jamie King <jamiek@nvidia.com>
The gateway mints `sandbox_token` only when a sandbox-JWT issuer is configured. There is no in-sandbox supervisor on MXC (supervisor-removal design — D1/D4), so no component ever consumes the token; requiring it on the driver side blocks the demo's `--disable-tls` smoke gateway with a spurious `invalid_argument`. Log the absence and proceed instead. Signed-off-by: Jamie King <jamiek@nvidia.com> (cherry picked from commit cea2097) Signed-off-by: Jamie King <jamiek@nvidia.com>
monitor_exec demoted Ready->Error on exit 0 (reason ExecCompleted), so the positive demo (write hello.txt + exit) landed in Error phase. Keep Ready=True (reason AgentCompleted) on success; only non-zero exits go to ExecFailed. Tighten the positive lifecycle test to assert the terminal condition stays Ready=True/AgentCompleted. Verified live via gateway mock round-trip: phase now Provisioning->Ready with no demotion. (cherry picked from commit 54ab030) Signed-off-by: Jamie King <jamiek@nvidia.com>
Add a backend selector to the MXC driver (isolation_session default | process_container). process_container drives a one-shot AppContainer that is genuinely default-deny: a write to any ungranted path is denied by the OS, unlike isolation_session which is grant-only and cannot deny. The lifecycle forks on the flag - isolation_session keeps provision/start/exec, process_container runs a single ephemeral container via run_oneshot. Also: run-demo.ps1 gains -Backend and hardens the CLI register/create calls; docs corrected to state isolation_session does NOT deny out-of-policy writes and that the negative proof requires process_container. Verified end-to-end on a real demo box (gateway -> CLI -> driver -> MXC): in-policy write succeeds, out-of-policy write denied (PermissionDenied), OVERALL: PASS. (cherry picked from commit c6cde38) Signed-off-by: Jamie King <jamiek@nvidia.com>
…one crate Adopt the proto-based mapper (map_to_mxc) as the single source of truth, embedded in openshell-driver-mxc as a Windows-gated `policy_map` module. Rewire EmbeddedPolicyMapper to call it directly on the typed SandboxPolicy, deleting the serde_yaml proto->YAML bridge. Move the CLI to a windows-gated example and the parity tests into the crate; delete openshell-policy-mapper. - gate policy_map + seam Windows-only (MXC is Windows-only) - drop serde_yaml; add dev-deps openshell-policy, clap, anyhow - normalize mapped paths to Windows form in the seam, in one place - docs: add driver-mxc to AGENTS.md table; correct design doc section 17 test lane Signed-off-by: Giedrius Burachas <gburachas@nvidia.com> (cherry picked from commit f22f9c7) Signed-off-by: Jamie King <jamiek@nvidia.com>
… egress Signed-off-by: Giedrius Burachas <gburachas@nvidia.com> (cherry picked from commit 96d6afa) Signed-off-by: Jamie King <jamiek@nvidia.com>
…he policy seam - split_policy: SocketAddr proxy_redirect (replaces bare port), processcontainer containment guard naming MXC M1, version preserved in the trimmed proxy_policy, delegation reported as an info loss item - seam: MappedConfig carries trimmed_policy + proxy_addr; MapCtx.egress selects the split path; coarse path unchanged when egress is disabled - driver: [openshell.drivers.mxc] egress_proxy / egress_proxy_addr config, validated at create (isolation_session rejected until M1); lifecycle threads the redirect into provision and stores the trimmed policy per sandbox, emitting an EgressRedirect platform event - mxc: optional MxcNetwork block (defaultPolicy=block + proxy) in provision and one-shot configs; mock records configs for test assertions - tests: lossless-invariant suite over all example policies (validate + serialize round-trip), split lifecycle proof, M1 rejection; example gains --split --proxy-addr writing mxc-config.json / trimmed-policy.yaml / loss-report.json Signed-off-by: Giedrius Burachas <gburachas@nvidia.com> (cherry picked from commit 34d54ad) Signed-off-by: Jamie King <jamiek@nvidia.com>
Verified against the real wxc-exec 0.6.0-alpha via --dry-run: MXC accepts
only the {localhost: N} proxy shape (the form the design doc specifies)
and rejects {host, port} with a parse error. Schema 0.6.0-alpha can
express only a loopback port, so non-127.0.0.1 redirect addresses are now
rejected: split_policy emits an error loss (no proxy block) and the driver
refuses egress_proxy_addr values off 127.0.0.1. Per-sandbox attribution
must use per-sandbox ports until the schema widens.
Signed-off-by: Giedrius Burachas <gburachas@nvidia.com>
(cherry picked from commit edde8d5)
Signed-off-by: Jamie King <jamiek@nvidia.com>
… variants
Empirical contract finding from the real test lane (build 26300.8553,
wxc-exec 2026-06-10): the stop and deprovision experimental blocks are
unit variants in the wxc-exec schema and must serialize as null; sending
{} is rejected with malformed_request (invalid type: map, expected unit),
while provision/start accept maps. The production invoker, the real-lane
test, the probe script, and the e2e runner all sent {} - the driver could
provision and run an agent but never stop or delete an isolation-session
sandbox against this build. Pinned by a unit test.
Signed-off-by: Giedrius Burachas <gburachas@nvidia.com>
(cherry picked from commit 0df39ca)
Signed-off-by: Jamie King <jamiek@nvidia.com>
… guard Three-quadrant, table-driven matrix (38 tests): mappable fields assert exact MXC output; every OpenShell field MXC cannot express asserts a loss item with the expected severity (and seam rejection on error); an empty policy asserts the restrictive default-deny posture for every MXC knob OpenShell does not control. The handled_fields_inventory drift guard serializes a fully-populated policy and compares its YAML keys against the mapper-handled field lists, so a new openshell-policy field fails the suite until consciously mapped, delegated, or reported as loss. Re-exports the policy seam types for integration tests; adds serde_yml, base64, serde_json as dev-dependencies. Signed-off-by: Giedrius Burachas <gburachas@nvidia.com> (cherry picked from commit 91807f9) Signed-off-by: Jamie King <jamiek@nvidia.com>
Add MxcComputeConfig.agent_env: each entry is either KEY=VALUE (verbatim) or a bare KEY resolved from the gateway host environment at launch, keeping secrets (e.g. inference API keys) out of the config file. Wire it into the agent process so gateway-launched agents can authenticate to cloud endpoints (process.env was previously hardcoded empty). Unit-tested via resolve_agent_env_passthrough_and_host_lookup. Also add a gateway-driven cloud-inference (T1) test harness: mxc-inference.toml (agent_env + curl agent), inference.yaml policy, and run-inference-test.ps1 which starts the gateway, creates an isolation_session sandbox, runs an authenticated Nemotron call, and bundles redacted results. Documented agent_env in mxc-gateway.toml. Validated end-to-end on the test box (chat HTTP 200 + completion via the gateway). (cherry picked from commit 94d9e82) Signed-off-by: Jamie King <jamiek@nvidia.com>
Replace std::env::{set,remove}_var (unsafe + racy under parallel test
execution in edition 2024) with a read-only PATH lookup. Preserves all
three behaviors under test and drops the #[allow(unsafe_code)].
(cherry picked from commit ac5766e)
Signed-off-by: Jamie King <jamiek@nvidia.com>
The MXC driver crate was authored on GitLab against an earlier proto/core API. Adapt it to the API on GitHub main: - build_capabilities_response no longer takes supports_interactive_session - DriverSandboxSpec.gpu (bool) is now resource_requirements; detect GPU via effective_driver_gpu_count(driver_gpu_requirements(..)) - DriverSandbox gained a `workspace` field - SandboxPolicy gained `network_middlewares`: pass it through the proxy split, emit a loss item on the coarse MXC path, and account for it in the mapper drift-guard test Verified: cargo check + 75 mock-based tests pass (lib 27, examples 10, policy_mapper_matrix 38). Signed-off-by: Jamie King <jamiek@nvidia.com>
Register openshell-driver-mxc as the Windows-only in-process compute backend so compute_driver = "mxc" resolves to a working runtime: - ComputeRuntime::new_mxc, adapted to the current 11-arg from_driver - mxc_policy_sink A1 side channel, staged in create_sandbox before dispatch - mxc_config_from_context loader and the Mxc dispatch arm (Windows constructs; other targets return an explicit "Windows-only" error) - Windows-gated openshell-driver-mxc dependency - Mxc arms for the telemetry, config-file required-fields, and CLI reserved-builtin matches to keep them exhaustive/correct Verified with cargo check --workspace --features openshell-prover/bundled-z3 on x86_64-pc-windows-msvc, stacked on PR NVIDIA#2496. Signed-off-by: Jamie King <jamiek@nvidia.com>
…2496 base Signed-off-by: Jamie King <jamiek@nvidia.com>
- tests/wxc_exec_real.rs: ignored-by-default integration tests against a real wxc-exec. Six --dry-run contract tests run wherever the binary exists (they caught the network.proxy shape mismatch); enforcement tests (processcontainer default-deny positive/negative, isolation session lifecycle round trip with a deprovision drop-guard) probe the backend and SKIP with a recorded reason where it is not live. - examples/probe-mxc-host.ps1: classifies a host (OS build, --probe, per-backend trial) and emits a JSON capability verdict. - examples/run-mxc-e2e.ps1 + e2e-policies/: scenario runner generalizing run-demo.ps1 (fs-rw, fs-readonly, fs-default-deny-empty, network-policy-rejected) with PASS/FAIL/SKIP gating and a stale OPENSHELL_MXC_MOCK_WXC guard in real mode. - tasks/windows.toml: windows:test:mxc-real:x64, windows:e2e:mxc, windows:e2e:mxc:mock. Signed-off-by: Giedrius Burachas <gburachas@nvidia.com> (cherry picked from commit 49afafe) Signed-off-by: Jamie King <jamiek@nvidia.com>
|
All contributors have signed the DCO ✍️ ✅ |
|
I have read the DCO document and I hereby sign the DCO. |
drew
left a comment
There was a problem hiding this comment.
gator-agent
PR Review Status
Validation: Project-valid implementation of merged RFC 0013 and the native-Windows foundation in #2496.
Head SHA: 7beadcbaa26e4f08b854b58f2bfef526a02e8609
Base SHA: f24a5aee1390115fbdba7164f158d1491adcda28
Merge base SHA: dd2b4e3bc0688bdd59f90030f7c1d52511d6e354
Patch ID: 86ddd0be6113b95a1aa583c429c0e6360cb7084a
Gator payload: 4
Review mode: initial
Previous reviewed SHA: none
Review budget exhausted: no
Maintainer decision required: no
Blocking findings:
GATOR-7beadcba-01(Critical): the implicit MXC backend is documented as unable to enforce default-deny filesystem policy.GATOR-7beadcba-02(Critical):share_dirsilently adds a writable host path outsideSandboxPolicy.GATOR-7beadcba-03(Critical): governed egress is configured without binding the trimmed policy to an enforcing proxy.GATOR-7beadcba-04(Critical): stop/delete can report success while execution or cleanup remains active, then discard cleanup identity.GATOR-7beadcba-05(Warning): piped child output is never drained and can deadlock sandbox completion.GATOR-7beadcba-06(Warning): joiningagent_commandloses Windows argv boundaries.GATOR-7beadcba-07(Warning): deterministic policy-mapping failures occur after CreateSandbox has already returned success.
Carried findings:
- None.
Non-blocking suggestions:
- None.
Docs: Missing required Fern documentation for the new user-facing MXC driver setup and [openshell.drivers.mxc] gateway configuration. Please update the relevant page under docs/ and docs/index.yml navigation if needed, or obtain a maintainer-authored explanation that published docs are intentionally unnecessary.
Next state: gator:in-review
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] | ||
| #[serde(rename_all = "snake_case")] | ||
| pub enum MxcBackend { | ||
| #[default] |
There was a problem hiding this comment.
gator-agent
Critical — GATOR-7beadcba-01
Invariant: A supported compute-driver default must preserve OpenShell's default-deny filesystem policy; selecting the driver without an explicit backend must not choose a backend documented as unable to enforce that policy (CWE-284).
Prerequisite: An operator enables the MXC driver and omits the optional backend setting; sandboxed agent code attempts to access a host path absent from filesystem_policy.
Entry point → sink: The documented [openshell.drivers.mxc] gateway configuration with its default backend, followed by the normal sandbox create path. → WxcExecInvoker::provision starts an isolation_session with grant lists but no deny primitive.
Base → head: At the merge base, MXC was not a supported compute driver, so this non-enforcing default could not be selected. → MxcBackend derives Default with IsolationSession selected even though the adjacent contract states that isolation_session is not default-deny and an out-of-policy write may succeed.
Impact: A malicious or compromised agent can access host filesystem locations excluded by SandboxPolicy while the gateway reports that it is running as an OpenShell sandbox.
Reproducer: Configure drivers=mxc with agent_command but no backend, create a sandbox whose filesystem_policy grants only C:\work\allowed, and run an agent that writes C:\Windows\Temp\outside.txt; on a live isolation_session host, verify that the ungranted write can succeed.
PR ownership: This PR introduces the Windows MXC runtime, explicitly identifies isolation_session's missing deny primitive, and nevertheless makes it the implicit supported default.
Requested change: Default to process_container, or reject isolation_session sandbox creation whenever the requested policy relies on default-deny unless the project defines an explicit, clearly unsafe opt-in contract approved by maintainers.
Sibling sites covered by this finding:
- crates/openshell-driver-mxc/src/driver.rs:37 documents that the selected default is not default-deny.
- crates/openshell-driver-mxc/src/driver.rs:102 installs MxcBackend::default() into default gateway configuration.
- crates/openshell-driver-mxc/src/driver.rs:632 provisions that backend with the translated grants.
| if let Some(dir) = &ctx.share_dir { | ||
| let norm = normalize_path(dir); | ||
| if !readwrite.contains(&norm) { | ||
| readwrite.push(norm); |
There was a problem hiding this comment.
gator-agent
Critical — GATOR-7beadcba-02
Invariant: MXC filesystem grants must be derived from SandboxPolicy; driver configuration must not silently add a writable host path outside the policy (CWE-732, CWE-863).
Prerequisite: The gateway's share_dir differs from the sandbox policy's read_write entries, and sandboxed agent code accesses share_dir.
Entry point → sink: The documented share_dir gateway setting combined with the standard sandbox create API and filesystem_policy. → EmbeddedPolicyMapper appends share_dir directly to the MXC readwritePaths grant list.
Base → head: The merge base had no MXC driver and no driver-configured path capable of widening a sandbox's filesystem policy. → Every non-empty share_dir is granted read-write even when it is absent from filesystem_policy; correctness depends only on documentation asking operators to keep two independent values identical.
Impact: A configuration mismatch becomes a policy bypass, allowing an agent to modify an arbitrary operator-selected host directory that the sandbox policy explicitly did not grant.
Reproducer: Set share_dir=C:\sensitive, create a process_container sandbox whose filesystem_policy grants only C:\work, and inspect the generated one-shot config or attempt a write to C:\sensitive; readwritePaths contains C:\sensitive and the write is admitted.
PR ownership: The unconditional extra grant is newly implemented by this PR and materially broadens the policy-to-MXC translation.
Requested change: Remove the implicit share_dir grant. If the demo requires it, require the normalized path to already be covered by filesystem_policy.read_write and reject creation with invalid_argument when it is not.
Sibling sites covered by this finding:
- crates/openshell-driver-mxc/src/driver.rs:571 passes the independent gateway share_dir into policy mapping.
- crates/openshell-driver-mxc/src/policy.rs:185 describes the assumption that the two values happen to match.
| { | ||
| let mut reg = registry.lock().await; | ||
| if let Some(entry) = reg.get_mut(&sandbox_id) { | ||
| entry.trimmed_policy = trimmed_policy; |
There was a problem hiding this comment.
gator-agent
Critical — GATOR-7beadcba-03
Invariant: Enabling governed egress must fail closed unless the configured proxy is actually bound to the sandbox's trimmed policy; retaining policy in an unused registry field is not enforcement (CWE-693, CWE-284).
Prerequisite: An operator enables egress_proxy for process_container and supplies a reachable loopback CONNECT proxy; sandboxed agent code issues an outbound request outside its network policy.
Entry point → sink: The documented egress_proxy and egress_proxy_addr gateway settings with a sandbox network_policies block. → MXC redirects traffic to the configured loopback proxy, while the translated policy is only stored in SandboxEntry and has no consumer.
Base → head: The merge base did not expose MXC governed-egress configuration or claim to delegate MXC network policy to a host proxy. → The mapper accepts otherwise unrepresentable network policy, emits a proxy redirect, and stores trimmed_policy locally; repository references show no code that starts, configures, or updates a CONNECT proxy from that field.
Impact: With no listener, all egress fails despite successful sandbox creation. With a generic listener at the configured address, requests can be forwarded without the declared host, port, binary, middleware, or L7 restrictions, violating the advertised default-deny policy boundary.
Reproducer: Start a permissive CONNECT proxy on 127.0.0.1:18080, enable egress_proxy with that address, create a sandbox policy allowing only api.example.com:443, and request a different destination; inspect that the only policy handoff is the unused registry field and the permissive proxy receives the request without a policy update.
PR ownership: This PR adds the public egress feature and suppresses network mapping losses on the premise that a host proxy enforces the retained policy, while its own README states that host proxy integration is a future consumer.
Requested change: Reject egress_proxy=true until the driver owns or registers a fail-closed proxy instance and atomically installs the per-sandbox trimmed policy before redirecting traffic; alternatively remove the public option from this PR.
Sibling sites covered by this finding:
- crates/openshell-driver-mxc/src/driver.rs:622 emits the effectful MXC network redirect.
- crates/openshell-driver-mxc/src/policy_map/map.rs:114 removes filesystem state and treats network rules as delegated.
- crates/openshell-driver-mxc/src/driver.rs:132 is the only runtime storage for trimmed_policy.
| } | ||
|
|
||
| let mut registry = self.registry.lock().await; | ||
| if registry.remove(sandbox_id).is_some() { |
There was a problem hiding this comment.
gator-agent
Critical — GATOR-7beadcba-04
Invariant: A successful stop or delete must prevent further workload execution and must retain cleanup identity when backend teardown fails (CWE-404, CWE-672).
Prerequisite: An operator stops or deletes a running process_container sandbox, deletes or stops either backend while asynchronous startup is in progress, or encounters an isolation_session deprovision failure.
Entry point → sink: The normal StopSandbox and DeleteSandbox RPCs, including openshell sandbox stop/delete. → The detached lifecycle task continues to provision/start/exec, process_container children are never terminated, and delete removes the registry entry even after deprovision failure.
Base → head: The merge base had no MXC lifecycle and could not report an MXC sandbox deleted while its workload or isolation session remained live. → Stop only invokes the isolation-session stop operation when an ID is already present and otherwise marks the entry stopped. Delete similarly acts only on an already-published isolation ID, does not terminate process_container children, ignores stop/deprovision failures, removes the entry, and has no cancellation handshake with run_lifecycle.
Impact: Agent code can continue running after the API reports it stopped or deleted; an immediate delete can orphan a newly provisioned single-session backend, and a failed deprovision loses the only ID needed to retry cleanup.
Reproducer: Use a blocking mock invoker: call create_sandbox, pause provision or run_oneshot, call delete_sandbox and assert deleted=true, then release the invoker and observe spawn_exec/run_oneshot execute. Separately, run a long process_container command, call stop, and verify the child remains alive; make deprovision return an error and verify the registry still reports deletion.
PR ownership: This PR introduces the detached MXC state machine and its stop/delete implementations without cancellation, child termination, or retryable teardown state.
Requested change: Add a per-entry cancellation/termination mechanism checked across every awaited lifecycle transition; terminate and await the process_container wxc-exec child on stop/delete; serialize teardown with startup; and retain the entry plus return an error when stop or deprovision fails so cleanup can be retried.
Sibling sites covered by this finding:
- crates/openshell-driver-mxc/src/driver.rs:447 skips backend termination when startup has not published iso_sandbox_id.
- crates/openshell-driver-mxc/src/driver.rs:456 reports Stopped without cancelling run_lifecycle or killing a process_container child.
- crates/openshell-driver-mxc/src/driver.rs:489 ignores deprovision failure before forgetting the session.
- crates/openshell-driver-mxc/src/driver.rs:632 and crates/openshell-driver-mxc/src/driver.rs:670 launch work without checking whether stop/delete occurred.
| .arg(&b64) | ||
| .arg("--experimental") | ||
| .stdin(std::process::Stdio::null()) | ||
| .stdout(std::process::Stdio::piped()) |
There was a problem hiding this comment.
gator-agent
Warning — GATOR-7beadcba-05
Invariant: Child stdout and stderr configured as pipes must be drained concurrently; waiting on an undrained child must not allow bounded pipe buffers to deadlock the sandbox (CWE-400).
Prerequisite: The configured agent emits enough stdout or stderr to fill a Windows pipe buffer, which is normal for a logging agent and controllable by sandboxed code.
Entry point → sink: Any MXC sandbox using agent_command on either supported backend. → monitor_exec waits for wxc-exec to exit without reading either piped stream.
Base → head: The merge base had no MXC child process or undrained output pipes. → Both real invocation paths set stdout and stderr to Stdio::piped, return only Child, and later call Child::wait without taking or draining the handles.
Impact: A sufficiently chatty agent or wxc-exec process blocks indefinitely on output, never reaches its intended exit, and leaves the sandbox incorrectly Ready with resources held.
Reproducer: Configure agent_command to write several megabytes to stdout and stderr before exiting, create a sandbox, and observe that monitor_exec never reaches AgentCompleted once the pipe buffer fills.
PR ownership: The PR introduces both piped spawn sites and the wait-only monitor.
Requested change: Either inherit/null the streams according to the intended logging contract or take and drain both streams concurrently before awaiting final completion; add a bounded-output regression test.
Sibling sites covered by this finding:
- crates/openshell-driver-mxc/src/mxc.rs:596 repeats the undrained-pipe configuration for process_container.
- crates/openshell-driver-mxc/src/driver.rs:729 waits without consuming child.stdout or child.stderr.
| // implicit. processContainer enforces that at the OS level regardless. | ||
| denied_paths: Vec::new(), | ||
| }; | ||
| let command_line = config.agent_command.join(" "); |
There was a problem hiding this comment.
gator-agent
Warning — GATOR-7beadcba-06
Invariant: The documented agent_command array must preserve Windows argv boundaries when converted to MXC's commandLine string.
Prerequisite: An operator configures an executable path or argument containing whitespace, quotes, a trailing backslash, or an empty argument.
Entry point → sink: The documented agent_command array in [openshell.drivers.mxc]. → run_lifecycle concatenates array elements with a single space before wxc-exec/CreateProcess parses the resulting command line.
Base → head: The merge base had no MXC agent_command configuration. → Structured command elements are flattened with Vec::join and no Windows command-line quoting, so the resulting argv differs from the configured array.
Impact: Common Windows paths such as C:\Program Files\Agent\agent.exe and arguments such as "hello world" are split or misparsed, causing the wrong executable or arguments to run and making supported configuration unreliable.
Reproducer: Set agent_command to ["C:\Program Files\Agent\agent.exe", "hello world", ""] and capture the generated process.commandLine; parse it with CommandLineToArgvW-compatible rules and verify it does not round-trip to the input vector.
PR ownership: This PR defines agent_command as Vec and introduces the lossy conversion used for every MXC launch.
Requested change: Encode each element with a tested Windows command-line quoting routine that round-trips under the MXC/CreateProcess parser, or expose a single explicitly documented command_line string instead of promising argv semantics.
| let policy_mapper = self.policy_mapper.clone(); | ||
| let sandbox = sandbox.clone(); | ||
|
|
||
| tokio::spawn(async move { |
There was a problem hiding this comment.
gator-agent
Warning — GATOR-7beadcba-07
Invariant: Policy translation failures discovered before any MXC side effect must be returned by CreateSandbox rather than converting an invalid request into an asynchronously failed persisted sandbox.
Prerequisite: A user submits a supported sandbox create request containing policy the selected MXC mode cannot enforce, such as network_policies while governed egress is disabled.
Entry point → sink: The gateway CreateSandbox API and openshell sandbox create command. → MxcComputeBackend returns CreateSandbox success after spawning run_lifecycle; the mapper later records ProvisionFailed instead of returning invalid_argument.
Base → head: At the merge base MXC was unsupported, so the gateway could not acknowledge an MXC create request whose policy had already made it invalid. → validate_sandbox_create cannot see the staged policy, create_sandbox starts a detached task and immediately returns Ok, and the deterministic mapper rejection occurs only inside that task after the gateway has persisted the sandbox.
Impact: The API reports successful creation and leaves a failed sandbox object requiring cleanup, contradicting the documented invalid_argument behavior and making automation unable to distinguish request errors from backend failures at create time.
Reproducer: Stage a policy containing one network rule with egress_proxy=false, invoke ComputeDriverService::create_sandbox, and assert that the RPC returns Ok while a later watch event changes the entry to Ready=False/ProvisionFailed.
PR ownership: The PR introduces the out-of-band policy channel and deliberately defers its only semantic validation until after the CreateSandbox response boundary.
Requested change: Consume and map the staged policy synchronously inside create_sandbox before registry insertion or task spawn, return invalid_argument for MapError::Unsupported, and only detach the effectful MXC lifecycle after validation succeeds.
Sibling sites covered by this finding:
- crates/openshell-driver-mxc/src/driver.rs:432 returns success before mapping completes.
- crates/openshell-driver-mxc/src/driver.rs:578 performs the deterministic policy validation asynchronously.
- crates/openshell-server/src/compute/mod.rs:988 stages the policy before the in-process driver call, so synchronous validation is available without changing the protobuf contract.
| # agent_env = [ | ||
| # "NV_API_KEY", # resolved from the gateway host env | ||
| # "OPENAI_BASE_URL=https://integrate.api.nvidia.com/v1", | ||
| # ] |
There was a problem hiding this comment.
generally we configure these from sandbox commands rather than the gateway. i think we should remove this for now.
| agent_command = [ | ||
| "C:/work/openshell-mxc-demo/mxc-demo-agent.exe", | ||
| "C:/work/openshell-mxc-demo", | ||
| "C:/Windows/Temp/openshell-mxc-out-of-policy.txt", | ||
| ] |
There was a problem hiding this comment.
why does this need to be configurable from the gateway?
| # agent_command = [ | ||
| # "powershell", | ||
| # "-NoProfile", | ||
| # "-Command", | ||
| # "Set-Content -Path 'C:/work/openshell-mxc-demo/hello.txt' -Value 'hello from mxc'", | ||
| # ] |
There was a problem hiding this comment.
same question as above. generally this is configured from the sandbox create command
There was a problem hiding this comment.
whats this example for? shouldn't the driver itself handle this?
| // before dispatch. The driver removes/consumes it in create_sandbox. The | ||
| // proto driver contract has no policy field, so this is the only path. | ||
| #[cfg(target_os = "windows")] | ||
| if let Some(sink) = &self.mxc_policy_sink |
There was a problem hiding this comment.
[P1] Propagate policy updates after sandbox creation
This side channel installs the policy only for CreateSandbox. Later sandbox policy updates persist a new revision and notify SandboxWatchBus, but the MXC backend neither consumes that bus nor exposes an update path; its mapped grants and stored trimmed policy therefore remain at the creation-time version. Tightening a running sandbox policy can be reported as successful while the MXC workload continues under the older, broader policy. Please either apply updates to MXC fail-closed or reject policy updates for MXC sandboxes until they can be enforced.
| let registry = self.registry.lock().await; | ||
| registry.values().map(|e| e.sandbox.clone()).collect() | ||
| }; | ||
| let mut broadcast_rx = self.watch_tx.subscribe(); |
There was a problem hiding this comment.
[P2] Subscribe before releasing the snapshot lock
The comment says the subscription prevents missed events, but subscribe happens after the registry lock is released. A lifecycle update in that interval is absent from the snapshot and published before this receiver exists, so the gateway can miss the transition permanently. Subscribe while still holding the registry lock, then capture the snapshot, so every update is represented by either the snapshot or the live stream.
| use crate::defaults::LocalTlsPaths; | ||
| use openshell_core::{Error, Result}; | ||
| #[cfg(target_os = "windows")] | ||
| use openshell_core::ComputeDriverKind; |
There was a problem hiding this comment.
[P2] Restore a rustfmt-clean import order
cargo fmt --all -- --check fails on this import block, and also reports ordering drift in compute/mod.rs. Please run cargo fmt and commit the resulting import ordering so the required pre-commit formatting check passes.
| | ComputeDriverKind::Podman | ||
| | ComputeDriverKind::Kubernetes | ||
| | ComputeDriverKind::Vm | ||
| | ComputeDriverKind::Mxc |
There was a problem hiding this comment.
[P2] Include MXC in the local-driver mTLS default
MXC is now reserved as an in-process built-in here, but it is omitted from is_singleplayer_driver below. With the normal local TLS bundle, Docker, Podman, and VM automatically enable mTLS user authentication while MXC does not, so selecting MXC unexpectedly changes the gateway authentication default. Please include ComputeDriverKind::Mxc in is_singleplayer_driver and add the corresponding default-resolution test.
|
/ok to test 7beadcb |
Summary
Adds
openshell-driver-mxc, a native-Windows compute driver backed by Microsoft Execution Containers (MXC /wxc-exec), and wires it intoopenshell-serverbehindComputeDriverKind::Mxc. This lets OpenShell run governed agent sandboxes on Windows with filesystem and network policy enforcement, without touching the Linux container drivers. Builds on the Windows build-enablement foundation merged in #2496.Related Issue
Part of RFC 0013 — Native Windows Support via MXC (merged in #2071). Stacks on the Windows build-enablement work in #2496. This is PR 1 (the driver) of the multi-PR native-Windows effort.
Changes
openshell-driver-mxc: in-process implementation of theComputeDrivergRPC contract over MXC /wxc-exec. Backends:process_container(default-deny AppContainer) andisolation_session.SandboxPolicy→ MXC grants (filesystem, network/egress, binary-scoped,network_middlewares) directly in the driver crate.ComputeDriverKind::Mxcdispatch inopenshell-server(Windows-gated),mxc_config_from_contextconfig plumbing, and telemetry / CLI / config-validation arms.openshell-driver-mxcis pulled in under[target.'cfg(target_os = "windows")'.dependencies], complementary to the conditional Linux-driver exclusion from feat(gateway,cli): windows compilation support #2496.wxc-exectests (no mocks) plus e2e policy fixtures.Testing
Built
x86_64-pc-windows-msvc(release,bundled-z3) and validated on a real Windows 11 host (7F203-MXC-001, build 26300.x):Filesystem enforcement — DEMO PASS (
process_container): in-policy write + read-back OK; out-of-policy write denied (PermissionDenied).Network policy — PASS: disallowed egress →
ProvisionFailed: policy(port/protocol/access/binary-scoped grant checks).MXC crate unit tests: 75 passed / 0 failed / 9 ignored.
windows-msvc.ps1 check x86_64-pc-windows-msvc: PASS.[]
mise run pre-commitpassesUnit tests added/updated
E2E tests added/updated (if applicable)
Checklist