diff --git a/crates/openshell-cli/src/commands/gateway.rs b/crates/openshell-cli/src/commands/gateway.rs index 705976f7af..133de24ac9 100644 --- a/crates/openshell-cli/src/commands/gateway.rs +++ b/crates/openshell-cli/src/commands/gateway.rs @@ -46,6 +46,7 @@ struct ComputeDriverInfoView { struct ComputeDriverCapabilitiesView { driver_name: String, driver_version: String, + workload_compatibility: Option, } #[derive(Debug, Clone)] @@ -417,6 +418,7 @@ pub async fn gateway_info( capabilities: ComputeDriverCapabilitiesView { driver_name: capabilities.driver_name, driver_version: capabilities.driver_version, + workload_compatibility: capabilities.workload_compatibility, }, } }) @@ -514,7 +516,47 @@ fn print_compute_driver_info(drivers: &[ComputeDriverInfoView]) { "Driver version:".dimmed(), driver.capabilities.driver_version ); + for line in + workload_compatibility_lines(driver.capabilities.workload_compatibility.as_ref()) + { + println!(" {line}"); + } + } +} + +fn workload_compatibility_lines( + workload: Option<&openshell_core::proto::WorkloadCompatibility>, +) -> Vec { + let reported = |value: &str| { + if value.is_empty() { + "unreported".to_string() + } else { + value.to_string() + } + }; + let Some(workload) = workload else { + return vec![ + "Workload platform: unreported".into(), + "Execution restrictions: unreported".into(), + ]; + }; + let mut lines = vec![format!( + "Workload platform: {} / {}", + reported(&workload.operating_system), + reported(&workload.architecture) + )]; + if workload.execution_restrictions.is_empty() { + lines.push("Execution restrictions: unreported".into()); + } else { + lines.push("Execution restrictions (reported):".into()); + lines.extend( + workload + .execution_restrictions + .iter() + .map(|restriction| format!(" {restriction}")), + ); } + lines } fn gateway_info_to_json(view: &GatewayInfoView) -> serde_json::Value { @@ -532,6 +574,11 @@ fn gateway_info_to_json(view: &GatewayInfoView) -> serde_json::Value { "capabilities": { "driver_name": &driver.capabilities.driver_name, "driver_version": &driver.capabilities.driver_version, + "workload_compatibility": driver.capabilities.workload_compatibility.as_ref().map(|workload| serde_json::json!({ + "operating_system": &workload.operating_system, + "architecture": &workload.architecture, + "execution_restrictions": &workload.execution_restrictions, + })), }, })) .collect::>(), @@ -1590,7 +1637,7 @@ mod tests { gateway_env_override_warning, gateway_info_to_json, gateway_remote_label, gateway_select_with, gateway_to_json, gateway_type_label, http_health_check, import_local_package_mtls_bundle, mtls_certs_exist_for_gateway, package_managed_tls_dirs, - plaintext_gateway_is_remote, + plaintext_gateway_is_remote, workload_compatibility_lines, }; use crate::TEST_ENV_LOCK; use crate::test_utils::{Environment, with_tmp_xdg, with_tmp_xdg_env}; @@ -1894,7 +1941,7 @@ mod tests { #[test] fn gateway_info_json_includes_compute_drivers_when_available() { - let view = GatewayInfoView { + let mut view = GatewayInfoView { gateway: "openshell".to_string(), server: "https://127.0.0.1:17670".to_string(), auth: Some("bearer"), @@ -1905,6 +1952,7 @@ mod tests { capabilities: ComputeDriverCapabilitiesView { driver_name: "podman".to_string(), driver_version: "0.0.75".to_string(), + workload_compatibility: None, }, }], extensions: vec![ExtensionInfoView { @@ -1933,6 +1981,46 @@ mod tests { json["compute_drivers"][0]["capabilities"]["driver_version"], "0.0.75" ); + assert!(json["compute_drivers"][0]["capabilities"]["workload_compatibility"].is_null()); + assert_eq!( + workload_compatibility_lines(None), + [ + "Workload platform: unreported", + "Execution restrictions: unreported" + ] + ); + + let workload = openshell_core::proto::WorkloadCompatibility { + operating_system: "macos".into(), + architecture: "arm64".into(), + execution_restrictions: vec!["Setuid executables cannot run in this runtime.".into()], + }; + assert_eq!( + workload_compatibility_lines(Some(&workload)), + [ + "Workload platform: macos / arm64", + "Execution restrictions (reported):", + " Setuid executables cannot run in this runtime." + ] + ); + view.compute_drivers[0].capabilities.workload_compatibility = Some(workload); + assert_eq!( + gateway_info_to_json(&view)["compute_drivers"][0]["capabilities"]["workload_compatibility"], + serde_json::json!({ + "operating_system": "macos", + "architecture": "arm64", + "execution_restrictions": ["Setuid executables cannot run in this runtime."], + }) + ); + assert_eq!( + workload_compatibility_lines(Some( + &openshell_core::proto::WorkloadCompatibility::default() + )), + [ + "Workload platform: unreported / unreported", + "Execution restrictions: unreported" + ] + ); } #[test] diff --git a/crates/openshell-core/src/extension_protocol.rs b/crates/openshell-core/src/extension_protocol.rs index d6d93418d8..e6ab2f0d7a 100644 --- a/crates/openshell-core/src/extension_protocol.rs +++ b/crates/openshell-core/src/extension_protocol.rs @@ -324,6 +324,49 @@ pub fn validate_gateway_metadata( negotiate(family, extension_name, &gateway, extension.cloned()).map(drop) } +/// Validate diagnostic facts before exposing a compute driver's startup snapshot. +/// Empty platform fields remain unreported; reported explanations are bounded text. +pub fn validate_workload_compatibility( + name: &str, + workload: Option<&crate::proto::compute::v1::WorkloadCompatibility>, +) -> Result<(), NegotiationError> { + let Some(workload) = workload else { + return Ok(()); + }; + for (field, value) in [ + ( + "workload_compatibility.operating_system", + &workload.operating_system, + ), + ( + "workload_compatibility.architecture", + &workload.architecture, + ), + ] { + if !value.is_empty() { + validate_text("compute", name, field, value, 64)?; + } + } + if workload.execution_restrictions.len() > 16 { + return Err(NegotiationError::InvalidMetadata { + family: "compute", + name: name.to_string(), + field: "workload_compatibility.execution_restrictions", + reason: "must contain at most 16 explanations".into(), + }); + } + for restriction in &workload.execution_restrictions { + validate_text( + "compute", + name, + "workload_compatibility.execution_restrictions", + restriction, + 512, + )?; + } + Ok(()) +} + fn validate_text( family: &'static str, name: &str, @@ -574,6 +617,53 @@ mod tests { )); } + #[test] + fn workload_compatibility_preserves_unknown_values_and_bounds_plain_text() { + use crate::proto::compute::v1::WorkloadCompatibility; + + assert!(validate_workload_compatibility("native", None).is_ok()); + assert!( + validate_workload_compatibility("native", Some(&WorkloadCompatibility::default())) + .is_ok() + ); + let valid = WorkloadCompatibility { + operating_system: "future-os".into(), + architecture: "future-arch".into(), + execution_restrictions: vec!["Fixed runtime restriction".into()], + }; + assert!(validate_workload_compatibility("native", Some(&valid)).is_ok()); + for invalid in [ + WorkloadCompatibility { + operating_system: "x".repeat(65), + ..valid.clone() + }, + WorkloadCompatibility { + architecture: "bad\narch".into(), + ..valid.clone() + }, + WorkloadCompatibility { + execution_restrictions: vec!["x".repeat(513)], + ..valid.clone() + }, + WorkloadCompatibility { + execution_restrictions: vec!["fixed".into(); 17], + ..valid.clone() + }, + WorkloadCompatibility { + execution_restrictions: vec![" ".into()], + ..valid.clone() + }, + WorkloadCompatibility { + execution_restrictions: vec!["\u{1b}[31mPRIVATE DIAGNOSTIC".into()], + ..valid + }, + ] { + let error = validate_workload_compatibility("native", Some(&invalid)).unwrap_err(); + assert!(matches!(error, NegotiationError::InvalidMetadata { .. })); + assert!(!error.to_string().contains("PRIVATE DIAGNOSTIC")); + } + } + #[test] fn extension_side_rejects_missing_and_incompatible_gateway_metadata() { let (mut gateway, extension) = compatible(); diff --git a/crates/openshell-driver-docker/src/lib.rs b/crates/openshell-driver-docker/src/lib.rs index aa1d7de706..4f2adeb57d 100644 --- a/crates/openshell-driver-docker/src/lib.rs +++ b/crates/openshell-driver-docker/src/lib.rs @@ -1088,6 +1088,13 @@ impl DockerComputeDriver { count_selection_supported: self.config.gpu.cdi_supported, }), }), + workload_compatibility: Some( + openshell_core::proto::compute::v1::WorkloadCompatibility { + operating_system: "linux".to_string(), + architecture: String::new(), + execution_restrictions: Vec::new(), + }, + ), rootfs_tar_staging_dir: String::new(), rootfs_tar_max_bytes: 0, extension: Some(openshell_core::extension_protocol::extension_metadata( diff --git a/crates/openshell-driver-kubernetes/src/driver.rs b/crates/openshell-driver-kubernetes/src/driver.rs index c08a567eed..8f101cfc90 100644 --- a/crates/openshell-driver-kubernetes/src/driver.rs +++ b/crates/openshell-driver-kubernetes/src/driver.rs @@ -1321,6 +1321,13 @@ impl KubernetesComputeDriver { count_selection_supported: true, }), }), + workload_compatibility: Some( + openshell_core::proto::compute::v1::WorkloadCompatibility { + operating_system: "linux".to_string(), + architecture: String::new(), + execution_restrictions: Vec::new(), + }, + ), rootfs_tar_staging_dir: String::new(), rootfs_tar_max_bytes: 0, extension: Some(openshell_core::extension_protocol::extension_metadata( diff --git a/crates/openshell-driver-mxc/src/driver.rs b/crates/openshell-driver-mxc/src/driver.rs index 2772f1a69c..c4d2da50d2 100644 --- a/crates/openshell-driver-mxc/src/driver.rs +++ b/crates/openshell-driver-mxc/src/driver.rs @@ -474,6 +474,13 @@ impl MxcComputeBackend { supports_sandbox_authentication: false, driver_reports_runtime_readiness: true, resource_capabilities: None, + workload_compatibility: Some( + openshell_core::proto::compute::v1::WorkloadCompatibility { + operating_system: "windows".to_string(), + architecture: String::new(), + execution_restrictions: Vec::new(), + }, + ), rootfs_tar_staging_dir: String::new(), rootfs_tar_max_bytes: 0, extension: Some(openshell_core::extension_protocol::extension_metadata( diff --git a/crates/openshell-driver-podman/src/driver.rs b/crates/openshell-driver-podman/src/driver.rs index 6bea5b7f16..66198d9d7e 100644 --- a/crates/openshell-driver-podman/src/driver.rs +++ b/crates/openshell-driver-podman/src/driver.rs @@ -738,6 +738,13 @@ impl PodmanComputeDriver { count_selection_supported: true, }), }), + workload_compatibility: Some( + openshell_core::proto::compute::v1::WorkloadCompatibility { + operating_system: "linux".to_string(), + architecture: String::new(), + execution_restrictions: Vec::new(), + }, + ), rootfs_tar_staging_dir: String::new(), rootfs_tar_max_bytes: 0, extension: Some(openshell_core::extension_protocol::extension_metadata( diff --git a/crates/openshell-driver-vm/src/driver.rs b/crates/openshell-driver-vm/src/driver.rs index f7946c34c2..355d1bb193 100644 --- a/crates/openshell-driver-vm/src/driver.rs +++ b/crates/openshell-driver-vm/src/driver.rs @@ -1568,6 +1568,13 @@ impl VmDriver { count_selection_supported: self.config.gpu_enabled, }), }), + workload_compatibility: Some( + openshell_core::proto::compute::v1::WorkloadCompatibility { + operating_system: "linux".to_string(), + architecture: linux_oci_arch().to_string(), + execution_restrictions: Vec::new(), + }, + ), rootfs_tar_staging_dir: self .config .rootfs_tar_staging_dir() @@ -14127,6 +14134,10 @@ while :; do sleep 0.05; done"#, #[test] fn capabilities_report_static_resource_support() { let mut driver = test_driver_with_extensions(LifecycleExtensionRegistry::new()); + let workload = driver.capabilities().workload_compatibility.unwrap(); + assert_eq!(workload.operating_system, "linux"); + assert_eq!(workload.architecture, linux_oci_arch()); + assert!(workload.execution_restrictions.is_empty()); let resources = driver.capabilities().resource_capabilities.unwrap(); assert!(!resources.cpu.unwrap().limit_supported); assert!(!resources.memory.unwrap().limit_supported); diff --git a/crates/openshell-server/src/compute/mod.rs b/crates/openshell-server/src/compute/mod.rs index a6768040e6..b2ba33e563 100644 --- a/crates/openshell-server/src/compute/mod.rs +++ b/crates/openshell-server/src/compute/mod.rs @@ -518,6 +518,8 @@ pub struct ComputeDriverInfoSnapshot { pub driver_reports_runtime_readiness: bool, /// Static portable resource request forms from the startup capability snapshot. pub resource_capabilities: Option, + /// Driver-reported workload facts; absence is deliberately not inferred. + pub workload_compatibility: Option, /// Directory where rootfs tar files must be staged. pub rootfs_tar_staging_dir: String, /// Maximum rootfs tar file size in bytes. @@ -1023,6 +1025,11 @@ impl ComputeRuntime { capabilities.extension.clone(), ) .map_err(|error| ComputeError::Message(error.to_string()))?; + openshell_core::extension_protocol::validate_workload_compatibility( + &driver_name, + capabilities.workload_compatibility.as_ref(), + ) + .map_err(|error| ComputeError::Message(error.to_string()))?; info!( configured_driver = %driver_name, advertised_driver = %capabilities.driver_name, @@ -1037,6 +1044,7 @@ impl ComputeRuntime { supports_sandbox_authentication: capabilities.supports_sandbox_authentication, driver_reports_runtime_readiness: capabilities.driver_reports_runtime_readiness, resource_capabilities: capabilities.resource_capabilities, + workload_compatibility: capabilities.workload_compatibility, rootfs_tar_staging_dir: capabilities.rootfs_tar_staging_dir, rootfs_tar_max_bytes: capabilities.rootfs_tar_max_bytes, }; @@ -8504,6 +8512,7 @@ impl ComputeDriver for NoopTestDriver { supports_sandbox_authentication: self.sandbox_authentication.is_some(), driver_reports_runtime_readiness: false, resource_capabilities: None, + workload_compatibility: None, rootfs_tar_staging_dir: String::new(), rootfs_tar_max_bytes: 0, extension: Some(openshell_core::extension_protocol::extension_metadata( @@ -8719,6 +8728,7 @@ pub fn new_test_runtime_with_driver( supports_sandbox_authentication, driver_reports_runtime_readiness: false, resource_capabilities: None, + workload_compatibility: None, rootfs_tar_staging_dir: String::new(), rootfs_tar_max_bytes: 0, }, @@ -9160,6 +9170,7 @@ mod tests { supports_sandbox_authentication: false, driver_reports_runtime_readiness: false, resource_capabilities: None, + workload_compatibility: None, rootfs_tar_staging_dir: String::new(), rootfs_tar_max_bytes: 0, extension: (!self.omit_protocol_metadata).then(|| { @@ -9716,6 +9727,7 @@ mod tests { supports_sandbox_authentication: false, driver_reports_runtime_readiness: false, resource_capabilities: None, + workload_compatibility: None, rootfs_tar_staging_dir: String::new(), rootfs_tar_max_bytes: 0, extension: Some(openshell_core::extension_protocol::extension_metadata( @@ -10017,6 +10029,7 @@ mod tests { supports_sandbox_authentication: false, driver_reports_runtime_readiness: false, resource_capabilities: None, + workload_compatibility: None, rootfs_tar_staging_dir: String::new(), rootfs_tar_max_bytes: 0, }, diff --git a/crates/openshell-server/src/grpc/mod.rs b/crates/openshell-server/src/grpc/mod.rs index 69208348de..385949e14d 100644 --- a/crates/openshell-server/src/grpc/mod.rs +++ b/crates/openshell-server/src/grpc/mod.rs @@ -318,6 +318,13 @@ impl OpenShell for OpenShellService { .resource_capabilities .as_ref() .map(|resources| public_resource_capabilities(*resources)), + workload_compatibility: driver.workload_compatibility.as_ref().map( + |workload| openshell_core::proto::WorkloadCompatibility { + operating_system: workload.operating_system.clone(), + architecture: workload.architecture.clone(), + execution_restrictions: workload.execution_restrictions.clone(), + }, + ), }), }) .collect(); @@ -1318,6 +1325,83 @@ mod tests { ResourceCapabilities as DriverResourceCapabilities, }; + #[tokio::test] + async fn gateway_info_preserves_driver_workload_compatibility_and_legacy_absence() { + use crate::test_support::FakeComputeDriver; + use openshell_core::proto::compute::v1::GetCapabilitiesRequest; + use openshell_core::proto::compute::v1::compute_driver_server::ComputeDriver; + + for (workload, valid) in [ + (None, true), + ( + Some(openshell_core::proto::compute::v1::WorkloadCompatibility { + operating_system: "macos".into(), + architecture: "arm64".into(), + execution_restrictions: vec![ + "Setuid executables cannot run in this runtime.".into(), + ], + }), + true, + ), + ( + Some(openshell_core::proto::compute::v1::WorkloadCompatibility { + execution_restrictions: vec!["bad\nPRIVATE DIAGNOSTIC".into()], + ..Default::default() + }), + false, + ), + ] { + let mut state = test_support::test_server_state().await; + let driver = FakeComputeDriver::new(); + let mut capabilities = driver + .get_capabilities(Request::new(GetCapabilitiesRequest::default())) + .await + .unwrap() + .into_inner(); + capabilities.workload_compatibility = workload.clone(); + let driver = Arc::new(driver.with_capabilities(capabilities)); + let compute = crate::compute::ComputeRuntime::from_driver( + "external-native".into(), + driver, + None, + state.store.clone(), + crate::sandbox_index::SandboxIndex::new(), + crate::sandbox_watch::SandboxWatchBus::new(), + crate::tracing_bus::TracingLogBus::new(), + Arc::new(crate::supervisor_session::SupervisorSessionRegistry::new()), + ) + .await; + if !valid { + let error = compute.expect_err("invalid metadata must fail startup"); + assert!(error.to_string().contains("workload_compatibility")); + assert!(!error.to_string().contains("PRIVATE DIAGNOSTIC")); + continue; + } + let compute = compute.unwrap(); + Arc::get_mut(&mut state).unwrap().compute = compute; + let response = OpenShellService::new(state) + .get_gateway_info(Request::new(GetGatewayInfoRequest {})) + .await + .unwrap() + .into_inner(); + let reported = response.compute_drivers[0] + .capabilities + .as_ref() + .unwrap() + .workload_compatibility + .as_ref(); + assert_eq!(reported.is_some(), workload.is_some()); + if let Some(reported) = reported { + assert_eq!(reported.operating_system, "macos"); + assert_eq!(reported.architecture, "arm64"); + assert_eq!( + reported.execution_restrictions, + workload.unwrap().execution_restrictions + ); + } + } + } + #[test] fn public_resource_capabilities_preserves_reported_fields() { let driver_capabilities = DriverResourceCapabilities { diff --git a/crates/openshell-server/src/storage_proto.rs b/crates/openshell-server/src/storage_proto.rs index d8da420871..552815815b 100644 --- a/crates/openshell-server/src/storage_proto.rs +++ b/crates/openshell-server/src/storage_proto.rs @@ -177,8 +177,10 @@ mod tests { // selections absent; storage.v1 remains frozen. Public field 3 retains its // image wire identity, now with explicit presence. Compute configuration // updates use a separate capability/service and add one classified RPC. + // Workload compatibility adds one diagnostic-only public message. Startup + // snapshots are not persisted; the durable and overlap inventories stay fixed. const PUBLIC_RPC_SCHEMA_SHA256: &str = - "3b4c2af6cf22e7ed1d84279ff865acb0db7199717bee9bc041caef791334104d"; + "b3dea2db5539a655b8af3dd0ea2d7e6566644b4dec9e72fb7c53a5332b623438"; const DURABLE_SCHEMA_SHA256: &str = "cb15f8f79a855064a43fe8c6cd908bf07eca6604e2bf8ab7d2de0ef33e97da4e"; const PUBLIC_DURABLE_OVERLAP_SHA256: &str = @@ -723,7 +725,7 @@ mod tests { overlap_hash.as_str(), ), ( - (337, 31), + (338, 31), (100, 22), (86, 22), PUBLIC_RPC_SCHEMA_SHA256, diff --git a/crates/openshell-server/src/test_support.rs b/crates/openshell-server/src/test_support.rs index b1d5736a93..68bf926be7 100644 --- a/crates/openshell-server/src/test_support.rs +++ b/crates/openshell-server/src/test_support.rs @@ -180,6 +180,7 @@ impl FakeComputeDriver { supports_sandbox_authentication: false, driver_reports_runtime_readiness: false, resource_capabilities: None, + workload_compatibility: None, rootfs_tar_staging_dir: String::new(), rootfs_tar_max_bytes: 0, extension: Some(openshell_core::extension_protocol::extension_metadata( diff --git a/docs/how-it-works/sandboxes/runtimes.mdx b/docs/how-it-works/sandboxes/runtimes.mdx index 70f6891ce1..d7304b2956 100644 --- a/docs/how-it-works/sandboxes/runtimes.mdx +++ b/docs/how-it-works/sandboxes/runtimes.mdx @@ -23,6 +23,34 @@ the sandbox record and driver-supported persistent storage. | [Kubernetes](#kubernetes-driver) | `kubernetes` | Shared clusters, remote compute, and GPU scheduling. | | [Windows MXC](https://github.com/NVIDIA/OpenShell/blob/windows/WINDOWS.md) | `mxc` | Native Windows workloads on x64 or ARM64. | +## Inspect Workload Compatibility + +Before choosing commands for an agent, inspect the runtime used by its gateway: + +```shell +openshell gateway info +openshell gateway info --output json +``` + +`Workload platform` is the workload's operating system and architecture reported +by the configured driver. +It can differ from your workstation and the gateway host. For example, a +MicroVM gateway on an Apple silicon Mac reports `linux / arm64` for its workload. +Some drivers determine the architecture from the image or placement, so they +leave that value unreported. + +`Execution restrictions` lists fixed limits reported by the runtime. A native +runtime might explain that setuid executables cannot run, even when the +executable is readable. This can explain a system command failing with +`Operation not permitted` without suggesting a policy change can enable it. + +Older gateways and drivers may leave this information unreported. An empty +restriction list does not mean execution is unrestricted. This inspection +describes known runtime limits; it does not check installation health or predict +every command's result. Policy checks and the runtime still decide whether a +particular operation can proceed. Use JSON output when an agent or script needs +the same information. + ## Select a Compute Driver Set `compute_driver` in the gateway TOML file. A gateway uses one driver at a time. diff --git a/proto/compute_driver.proto b/proto/compute_driver.proto index 88d090fc18..77fbee0b92 100644 --- a/proto/compute_driver.proto +++ b/proto/compute_driver.proto @@ -157,6 +157,23 @@ message GetCapabilitiesResponse { // Gateways require an exact policy match before activating the driver. // Empty denotes a legacy driver and requires explicit admission opt-out. string resource_admission_policy = 13; + // Diagnostic workload facts from this configured driver. Absence means + // unreported, not the gateway host's platform or unrestricted execution. + WorkloadCompatibility workload_compatibility = 15; +} + +// Workload compatibility reported by the driver, not by the CLI or gateway host. +// These facts describe the runtime; they do not admit a particular command. +message WorkloadCompatibility { + // Workload OS, such as "linux", "windows", or "macos". Empty is unreported. + string operating_system = 1; + // Workload architecture in OCI form, such as "arm64" or "amd64". + // Empty is unreported, including when placement or the image determines it. + string architecture = 2; + // At most 16 fixed execution restrictions, each a nonempty plain-text + // explanation of at most 512 bytes. Platform strings are at most 64 bytes. + // An empty list does not establish that execution is unrestricted. + repeated string execution_restrictions = 3; } message AuthenticateSandboxRequest { diff --git a/proto/openshell.proto b/proto/openshell.proto index b40ec68263..dd0309c22c 100644 --- a/proto/openshell.proto +++ b/proto/openshell.proto @@ -1022,6 +1022,23 @@ message ComputeDriverCapabilities { // Static portable resource request forms reported by the driver. ResourceCapabilities resource_capabilities = 3; + + // Diagnostic workload facts reported by this configured driver. + // Absence means unreported, not the gateway host's platform. + WorkloadCompatibility workload_compatibility = 4; +} + +// Workload compatibility from the driver's startup capability snapshot. +// This describes the runtime, not installation health or command admission. +message WorkloadCompatibility { + // Workload OS, such as "linux", "windows", or "macos". Empty is unreported. + string operating_system = 1; + // Workload architecture in OCI form, such as "arm64" or "amd64". + // Empty is unreported, including when placement or the image determines it. + string architecture = 2; + // Known fixed execution restrictions, each a short plain-text explanation. + // An empty list does not establish that execution is unrestricted. + repeated string execution_restrictions = 3; } // Static portable resource request forms reported by a compute driver.