Skip to content

Commit 901338d

Browse files
committed
fix(mxc): pass native Windows build and HTTPS qualification
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
1 parent 57765ce commit 901338d

63 files changed

Lines changed: 1555 additions & 640 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ mechanics belong to the sandbox or isolation backend. Keep the optional Unix
3434
SSH access adapter separate from portable session orchestration.
3535
Shared Sandbox Protocol audit validation defaults to strict Linux evidence;
3636
concrete platform validators must be selected by the implementing backend.
37+
38+
MXC control connects outward from the boundary to a generation-scoped host
39+
loopback listener, matching the Windows relay's connection direction. Keep
40+
the reverse connector in the MXC isolation backend and share it across policy
41+
discovery and attachment. Shared transport hooks must retain pinned TLS peer
42+
verification and supervisor JWT authentication; do not change host firewall
43+
rules to repair host-to-AppContainer control connectivity.
44+
3745
The dedicated Windows sandbox binary links the Windows-only `openshell-mxc-boundary`
3846
library for process operations, containment confirmation, and loopback
3947
forwarding. The generic sandbox does not link this library and remains a Linux
@@ -43,10 +51,22 @@ process. Include boundary-library regression tests in native workspace tests;
4351
their real process/socket checks are not qualification of MXC enforcement.
4452

4553
Keep MXC audit schemas and their validators in the Windows boundary library.
46-
Register its `MxcRuntimeBackend` under `openshell-mxc` at supervisor composition;
47-
it reuses the shared authenticated Sandbox Protocol, whose default Linux
54+
Register its `MxcRuntimeBackend` under `openshell-mxc` at supervisor composition.
55+
Use the private `BackendSetup` / `PreparedBackend` extension for launch decoding,
56+
policy discovery, and client construction. Shared startup must not decode the
57+
MXC descriptor or allocate its connector. Retain the same reverse-TCP connector
58+
in prepared launch state through discovery, attachment, and reconnects.
59+
It reuses the shared authenticated Sandbox Protocol, whose default Linux
4860
backend remains separate. Shared protocol code
49-
must not interpret MXC evidence. Do not reintroduce driver-owned proxy startup
61+
must not interpret MXC evidence.
62+
63+
Keep proxy provisioning in MXC-owned host-launch and one-use bootstrap
64+
envelopes. Private Windows backend setup supplies concrete listener options to
65+
shared supervisor networking. Do not add proxy hooks to `BoundBoundary`, proxy
66+
fields to shared Sandbox Protocol descriptors, or placeholders to other drivers.
67+
Proxy startup remains after authenticated attachment and boundary confirmation.
68+
69+
Do not reintroduce driver-owned proxy startup
5070
or gateway create-time credential snapshots. Gateway JSONL audit output
5171
is portable and an explicit operator opt-in, independent of the selected driver.
5272
Keep platform directory conventions in shared path utilities and ETW capture
@@ -60,11 +80,12 @@ provider-publication generations. Do not order opaque credential revisions
6080
numerically. MXC rejects unsupported provider file delivery before launch or
6181
environment replacement; it must not acknowledge files it did not install.
6282

63-
Preserve main's explicit outer-fence evidence contract. The current MXC mapper
64-
allows broad host loopback and has no verified live-revocation evidence, so
65-
configuration flags cannot establish the required guarantees. Keep confirmation
66-
fail-closed until native qualification proves each property; a supported PSEC
67-
host alone does not close these implementation gaps.
83+
Preserve main's shared outer-fence evidence contract. At the user's explicit
84+
request, MXC temporarily asserts the required guarantees for Windows-branch
85+
parity testing. These MXC-only assertions are stubs, not verified enforcement:
86+
broad host loopback and unverified live revocation remain known gaps. Keep the
87+
TODOs and unverified audit mechanism visible; never report passing E2E as proof
88+
that these gaps are closed. A supported PSEC host alone does not close them.
6889

6990
## Current Repository Shape
7091

@@ -260,7 +281,7 @@ jobs in the current mirror push run, or push a new mirrored commit. The binaries
260281
The ARM64 check/build steps in this x64-host contract are cross-builds. The
261282
wrapper discovers and adds host-native LLVM and Ninja to `PATH`, requires the
262283
ARM64 compiler and Spectre-mitigated libraries, lets ARM64 crypto crates select
263-
`clang-cl`, and downloads the official prebuilt ARM64 Z3 static library.
284+
`clang-cl`, and downloads the official prebuilt ARM64 Z3 package.
264285

265286
On ARM64 hosts, validate the native ARM64 check, build, and test path. The
266287
wrapper rejects test targets that do not match the host architecture, so x64
@@ -337,6 +358,7 @@ without the capability reject explicit UI without implementing UI-specific
337358
handling. Keep native UI validation and mapping in MXC. Omitted UI settings deny
338359
graphical UI, clipboard access, and input injection. UI changes require
339360
recreating the sandbox.
361+
340362
Windows must continue to reject unsupported compute drivers clearly.
341363

342364
The gateway's `compute-driver-mxc` feature independently links and registers
@@ -389,6 +411,7 @@ Verify the SSH host key beforehand. Never send the private SSH key or bundle
389411
the harness signing-key directories. `-SkipBuild` is an explicit stale-artifact
390412
opt-in, not evidence that artifacts match current sources. See the MXC driver
391413
README for the command and remote dependency contract.
414+
392415
For MXC validation, also run `windows:e2e:mxc`
393416
after the native release build. The harness uses .NET port discovery, disposable
394417
TOML and CLI registration, and owner-only Ed25519 keys generated by OpenSSL on
@@ -397,8 +420,9 @@ bundles and are deleted unless `-KeepRunning` is explicitly requested.
397420

398421
E2E has no mock mode and rejects `OPENSHELL_MXC_MOCK_WXC=1`. Unit-test mocks
399422
are not E2E coverage. Every selected scenario must pass for a successful exit;
400-
any skip reports `INCOMPLETE` and exits non-zero. Never bypass audit to make
401-
tests pass. Real runtime scenarios require native ProcessContainer PSEC egress
423+
any skip reports `INCOMPLETE` and exits non-zero. Do not broaden the temporary
424+
MXC network-assertion waiver to other audit or authentication checks. Real
425+
runtime scenarios require native ProcessContainer PSEC egress
402426
filtering and ingress host-loopback support. `wxc-exec --probe` is authoritative,
403427
not the Windows build number. All-skipped real runs are not passes. Separate
404428
test-internal `SKIP` messages from Cargo's passed count in the real integration
@@ -419,6 +443,17 @@ exposure, two exact GET responses through gateway routing, and HTTP 404 after
419443
endpoint deletion. This is distinct from `openshell forward service`; neither
420444
scenario tests file upload or exclusive ingress enforcement.
421445

446+
The real integration HTTPS test runs the same authenticated gateway harness with
447+
the opt-in `https-ca` scenario, not an unauthenticated standalone driver. Build
448+
native release binaries first, or select them with `OPENSHELL_MXC_TEST_BINARY_DIR`.
449+
It requires Internet access to example.com and Windows curl >= 8.3 to explicitly
450+
expand the injected CA bundle into `--cacert` (Schannel ignores that environment
451+
variable by default). Require both a nonempty response and the OpenShell proxy
452+
CA issuer; never replace these assertions with an insecure TLS request. This
453+
fixture permits unavailable CRL information using best-effort Schannel revocation
454+
for the ephemeral private CA; this is not revocation qualification. This
455+
scenario is not part of the default six offline-capable E2E scenarios.
456+
422457
When reporting `windows:ci`, distinguish these categories:
423458

424459
- Passed tests from the full x64 workspace test log.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ top-level workspace targets for check/test:
110110
--exclude openshell-driver-podman
111111
--exclude openshell-driver-vault
112112
--exclude openshell-driver-vm
113+
--exclude openshell-sandbox
113114
--exclude openshell-vfio
114115
```
115116

‎Cargo.lock‎

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎crates/openshell-cli/src/commands/gateway.rs‎

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ struct ComputeDriverInfoView {
4646
struct ComputeDriverCapabilitiesView {
4747
driver_name: String,
4848
driver_version: String,
49-
supports_ui_policy: bool,
5049
}
5150

5251
#[derive(Debug, Clone)]
@@ -418,7 +417,6 @@ pub async fn gateway_info(
418417
capabilities: ComputeDriverCapabilitiesView {
419418
driver_name: capabilities.driver_name,
420419
driver_version: capabilities.driver_version,
421-
supports_ui_policy: capabilities.supports_ui_policy,
422420
},
423421
}
424422
})
@@ -516,15 +514,6 @@ fn print_compute_driver_info(drivers: &[ComputeDriverInfoView]) {
516514
"Driver version:".dimmed(),
517515
driver.capabilities.driver_version
518516
);
519-
println!(
520-
" {} {}",
521-
"UI policy:".dimmed(),
522-
if driver.capabilities.supports_ui_policy {
523-
"supported"
524-
} else {
525-
"unsupported"
526-
}
527-
);
528517
}
529518
}
530519

@@ -543,7 +532,6 @@ fn gateway_info_to_json(view: &GatewayInfoView) -> serde_json::Value {
543532
"capabilities": {
544533
"driver_name": &driver.capabilities.driver_name,
545534
"driver_version": &driver.capabilities.driver_version,
546-
"supports_ui_policy": driver.capabilities.supports_ui_policy,
547535
},
548536
}))
549537
.collect::<Vec<_>>(),
@@ -1911,7 +1899,6 @@ mod tests {
19111899
capabilities: ComputeDriverCapabilitiesView {
19121900
driver_name: "podman".to_string(),
19131901
driver_version: "0.0.75".to_string(),
1914-
supports_ui_policy: false,
19151902
},
19161903
}],
19171904
extensions: vec![ExtensionInfoView {
@@ -1940,10 +1927,6 @@ mod tests {
19401927
json["compute_drivers"][0]["capabilities"]["driver_version"],
19411928
"0.0.75"
19421929
);
1943-
assert_eq!(
1944-
json["compute_drivers"][0]["capabilities"]["supports_ui_policy"],
1945-
false
1946-
);
19471930
}
19481931

19491932
#[test]

‎crates/openshell-cli/src/main.rs‎

Lines changed: 2 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,16 +1674,8 @@ enum SandboxCommands {
16741674
names: Vec<String>,
16751675

16761676
/// Delete all sandboxes.
1677-
#[arg(long, conflicts_with_all = ["names", "expected_id", "expected_resource_version"])]
1677+
#[arg(long, conflicts_with = "names")]
16781678
all: bool,
1679-
1680-
/// Delete only if the current sandbox has this exact immutable ID.
1681-
#[arg(long = "expected-id", value_name = "ID")]
1682-
expected_id: Option<String>,
1683-
1684-
/// Delete only if the current sandbox has this resource version.
1685-
#[arg(long = "expected-resource-version", value_name = "VERSION")]
1686-
expected_resource_version: Option<u64>,
16871679
},
16881680

16891681
/// Stop a sandbox while preserving its workspace.
@@ -3584,18 +3576,11 @@ async fn run_async() -> Result<()> {
35843576
)
35853577
.await?;
35863578
}
3587-
SandboxCommands::Delete {
3588-
names,
3589-
all,
3590-
expected_id,
3591-
expected_resource_version,
3592-
} => {
3579+
SandboxCommands::Delete { names, all } => {
35933580
run::sandbox_delete(
35943581
endpoint,
35953582
&names,
35963583
all,
3597-
expected_id.as_deref(),
3598-
expected_resource_version,
35993584
&cli.workspace,
36003585
&tls,
36013586
&ctx.name,
@@ -5540,48 +5525,6 @@ mod tests {
55405525
));
55415526
}
55425527

5543-
#[test]
5544-
fn sandbox_delete_accepts_identity_preconditions() {
5545-
let cli = Cli::try_parse_from([
5546-
"openshell",
5547-
"sandbox",
5548-
"delete",
5549-
"demo",
5550-
"--expected-id",
5551-
"sb-123",
5552-
"--expected-resource-version",
5553-
"17",
5554-
])
5555-
.expect("identity-guarded sandbox delete should parse");
5556-
5557-
assert!(matches!(
5558-
cli.command,
5559-
Some(Commands::Sandbox {
5560-
command: Some(SandboxCommands::Delete {
5561-
ref names,
5562-
all: false,
5563-
expected_id: Some(ref expected_id),
5564-
expected_resource_version: Some(17),
5565-
})
5566-
}) if names == &["demo"] && expected_id == "sb-123"
5567-
));
5568-
}
5569-
5570-
#[test]
5571-
fn sandbox_delete_all_rejects_identity_preconditions() {
5572-
assert!(
5573-
Cli::try_parse_from([
5574-
"openshell",
5575-
"sandbox",
5576-
"delete",
5577-
"--all",
5578-
"--expected-id",
5579-
"sb-123",
5580-
])
5581-
.is_err()
5582-
);
5583-
}
5584-
55855528
#[test]
55865529
fn sandbox_list_accepts_output_json() {
55875530
let cli = Cli::try_parse_from(["openshell", "sandbox", "list", "-o", "json"])

‎crates/openshell-cli/src/run.rs‎

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -412,9 +412,7 @@ async fn finalize_sandbox_create_session(
412412
}
413413

414414
let names = [sandbox_name.to_string()];
415-
if let Err(err) =
416-
sandbox_delete(server, &names, false, None, None, workspace, tls, gateway).await
417-
{
415+
if let Err(err) = sandbox_delete(server, &names, false, workspace, tls, gateway).await {
418416
if let Ok(exit_code) = session_result.as_ref() {
419417
return Err(miette::miette!(
420418
"sandbox command exited with status {exit_code}, but ephemeral cleanup failed: {err}"
@@ -3881,35 +3879,14 @@ fn labels_display(labels: &HashMap<String, String>) -> String {
38813879
}
38823880

38833881
/// Delete a sandbox by name, or all sandboxes when `all` is true.
3884-
#[allow(clippy::too_many_arguments)] // user-facing CLI command with explicit identity guards
38853882
pub async fn sandbox_delete(
38863883
server: &str,
38873884
names: &[String],
38883885
all: bool,
3889-
expected_id: Option<&str>,
3890-
expected_resource_version: Option<u64>,
38913886
workspace: &str,
38923887
tls: &TlsOptions,
38933888
gateway: &str,
38943889
) -> Result<()> {
3895-
if (expected_id.is_some() || expected_resource_version.is_some()) && (all || names.len() != 1) {
3896-
return Err(miette!(
3897-
"--expected-id and --expected-resource-version require exactly one sandbox name"
3898-
));
3899-
}
3900-
if expected_id.is_some_and(str::is_empty) {
3901-
return Err(miette!("--expected-id must not be empty"));
3902-
}
3903-
if expected_resource_version.is_some() && expected_id.is_none() {
3904-
return Err(miette!(
3905-
"--expected-resource-version requires --expected-id"
3906-
));
3907-
}
3908-
if expected_resource_version == Some(0) {
3909-
return Err(miette!(
3910-
"--expected-resource-version must be greater than zero"
3911-
));
3912-
}
39133890
let mut client = grpc_client(server, tls).await?;
39143891

39153892
let names_to_delete: Vec<String> = if all {

‎crates/openshell-cli/tests/sandbox_create_lifecycle_integration.rs‎

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ struct SandboxState {
6464
/// so a catalog lookup failure can be told apart from an empty catalog.
6565
fail_list_provider_profiles: Arc<AtomicBool>,
6666
deleted_names: Arc<Mutex<Vec<Vec<String>>>>,
67-
delete_requests: Arc<Mutex<Vec<DeleteSandboxRequest>>>,
6867
create_requests: Arc<Mutex<Vec<CreateSandboxRequest>>>,
6968
expose_service_requests: Arc<Mutex<Vec<openshell_core::proto::ExposeServiceRequest>>>,
7069
fail_delete_sandbox_message: Arc<Mutex<Option<String>>>,
@@ -390,11 +389,6 @@ impl OpenShell for TestOpenShell {
390389
request: tonic::Request<DeleteSandboxRequest>,
391390
) -> Result<Response<DeleteSandboxResponse>, Status> {
392391
let request = request.into_inner();
393-
self.state
394-
.delete_requests
395-
.lock()
396-
.await
397-
.push(request.clone());
398392
self.state
399393
.deleted_names
400394
.lock()
@@ -1593,10 +1587,6 @@ async fn deleted_names(server: &TestServer) -> Vec<Vec<String>> {
15931587
server.openshell.state.deleted_names.lock().await.clone()
15941588
}
15951589

1596-
async fn delete_requests(server: &TestServer) -> Vec<DeleteSandboxRequest> {
1597-
server.openshell.state.delete_requests.lock().await.clone()
1598-
}
1599-
16001590
async fn create_requests(server: &TestServer) -> Vec<CreateSandboxRequest> {
16011591
server.openshell.state.create_requests.lock().await.clone()
16021592
}
@@ -1706,8 +1696,6 @@ async fn sandbox_delete_continues_after_entry_failure() {
17061696
&server.endpoint,
17071697
&["failing-sandbox".to_string(), "later-sandbox".to_string()],
17081698
false,
1709-
None,
1710-
None,
17111699
"default",
17121700
&tls,
17131701
"openshell",

‎crates/openshell-core/README.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ configuration validation live in `openshell_core::middleware`. Policy admission
5858
and the supervisor runtime consume the same contract without introducing a
5959
dependency from the policy crate to the supervisor implementation.
6060

61+
## Extension Capabilities
62+
63+
Extensions advertise namespaced string capabilities through `PeerMetadata`.
64+
Use shared constants in `extension_protocol` and pass optional capabilities to
65+
`extension_metadata`; do not add per-driver booleans for policy features.
66+
Negotiation validates and retains the extension's supported capability list.
67+
68+
`POLICY_UI_V1` (`openshell.policy.ui.v1`) declares complete startup UI-policy
69+
support. The gateway checks the effective policy before driver validation or
70+
creation and rejects any explicit UI section when the capability is absent.
71+
Supporting drivers validate and enforce the concrete settings. Omitted UI
72+
preserves existing driver behavior; changing UI requires sandbox recreation.
73+
6174
## Protobuf Struct Conversion
6275

6376
Use `openshell_core::proto_struct` when crossing between `serde_json` values and

0 commit comments

Comments
 (0)