Skip to content

Commit 57765ce

Browse files
committed
fix(mxc): address Windows qualification review comments
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
1 parent 4e67665 commit 57765ce

7 files changed

Lines changed: 182 additions & 2 deletions

File tree

‎.agents/skills/build-openshell-mxc-windows/SKILL.md‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,25 @@ validation; GitHub Actions does not re-run it after the full suite.
370370

371371
## Test Accounting Guidance
372372

373+
For a separate qualified Windows test host, use
374+
`tasks/scripts/test-mxc-remote.ps1 -HostName <host> -UserName <user>`.
375+
It builds locally with the existing Windows tasks, detects native remote
376+
architecture through Windows system configuration (not an emulated SSH shell),
377+
caches uploads by SHA256, and runs the unchanged real E2E harness. Results and
378+
host probes return to `target/windows-remote-results`, including nonzero test
379+
outcomes. The remote host needs SSH, OpenSSL, and MXC, not a compiler toolchain.
380+
Package the matching `libz3.dll` from the target-specific Cargo cache alongside
381+
the gateway: Windows prebuilt Z3 uses an import library, not static linkage.
382+
The `windows:build:*` tasks now stage the pinned DLL, check PE architecture,
383+
reject conflicting cache hashes, and verify the copied hash. Local MXC E2E
384+
requires the adjacent DLL and reports a preflight error if it is missing.
385+
Use `-Z3DllPath` when discovery is ambiguous. The native Visual C++ runtime
386+
must be present on the remote host. Verify DLL architecture and the gateway
387+
loader before E2E; do not count missing-DLL crashes as MXC enforcement failures.
388+
Verify the SSH host key beforehand. Never send the private SSH key or bundle
389+
the harness signing-key directories. `-SkipBuild` is an explicit stale-artifact
390+
opt-in, not evidence that artifacts match current sources. See the MXC driver
391+
README for the command and remote dependency contract.
373392
For MXC validation, also run `windows:e2e:mxc`
374393
after the native release build. The harness uses .NET port discovery, disposable
375394
TOML and CLI registration, and owner-only Ed25519 keys generated by OpenSSL on

‎crates/openshell-driver-mxc/README.md‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,54 @@ not required; driver-owned guest TLS fields are rejected.
135135

136136
## Validation
137137

138+
Windows release build tasks stage the pinned, target-matching `libz3.dll` beside
139+
the gateway and verify its architecture and copied SHA256. Conflicting cached
140+
DLLs fail the build instead of selecting an arbitrary runtime. Local E2E requires
141+
this adjacent DLL; rebuild for the native architecture if preflight reports it
142+
missing.
143+
144+
### Remote Windows host
145+
146+
Build on a Windows MSVC development machine and run the unchanged real E2E
147+
harness on a separate MXC host:
148+
149+
```powershell
150+
.\tasks\scripts\test-mxc-remote.ps1 -HostName 172.16.178.137 -UserName test `
151+
-WxcExecPath 'C:\Tools\MXC\wxc-exec.exe'
152+
```
153+
154+
Install the public SSH key on the target and verify its host key before running.
155+
The script defaults to `~/.ssh/openshell-mxc-test`; override `-IdentityFile` as
156+
needed. Both SSH and SCP must be on PATH locally. The target needs Windows
157+
OpenSSH Server, OpenSSL on its SSH-session PATH, and a qualified MXC runtime,
158+
but does not need Rust, MSVC, mise, or a source checkout.
159+
160+
The script detects native Windows architecture independently of the SSH shell,
161+
uses the existing `windows:build:*` task, and validates the executable PE
162+
architecture before testing. `-BuildDirectory` selects a dedicated Cargo cache.
163+
`-SkipBuild` reuses existing artifacts without claiming they match current
164+
sources. `-Scenario` selects an existing harness scenario.
165+
166+
The package includes `libz3.dll` from the target-specific Cargo build cache.
167+
Windows prebuilt Z3 uses a dynamic runtime DLL. Supply `-Z3DllPath` if multiple
168+
different cached versions exist or when using a custom Z3 build. The remote
169+
host needs the native Visual C++ runtime; the runner validates DLL architecture
170+
and gateway startup before invoking E2E.
171+
172+
The example gateway explicitly enables caller driver config so tests can submit
173+
their workload command and directory. MXC does not implement external-resource
174+
label admission. The temporary outer-fence assertions permit compatibility
175+
testing but do not qualify exclusive network mediation or live revocation.
176+
177+
Uploads are cached by SHA256 and verified before execution. Each run has an
178+
isolated remote directory under `%USERPROFILE%\openshell-mxc-tests`; old runs
179+
are retained. Full logs, host capabilities, source status, artifact hashes, and
180+
the results ZIP return to `target/windows-remote-results` in this checkout.
181+
Signing-key directories are not downloaded. Test failures and incomplete
182+
coverage remain nonzero exits. Missing host-loopback evidence is not a pass,
183+
and a qualified host does not close the outer-fence implementation gaps above.
184+
185+
### Local Windows host
138186
Run the Windows build lane on a native Windows MSVC host:
139187

140188
```powershell

‎crates/openshell-driver-mxc/examples/run-mxc-e2e.ps1‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,9 @@ try {
646646
if (-not (Test-Path $policyDir)) {
647647
throw "e2e-policies/ directory not found at $policyDir"
648648
}
649+
if (-not (Test-Path -LiteralPath (Join-Path $BinaryDir 'libz3.dll') -PathType Leaf)) {
650+
throw "Missing libz3.dll beside the gateway. Re-run windows:build for this architecture, or package the matching Z3 runtime with these binaries."
651+
}
649652

650653
# Capture the pristine TOML once; every scenario renders a fresh copy from this.
651654
$tomlBase = Get-Content $tomlTemplate -Raw

‎crates/openshell-driver-mxc/tests/windows_demo_examples.rs‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,75 @@ if (Test-Path -LiteralPath $cliStateRoot) { throw "temporary state not removed"
165165
);
166166
}
167167
}
168+
169+
#[test]
170+
fn windows_build_stages_only_unambiguous_target_matching_z3() {
171+
// Synthetic PE headers exercise packaging checks, not MXC containment.
172+
let root = tempfile::tempdir().unwrap();
173+
let script = r#"
174+
$ErrorActionPreference = 'Stop'
175+
Set-StrictMode -Version Latest
176+
$tokens = $null
177+
$errors = $null
178+
$ast = [System.Management.Automation.Language.Parser]::ParseFile(
179+
$env:OPENSHELL_BUILD_SCRIPT, [ref]$tokens, [ref]$errors)
180+
if ($errors.Count) { throw "build script parse errors: $($errors.Message -join '; ')" }
181+
$function = $ast.Find({ param($node)
182+
$node -is [System.Management.Automation.Language.FunctionDefinitionAst] -and $node.Name -eq 'Stage-Z3Runtime'
183+
}, $true)
184+
if ($null -eq $function) { throw 'missing staging function' }
185+
Invoke-Expression $function.Extent.Text
186+
$TargetDir = $env:OPENSHELL_TEST_ROOT
187+
$PrebuiltZ3Version = '5.1.0'
188+
$env:Z3_LIBRARY_PATH_OVERRIDE = $null
189+
$release = Join-Path $TargetDir 'aarch64-pc-windows-msvc/release'
190+
$sourceDir = Join-Path $release 'build/z3-sys-first/out/z3-5.1.0/bin'
191+
New-Item -ItemType Directory -Path $sourceDir -Force | Out-Null
192+
$source = Join-Path $sourceDir 'libz3.dll'
193+
$bytes = New-Object byte[] 128
194+
$bytes[0] = 0x4D; $bytes[1] = 0x5A; $bytes[60] = 64
195+
$bytes[64] = 0x50; $bytes[65] = 0x45
196+
$bytes[68] = 0x64; $bytes[69] = 0xAA
197+
[IO.File]::WriteAllBytes($source, $bytes)
198+
Stage-Z3Runtime 'aarch64-pc-windows-msvc'
199+
$destination = Join-Path $release 'libz3.dll'
200+
if ((Get-FileHash $destination).Hash -ne (Get-FileHash $source).Hash) { throw 'copy mismatch' }
201+
# A stale adjacent DLL must be overwritten from the pinned cache.
202+
[IO.File]::WriteAllBytes($destination, (New-Object byte[] 128))
203+
Stage-Z3Runtime 'aarch64-pc-windows-msvc'
204+
if ((Get-FileHash $destination).Hash -ne (Get-FileHash $source).Hash) { throw 'stale DLL retained' }
205+
$bytes[68] = 0x64; $bytes[69] = 0x86
206+
[IO.File]::WriteAllBytes($source, $bytes)
207+
$rejected = $false
208+
try { Stage-Z3Runtime 'aarch64-pc-windows-msvc' } catch {
209+
if ($_.Exception.Message -notmatch 'architecture') { throw }
210+
$rejected = $true
211+
}
212+
if (-not $rejected) { throw 'wrong architecture accepted' }
213+
$secondDir = Join-Path $release 'build/z3-sys-second/out/z3-5.1.0/bin'
214+
New-Item -ItemType Directory -Path $secondDir -Force | Out-Null
215+
$bytes[69] = 0xAA
216+
[IO.File]::WriteAllBytes((Join-Path $secondDir 'libz3.dll'), $bytes)
217+
$rejected = $false
218+
try { Stage-Z3Runtime 'aarch64-pc-windows-msvc' } catch {
219+
if ($_.Exception.Message -notmatch 'Ambiguous') { throw }
220+
$rejected = $true
221+
}
222+
if (-not $rejected) { throw 'conflicting runtimes accepted' }
223+
"#;
224+
let output = Command::new("powershell.exe")
225+
.args(["-NoProfile", "-Command", script])
226+
.env(
227+
"OPENSHELL_BUILD_SCRIPT",
228+
examples().join("../../../tasks/scripts/windows-msvc.ps1"),
229+
)
230+
.env("OPENSHELL_TEST_ROOT", root.path())
231+
.output()
232+
.unwrap();
233+
assert!(
234+
output.status.success(),
235+
"Z3 staging regression failed:\n{}\n{}",
236+
String::from_utf8_lossy(&output.stdout),
237+
String::from_utf8_lossy(&output.stderr)
238+
);
239+
}

‎crates/openshell-driver-mxc/tests/wxc_exec_real.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,8 @@ async fn pc_https_egress_reads_injected_ca_bundle() {
916916
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
917917
let config = MxcComputeConfig {
918918
wxc_exec_path: wxc.to_string_lossy().into_owned(),
919+
// This fixture intentionally submits a caller-selected curl command.
920+
allow_driver_config: true,
919921
..Default::default()
920922
};
921923
let backend = MxcComputeBackend::new(openshell_core::config::DEFAULT_GATEWAY_NAME, config);

‎crates/openshell-gateway/Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ telemetry = ["openshell-core/telemetry", "openshell-server/telemetry"]
7474
## `rust:verify:defaults-without-telemetry`.
7575
defaults-without-telemetry = ["in-tree-compute-drivers"]
7676
vendored-z3 = ["openshell-server/vendored-z3"]
77+
prebuilt-z3 = ["openshell-server/prebuilt-z3"]
7778

7879
[lints]
7980
workspace = true

‎tasks/scripts/windows-msvc.ps1‎

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ $PrebuiltZ3ServerFeatures = "--features openshell-server/prebuilt-z3,openshell-p
5858
$PrebuiltZ3Version = "5.1.0"
5959
$Z3WorkspaceFeatures = $PrebuiltZ3WorkspaceFeatures
6060
$Z3ServerFeatures = $PrebuiltZ3ServerFeatures
61+
$Z3GatewayFeatures = "--features prebuilt-z3"
6162

6263
function Get-VsInstallRoots {
6364
$programFiles = @(
@@ -514,6 +515,39 @@ function Invoke-Build([string] $RustTarget) {
514515
-RustTarget $RustTarget `
515516
-CargoArgs "cargo build --release --target $RustTarget --bin openshell-gateway --bin openshell --bin openshell-supervisor --bin openshell-windows-sandbox $Z3WorkspaceFeatures" `
516517
-LogName "build-$RustTarget-release.log"
518+
Stage-Z3Runtime $RustTarget
519+
}
520+
521+
function Stage-Z3Runtime([string] $RustTarget) {
522+
$release = Join-Path $TargetDir "$RustTarget/release"
523+
if ($env:Z3_LIBRARY_PATH_OVERRIDE) {
524+
$candidates = @(Get-Item -LiteralPath (Join-Path $env:Z3_LIBRARY_PATH_OVERRIDE 'libz3.dll') -ErrorAction SilentlyContinue)
525+
} else {
526+
# Only use the pinned release from this target's build cache. Never pick
527+
# the newest arbitrary DLL, or trust a stale adjacent runtime.
528+
$candidates = @(Get-ChildItem -Path (Join-Path $release "build/z3-sys-*/out/z3-$PrebuiltZ3Version/bin/libz3.dll") -File -ErrorAction SilentlyContinue)
529+
}
530+
if ($candidates.Count -eq 0) { throw "Matching libz3.dll not found for $RustTarget." }
531+
$hashes = @($candidates | ForEach-Object { (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash } | Select-Object -Unique)
532+
if ($hashes.Count -ne 1) { throw "Ambiguous Z3 runtimes for $RustTarget; use a clean target cache or Z3_LIBRARY_PATH_OVERRIDE." }
533+
$source = $candidates[0].FullName
534+
$reader = [IO.BinaryReader]::new([IO.File]::OpenRead($source))
535+
try {
536+
if ($reader.ReadUInt16() -ne 0x5A4D) { throw "Invalid Z3 DLL: $source" }
537+
$reader.BaseStream.Position = 0x3C
538+
$offset = $reader.ReadInt32()
539+
if ($offset -lt 64 -or $offset -gt $reader.BaseStream.Length - 6) { throw 'Invalid Z3 PE offset.' }
540+
$reader.BaseStream.Position = $offset
541+
if ($reader.ReadUInt32() -ne 0x00004550) { throw 'Invalid Z3 PE signature.' }
542+
$expected = if ($RustTarget -eq 'aarch64-pc-windows-msvc') { 0xAA64 } else { 0x8664 }
543+
if ($reader.ReadUInt16() -ne $expected) { throw "Z3 DLL architecture does not match $RustTarget." }
544+
} finally { $reader.Dispose() }
545+
$destination = Join-Path $release 'libz3.dll'
546+
if ([IO.Path]::GetFullPath($source) -ne [IO.Path]::GetFullPath($destination)) {
547+
Copy-Item -LiteralPath $source -Destination $destination -Force
548+
}
549+
if ((Get-FileHash -LiteralPath $destination -Algorithm SHA256).Hash -ne $hashes[0]) { throw 'Staged Z3 DLL hash mismatch.' }
550+
Write-Host "==> Staged $destination (SHA256=$($hashes[0]))"
517551
}
518552

519553
function Invoke-Test([string] $RustTarget) {
@@ -542,7 +576,7 @@ function Invoke-UnsupportedContractTests([string] $RustTarget) {
542576
foreach ($test in $tests) {
543577
Invoke-VsCargo `
544578
-RustTarget $RustTarget `
545-
-CargoArgs "cargo test -p openshell-gateway --target $RustTarget $test $Z3WorkspaceFeatures" `
579+
-CargoArgs "cargo test -p openshell-gateway --target $RustTarget $test $Z3GatewayFeatures" `
546580
-LogName "test-$RustTarget-unsupported-$test.log"
547581
}
548582

@@ -551,7 +585,7 @@ function Invoke-UnsupportedContractTests([string] $RustTarget) {
551585
$variant = if ($features) { $features.Replace(",", "-") } else { "protocol-only" }
552586
Invoke-VsCargo `
553587
-RustTarget $RustTarget `
554-
-CargoArgs "cargo test -p openshell-gateway --lib --target $RustTarget --no-default-features $featureArgs $Z3ServerFeatures" `
588+
-CargoArgs "cargo test -p openshell-gateway --lib --target $RustTarget --no-default-features $featureArgs $Z3GatewayFeatures" `
555589
-LogName "test-$RustTarget-selective-$variant.log"
556590
}
557591
}
@@ -618,6 +652,7 @@ if ($Action -in @("check", "lint", "build", "build-mxc-fixtures", "test", "test-
618652
$z3Features = Configure-Z3
619653
$Z3WorkspaceFeatures = $z3Features.WorkspaceFeatures
620654
$Z3ServerFeatures = $z3Features.ServerFeatures
655+
$Z3GatewayFeatures = if ($z3Features.WorkspaceFeatures) { "--features prebuilt-z3" } else { "" }
621656
$env:LIBCLANG_PATH = Resolve-LibclangPath
622657
Add-PathEntry $env:LIBCLANG_PATH
623658
Write-Host "==> LIBCLANG_PATH=$env:LIBCLANG_PATH"

0 commit comments

Comments
 (0)