Skip to content

User/shschaefer/rust lib#635

Merged
shschaefer merged 12 commits into
mainfrom
user/shschaefer/rust_lib
Jul 13, 2026
Merged

User/shschaefer/rust lib#635
shschaefer merged 12 commits into
mainfrom
user/shschaefer/rust_lib

Conversation

@shschaefer

@shschaefer shschaefer commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

📖 Description

This PR begins the work necessary to create SDKs for multiple languages. We refactor MXC so the executor binaries become thin CLI shells over a single execution library, then prove that boundary by adding a C ABI and a C# SDK that binds to it.

Previously each executor ( wxc-exec ,  lxc-exec ,  mxc-exec-mac ) carried its own  match request.containment  backend-selection logic, and the public Rust SDK ( mxc-sdk ) was a provisional streaming-only wrapper that duplicated dispatch. This PR consolidates all dispatch into one engine and layers the public surfaces on top.

New crates

•  core/mxc_engine  (internal) — the single home for "given an  ExecutionRequest , run it": run-to-completion selection over all backends (incl. the Windows ProcessContainer BaseContainer/AppContainer BFS/DACL fallback tiers and every experimental backend), streaming  spawn , state-aware lifecycle dispatch, host probing, and policy/config building. Absorbs the logic previously duplicated across the executors and the old  mxc-sdk  internals.
•  ffi/mxc_ffi  ( cdylib / staticlib ) — a flat, panic-safe C ABI over  mxc-sdk .  mxc_run(policyJson, command, out)  runs a sandbox to completion and returns captured stdout/stderr + exit outcome via a  #[repr(C)] MxcRunResult . Every entry point is  catch_unwind -wrapped; JSON in, bytes+status out. A  build.rs  runs csbindgen to generate the C# P/Invoke layer.

Refactors

•  mxc-sdk  is now a thin public facade re-exporting the engine, and gains  run()  (run-to-completion capture) alongside  spawn_sandbox()  (live streaming).
•  wxc-exec  /  lxc-exec  /  mxc-exec-mac  reduced to arg-parse + config-load + maintenance modes + delegate to  mxc_engine ; no backend  match  remains in any binary. The  ContainmentBackend → CommandLineContext  mapping moved into  wxc_common::cmdline  as the single source of truth.

New C# SDK ( csharp/ )

•  Microsoft.Mxc.Sdk  —  MxcSandbox.Run/RunAsync  →  RunResult , policy POCOs mirroring the Rust wire shape,  MxcException / ErrorCode , and a dev/NuGet native-library resolver. Plus a console sample and xUnit tests, wired into a solution.

🔗 References


Follow phase will add streaming and state-awareness over the FFI and through the C# SDK.

🔍 Validation

Rust (Windows host):

•  cargo fmt --all -- --check ,  cargo clippy --workspace --all-targets -- -D warnings 
•  cargo test  for  mxc_engine ,  mxc-sdk ,  mxc_ffi ,  wxc ,  lxc ,  mxc_darwin ,  wxc_common  — all pass
•  cargo test -p wxc_e2e_tests  — one-shot + state-aware + experimental (microvm/hyperlight/windows-sandbox) suites pass; host-prep-gated cases  #[ignore] d
• Live:  wxc-exec basic_processcontainer.json -- cmd /c echo …  runs through the engine; experimental-gate + state-aware  provision  error envelopes verified

Cross-platform (Windows→Linux  cargo check , no-link):  mxc_engine  (incl.  microvm ),  mxc-sdk ,  wxc_common ,  mxc_ffi  type-check clean; engine  clippy  for the Linux target clean. macOS/Hyperlight-feature arms are faithful mirrors of already-compiling code (toolchains unavailable on the dev host).

C# (.NET 8):  dotnet test  on the solution — 5/5 pass (native load, serialization, error mapping); the sample ran a real sandbox end-to-end from C# ( hello from MXC , exit 0), exercising C# → P/Invoke →  mxc_ffi.dll  →  mxc-sdk  → engine → ProcessContainer.

CI gates (new):  check-csharp-errorcode-parity.js  (16 codes),  check-csharp-bindings-codegen.js  (csbindgen drift), and the extended  check-version-sync.js  (Cargo ↔ npm ↔ C# = 0.7.0) — all pass. Existing TS SDK contract unchanged.

✅ Checklist

📋 Issue Type

  • Bug fix
  • Feature
  • Task

GitHub Actions runs the PR validation build automatically. The ADO pipeline
(MXC-PR-Build) is the Azure version of the PR pipeline, kept in parity with the GitHub
Actions build; it runs on merge to main, and Microsoft reviewers with write access can trigger it
on a PR with /azp run. See docs/pull-requests.md.

If the dependency-feed-check check fails on a new dependency, the crate must be added to
the feed before the PR can pass. See docs/pull-requests.md
for the steps.

Microsoft Reviewers: Open in CodeFlow

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR lays the groundwork for multi-language MXC SDKs by centralizing backend dispatch/execution into a single Rust “engine” crate, then layering a C ABI and a new C# SDK on top. It refactors the existing executor binaries and the Rust mxc-sdk to delegate dispatch to the engine, proving the shared boundary.

Changes:

  • Introduce mxc_engine as the shared execution/dispatch/probing/policy-building layer used by mxc-sdk and executors.
  • Add mxc_ffi (cdylib/staticlib) exposing a panic-safe C ABI and generating C# P/Invoke bindings via csbindgen.
  • Add a new csharp/ SDK and CI drift/version parity scripts for C# ↔ native bindings.
Show a summary per file
File Description
src/ffi/mxc_ffi/tests/ffi.rs Integration tests for the C ABI surface
src/ffi/mxc_ffi/src/lib.rs C ABI: mxc_run, result/free helpers, version
src/ffi/mxc_ffi/Cargo.toml New FFI crate definition + csbindgen build-dep
src/ffi/mxc_ffi/build.rs Generates checked-in C# P/Invoke bindings
src/core/wxc/src/main.rs Refactor: delegate one-shot/state-aware dispatch to engine
src/core/wxc/Cargo.toml Depend on mxc_engine; forward feature flags
src/core/wxc_common/src/cmdline.rs Centralize backend→commandline-context mapping
src/core/mxc-sdk/tests/sandbox.rs Tests for new mxc_sdk::run convenience
src/core/mxc-sdk/src/lib.rs Re-export engine surface; add run() API
src/core/mxc-sdk/README.md Docs updated for run() and engine relationship
src/core/mxc-sdk/Cargo.toml Make mxc-sdk depend on mxc_engine
src/core/mxc_engine/src/state_aware.rs Engine-owned state-aware dispatch entry point
src/core/mxc_engine/src/run.rs Engine-owned run-to-completion backend resolution/execution
src/core/mxc_engine/src/policy.rs Doc updates to reference spawn API
src/core/mxc_engine/src/platform.rs Update prose: probing now lives in engine
src/core/mxc_engine/src/lib.rs New engine crate public surface (spawn/run/probe/policy)
src/core/mxc_engine/src/error.rs Engine error facade over wxc_common errors
src/core/mxc_engine/src/dispatch.rs Streaming backend dispatch implementation moved/updated
src/core/mxc_engine/Cargo.toml New engine crate definition + feature wiring
src/core/mxc_darwin/src/main.rs Refactor: delegate run-to-completion to engine
src/core/mxc_darwin/Cargo.toml Depend on mxc_engine instead of seatbelt directly
src/core/lxc/src/main.rs Refactor: delegate run-to-completion to engine
src/core/lxc/Cargo.toml Depend on mxc_engine; forward feature flags
src/Cargo.toml Add core/mxc_engine and ffi/mxc_ffi to workspace
src/Cargo.lock Lockfile updates (incl. csbindgen, new crates)
scripts/check-version-sync.js Enforce Cargo ↔ npm ↔ C# version parity
scripts/check-csharp-errorcode-parity.js CI gate: C# ErrorCode matches native status constants
scripts/check-csharp-bindings-codegen.js CI gate: regenerate and diff C# P/Invoke bindings
microvm-perf-results.json Added perf results JSON file at repo root
csharp/README.md New C# SDK docs and usage guidance
csharp/Microsoft.Mxc.Sdk/SandboxPolicy.cs C# policy POCOs with JSON annotations
csharp/Microsoft.Mxc.Sdk/RunResult.cs C# run-to-completion result model
csharp/Microsoft.Mxc.Sdk/Native/NativeMethods.g.cs Generated P/Invoke layer from csbindgen
csharp/Microsoft.Mxc.Sdk/Native/NativeLibraryResolver.cs Native library resolution for dev/NuGet layouts
csharp/Microsoft.Mxc.Sdk/MxcSandbox.cs Public C# entrypoint wrapping the native ABI
csharp/Microsoft.Mxc.Sdk/MxcException.cs Typed exception mapping native codes
csharp/Microsoft.Mxc.Sdk/Microsoft.Mxc.Sdk.csproj New SDK project file + runtime assets packing
csharp/Microsoft.Mxc.Sdk/ErrorCode.cs C# enum mirroring native status codes
csharp/Microsoft.Mxc.Sdk.Tests/MxcSandboxTests.cs xUnit tests for load path + error mapping + JSON
csharp/Microsoft.Mxc.Sdk.Tests/Microsoft.Mxc.Sdk.Tests.csproj Test project definition
csharp/Microsoft.Mxc.Sdk.slnx Solution definition including sample/tests/sdk
csharp/Microsoft.Mxc.Sdk.Sample/Program.cs Console sample wiring policy→run→print
csharp/Microsoft.Mxc.Sdk.Sample/Microsoft.Mxc.Sdk.Sample.csproj Sample project definition
csharp/.gitignore Ignore build artifacts + staged runtimes
build.bat Stage mxc_ffi.dll into C# NuGet runtime assets (Windows)
.github/copilot-instructions.md Document new engine/FFI/C# SDK structure & commands

Review details

  • Files reviewed: 44/46 changed files
  • Comments generated: 3
  • Review effort level: Low

Comment thread microvm-perf-results.json Outdated
Comment thread csharp/Microsoft.Mxc.Sdk/Microsoft.Mxc.Sdk.csproj Outdated
Comment thread csharp/.gitignore
@shschaefer

Copy link
Copy Markdown
Collaborator Author

Ran the dependency-feed-check. Needs to pickup csbindgen dependency.

…ature is enabled

The `lxc` crate's `main.rs` calls `hyperlight_common::setup` under
`#[cfg(all(feature = "hyperlight", target_arch = "x86_64"))]`, but
`hyperlight_common` was not declared as a dependency. This caused a
compile error across Hyperlight E2E, Windows x64, Linux x64/lint jobs.

Follow the same pattern as `wxc/Cargo.toml`:
- Add `hyperlight_common` as an optional workspace dep scoped to x86_64
- Update `hyperlight` feature to activate `dep:hyperlight_common` and
  enable its `hyperlight` sub-feature
Comment thread csharp/.gitignore
Comment thread csharp/README.md Outdated
Stuart Schaefer and others added 2 commits July 13, 2026 09:29
mxc_ffi's build.rs unconditionally ran csbindgen to regenerate the C# P/Invoke,
so every whole-workspace build (including the backend feature matrix, e.g.
--features hyperlight) compiled csbindgen and wrote into the source tree.

Make csbindgen an optional build-dependency behind a new `csharpsdk` cargo
feature (off by default). Normal and backend builds no longer compile csbindgen;
only the drift gate (scripts/check-csharp-bindings-codegen.js) opts in via
`cargo build -p mxc_ffi --features csharpsdk` to regenerate the committed
bindings. Updates the C# README and copilot-instructions accordingly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4efbd7c8-05fc-47ac-9a4b-4d92414a822a
…at build

- Remove the committed microvm-perf-results.json CI test-output artifact and
  gitignore microvm-perf-results*.json (root .gitignore).
- Stop committing the generated C# P/Invoke: gitignore *.g.cs and remove
  NativeMethods.g.cs. The csproj's GenerateNativeBindings MSBuild target now
  regenerates it before each compile (cargo build -p mxc_ffi --features
  csharpsdk), and mxc_ffi's build.rs emits rerun-if-changed on the output so a
  missing (gitignored) file reliably re-triggers codegen. The bindings-codegen
  gate is repurposed from a drift check to a generation check (asserts the
  expected entry points are produced).
- Fix the csproj runtimes comment: only build.bat stages the Windows native
  library today; a build stages only its own host platform's mxc_ffi.
- csharp/README.md: describe the binding as a .NET binding (implemented in C#).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4efbd7c8-05fc-47ac-9a4b-4d92414a822a
@shschaefer shschaefer requested a review from MGudgin July 13, 2026 17:29

### C# SDK

- **C# SDK** (`csharp/`, `Microsoft.Mxc.Sdk`) — a managed binding that P/Invokes the native `mxc_ffi` library (which wraps the Rust `mxc-sdk` → `mxc_engine`), rather than spawning an executor. `MxcSandbox.Run(policy, command)` / `RunAsync` run a command to completion and return a `RunResult` (`ExitCode`, `TimedOut`, `Stdout`, `Stderr`); policy POCOs (`SandboxPolicy`, `FilesystemPolicy`, `NetworkPolicy`, `UiPolicy`) serialize to the same camelCase JSON the native layer expects. `MxcException` carries a typed `ErrorCode` that mirrors the native `MXC_STATUS_*` codes (parity-gated by `scripts/check-csharp-errorcode-parity.js`). `Native/NativeMethods.g.cs` is **generated** by csbindgen from the Rust FFI and is **not committed** (gitignored) — the csproj's `GenerateNativeBindings` MSBuild target regenerates it before each C# compile via `cargo build -p mxc_ffi --features csharpsdk`, so a `dotnet build` needs the Rust toolchain on PATH. `NativeLibraryResolver` finds `mxc_ffi` via `MXC_FFI_DIR`, the assembly dir / `runtimes/<rid>/native`, or `src/target/{debug,release}`. Projects: `Microsoft.Mxc.Sdk` (library), `Microsoft.Mxc.Sdk.Sample` (console), `Microsoft.Mxc.Sdk.Tests` (xUnit), in `Microsoft.Mxc.Sdk.slnx`. This first increment exposes run-to-completion only.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

scripts/check-csharp-errorcode-parity.js

This doesn't actually run in CI yet, right?

- `mxc_engine` is the **single execution engine** — the one home for "given an `ExecutionRequest`, run it". It owns: run-to-completion backend selection (`run` / `resolve_runner`, covering **all** backends, incl. the Windows ProcessContainer BaseContainer/AppContainer BFS/DACL fallback tiers via `appcontainer_common::dispatcher::dispatch_with_fallback`, and every experimental backend, feature-gated); streaming (`spawn` → `Box<dyn SandboxProcess>`); state-aware lifecycle dispatch (`run_state_aware`); host probing (`platform_support` / `PlatformSupport`); and config building (`build_request`, `SandboxPolicy` + sections, `available_tools_policy`/`user_profile_policy`/`temporary_files_policy`). It depends on the backend crates (cfg-split: appcontainer/windows_sandbox/isolation_session/wslc/nanvix on Windows, bubblewrap/lxc/nanvix on Linux, seatbelt on macOS) so it can't live in `wxc_common`. Both the executor binaries and `mxc-sdk` call into it. `ResolvedRunner` carries the boxed runner plus (Windows only) the optional `DaclManager` guard, so `wxc-exec` can park the guard for its signal handler.
- `mxc-sdk` is the **public Rust SDK** — a thin facade over `mxc_engine`. Build a `SandboxRequest` with `build_request`, then either `run(request)` (run-to-completion; returns an `Output` with the `WaitOutcome` + captured `stdout`/`stderr`) or `spawn_sandbox(request)` (returns a `Sandbox` handle for live bidirectional stdio — `take_stdin`/`take_stdout`/`take_stderr`, `kill()`, `wait()` returning a `WaitOutcome` (`Exited(i32)` / `TimedOut`) as `io::Result`, or `wait_with_output()`). It re-exports the engine's config-building surface (`build_request`, `mxc_sdk::policy::{SandboxPolicy sections}`, discovery helpers) and `platform_support`; `mod sandbox` (wrapping the engine's `SandboxProcess` in `Sandbox`) is its only local module. No pty is ever allocated. Streaming supports Seatbelt (macOS), Bubblewrap (Linux), and Windows ProcessContainer (AppContainer + BaseContainer); other backends return `ErrorCode::UnsupportedContainment`.
- The lower-level execution surface lives in `wxc_common::sandbox_process`: the `SandboxBackend` trait (`validate` + `spawn(request, logger, StdioMode) -> Box<dyn SandboxProcess>` + a `diagnose_exit` hook) and the generic `Runner<B>` adapter that bridges any `SandboxBackend` to the run-to-completion `ScriptRunner` (via `spawn(StdioMode::Inherit)` then `wait()`). `StdioMode::Pipes` hands the caller live stdin/stdout/stderr (what the `mxc-sdk` streaming path uses); `StdioMode::Inherit` lets the child inherit the host's stdio (what the executor binaries use, preserving the TTY under a pty). `SandboxBackend` is implemented for Seatbelt, Bubblewrap, and Windows ProcessContainer.
- `mxc_ffi` (`ffi/mxc_ffi`, `crate-type = ["cdylib", "staticlib", "lib"]`) is a flat, panic-safe **C ABI over `mxc-sdk`** for language bindings. `mxc_run(policyJson, command, out)` runs a sandbox to completion, filling a `#[repr(C)] MxcRunResult` (status + exit_code + timed_out + owned stdout/stderr/error C strings); every entry point is `catch_unwind`-wrapped so a panic becomes a status code, never an unwind. Its `build.rs` runs **csbindgen** to generate the C# P/Invoke (`csharp/Microsoft.Mxc.Sdk/Native/NativeMethods.g.cs`), gated behind the crate's **`csharpsdk`** feature (off by default, so the whole-workspace backend build matrix doesn't compile csbindgen). The generated file is **not committed** (gitignored); the C# csproj regenerates it at build time and `scripts/check-csharp-bindings-codegen.js` runs the codegen in CI and asserts the expected entry points are produced. The C ABI is **not a stable external contract** (native + binding are co-versioned and generated together; see the crate docs). Currently exposes run-to-completion only; streaming + state-aware over FFI are deferred.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

scripts/check-csharp-bindings-codegen.js

This doesn't actually run in CI yet, right?

@MGudgin MGudgin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

@shschaefer shschaefer merged commit 3777ab8 into main Jul 13, 2026
22 checks passed
@shschaefer shschaefer deleted the user/shschaefer/rust_lib branch July 13, 2026 19:18
caarlos0 added a commit to caarlos0/mxc that referenced this pull request Jul 13, 2026
Integrate upstream microsoft#635 (Rust FFI library + C# SDK), which extracted the
mxc-sdk engine internals (policy/dispatch/platform/error) into a new
internal `mxc_engine` crate and added the `mxc_ffi` crate and `csharp/`
bindings.

Git auto-followed the `mxc-sdk/src/policy.rs` -> `mxc_engine/src/policy.rs`
rename and applied this branch's Seatbelt-proxy edits to the moved file:
the "not supported on macOS" proxy rejection is dropped, the host-rule
flag is renamed to `accepts_host_rules_without_outbound`, and the
`macos_proxy_is_accepted_and_mapped` test moves with it. The ported SDK
helper test keeps using the `mxc_sdk::{build_request, ...}` re-exports,
which now forward to `mxc_engine`.

Workspace members (unix_test_proxy) and copilot-instructions.md sections
from both sides are preserved. Validated: mxc_engine/mxc-sdk/seatbelt_common/
wxc_common/unix_test_proxy/mxc_darwin build + test green (incl. the moved
proxy test), clippy -D warnings clean, non-macOS seatbelt_common cross-check
clean, version-sync OK, SDK build + unit tests green (3 pre-existing
PowerShell-on-macOS failures only).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d3191c12-2deb-452b-8379-3abbcd5eeb68
Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
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.

4 participants