diff --git a/AGENTS.md b/AGENTS.md index b049c28f50..28e03ee2cc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -57,7 +57,7 @@ Do not rely on this file for a full inventory. The detailed public and contribut | `crates/openshell-supervisor-middleware/` | Middleware runtime | Generic middleware registry, remote service integration, and chain execution | | `crates/openshell-supervisor-middleware-builtins/` | Built-in middleware | First-party in-process middleware implementations | | `crates/openshell-supervisor-network/` | Network supervisor | Proxying, L7 enforcement, policy evaluation, and provider credential injection | -| `crates/openshell-supervisor-process/` | Process supervisor | Process lifecycle, namespace, and bypass monitoring | +| `crates/openshell-supervisor-process/` | Process supervisor | SSH access, gateway session, log forwarding, and the OTLP telemetry relay | | `crates/openshell-vfio/` | VFIO support | PCI and GPU passthrough preparation and lifecycle | | `python/openshell/` | Python SDK | Python bindings and CLI packaging | | `sdk/typescript/` | TypeScript SDK | Native Connect client, curated sandbox API, and generated protobuf types | diff --git a/Cargo.lock b/Cargo.lock index c6173fee09..f304664718 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1128,6 +1128,18 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "const-hex" +version = "1.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e2a781ebdf4467d1428dc4593067825fb646f6871475098d8577421af73558" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "proptest", + "serde_core", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -4678,6 +4690,7 @@ dependencies = [ "openshell-supervisor-middleware", "openshell-supervisor-middleware-builtins", "opentelemetry", + "opentelemetry-proto", "opentelemetry_sdk", "petname", "pin-project-lite", @@ -4748,6 +4761,7 @@ dependencies = [ "openshell-supervisor-middleware-builtins", "openshell-supervisor-network", "openshell-supervisor-process", + "opentelemetry-proto", "prost", "prost-types", "rustix 1.1.4", @@ -4863,17 +4877,23 @@ dependencies = [ "base64 0.22.1", "bytes", "hex", + "http-body-util", + "hyper", + "hyper-util", "libc", "miette", "nix 0.29.0", "openshell-core", "openshell-isolation-interface", "openshell-ocsf", + "opentelemetry-proto", + "prost", "rand 0.10.2", "russh", "serde_json", "sha2 0.10.9", "tempfile", + "thiserror 2.0.20", "tokio", "tokio-stream", "tonic", @@ -4972,9 +4992,12 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56d658ba1faf63f7b9c492cfbe6e0ec365440a16132d3270c1065f7b33f1b638" dependencies = [ + "base64 0.22.1", + "const-hex", "opentelemetry", "opentelemetry_sdk", "prost", + "serde", "tonic", "tonic-prost", ] @@ -5521,6 +5544,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bitflags", + "num-traits", + "rand 0.9.4", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax", + "unarray", +] + [[package]] name = "prost" version = "0.14.3" @@ -5849,6 +5887,15 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.5", +] + [[package]] name = "rand_xoshiro" version = "0.7.0" @@ -7996,6 +8043,12 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicase" version = "2.9.0" diff --git a/Cargo.toml b/Cargo.toml index a50457f136..ba97bee817 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,6 +71,7 @@ tracing-appender = "0.2" opentelemetry = "0.32" opentelemetry_sdk = { version = "0.32", features = ["rt-tokio"] } opentelemetry-otlp = { version = "0.32", default-features = false, features = ["grpc-tonic", "trace"] } +opentelemetry-proto = { version = "0.32", default-features = false, features = ["gen-tonic", "trace", "with-serde"] } tracing-opentelemetry = { version = "0.33", default-features = false, features = ["tracing-log"] } # Metrics diff --git a/crates/openshell-core/src/sandbox_env.rs b/crates/openshell-core/src/sandbox_env.rs index 9ac843cecd..bf59c96d2d 100644 --- a/crates/openshell-core/src/sandbox_env.rs +++ b/crates/openshell-core/src/sandbox_env.rs @@ -266,11 +266,67 @@ pub const DEFAULT_SANDBOX_GID: u32 = 1000; /// OCI only for the former contract. pub const OCI_IMAGE_USER: &str = "OPENSHELL_OCI_IMAGE_USER"; +/// Standard OpenTelemetry environment variable for the OTLP exporter endpoint. +/// +/// The gateway sets it in the sandbox environment when +/// `[openshell.gateway.otlp]` is configured, pointing agent SDKs at +/// [`OTLP_RELAY_ENDPOINT`]. A value the user declared explicitly wins. +pub const OTEL_EXPORTER_OTLP_ENDPOINT: &str = "OTEL_EXPORTER_OTLP_ENDPOINT"; + +/// Standard OpenTelemetry environment variable for the OTLP exporter protocol. +/// +/// Set to `http/protobuf` alongside [`OTEL_EXPORTER_OTLP_ENDPOINT`]. +pub const OTEL_EXPORTER_OTLP_PROTOCOL: &str = "OTEL_EXPORTER_OTLP_PROTOCOL"; + +/// Reserved destination agent processes export OTLP to. +/// +/// This address is never routed. A workload `connect()` to any non-loopback +/// address is intercepted by the sandbox seccomp broker and staged for the +/// supervisor, which recognises this destination and serves the OTLP +/// receiver on the staged stream itself instead of dialing upstream. So the +/// value is a label the supervisor switches on, and it must stay outside +/// loopback (loopback connects complete locally without mediation) and +/// outside `198.18.0.0/15`, which the policy DNS runtime uses for synthetic +/// answers. `192.0.0.8` is the IPv4 dummy address (RFC 7600): reserved, +/// unroutable, and never assigned to a real service. +pub const OTLP_RELAY_ADDR: &str = "192.0.0.8:4318"; + +/// [`OTLP_RELAY_ADDR`] as the URL injected through +/// [`OTEL_EXPORTER_OTLP_ENDPOINT`]. +pub const OTLP_RELAY_ENDPOINT: &str = "http://192.0.0.8:4318"; + // The corporate upstream-proxy configuration deliberately has no reserved // environment variables: it travels on the supervisor's argv // (`--upstream-proxy` and friends), which a sandbox image cannot forge the // way it could bake `ENV` values. +#[cfg(test)] +mod otlp_relay_address_tests { + use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + + use super::{OTLP_RELAY_ADDR, OTLP_RELAY_ENDPOINT}; + + #[test] + fn relay_address_is_an_unroutable_non_loopback_label() { + let addr: SocketAddr = OTLP_RELAY_ADDR.parse().expect("relay address parses"); + assert_eq!(addr.port(), 4318, "OTLP HTTP default port"); + let IpAddr::V4(ip) = addr.ip() else { + panic!("relay address must be IPv4"); + }; + assert!( + !ip.is_loopback(), + "loopback would bypass seccomp mediation and never reach the supervisor" + ); + assert_eq!(ip, Ipv4Addr::new(192, 0, 0, 8), "RFC 7600 dummy address"); + let [a, b, ..] = ip.octets(); + assert!( + !(a == 198 && (b == 18 || b == 19)), + "198.18.0.0/15 is the policy DNS synthetic answer pool" + ); + assert_eq!(OTLP_RELAY_ENDPOINT, format!("http://{OTLP_RELAY_ADDR}")); + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/openshell-gateway/src/vm.rs b/crates/openshell-gateway/src/vm.rs index d0c00cd786..1f07f0ad1b 100644 --- a/crates/openshell-gateway/src/vm.rs +++ b/crates/openshell-gateway/src/vm.rs @@ -810,6 +810,7 @@ mod tests { Some(&OtlpConfig { endpoint: "http://collector.internal:4317".to_string(), service_name: Some("custom-gateway".to_string()), + agent_endpoint: None, }), "production-us-west", ); diff --git a/crates/openshell-ocsf/src/lib.rs b/crates/openshell-ocsf/src/lib.rs index ba8eb70f43..8635da0889 100644 --- a/crates/openshell-ocsf/src/lib.rs +++ b/crates/openshell-ocsf/src/lib.rs @@ -64,6 +64,7 @@ pub use builders::{ // --- Tracing layers --- pub use tracing_layers::{ - OCSF_TARGET, OcsfJsonlLayer, OcsfShorthandLayer, clear_current_event, clone_current_event, - emit_ocsf_event, emit_ocsf_event_routed, set_current_event, + OCSF_TARGET, OcsfJsonlLayer, OcsfRelayLayer, OcsfRelaySink, OcsfShorthandLayer, + clear_current_event, clone_current_event, emit_ocsf_event, emit_ocsf_event_routed, + set_current_event, }; diff --git a/crates/openshell-ocsf/src/tracing_layers/mod.rs b/crates/openshell-ocsf/src/tracing_layers/mod.rs index b57ba1364a..3c4565b691 100644 --- a/crates/openshell-ocsf/src/tracing_layers/mod.rs +++ b/crates/openshell-ocsf/src/tracing_layers/mod.rs @@ -9,6 +9,7 @@ pub(crate) mod event_bridge; mod jsonl_layer; +mod relay_layer; mod shorthand_layer; pub use event_bridge::{ @@ -16,4 +17,5 @@ pub use event_bridge::{ set_current_event, }; pub use jsonl_layer::OcsfJsonlLayer; +pub use relay_layer::{OcsfRelayLayer, OcsfRelaySink}; pub use shorthand_layer::OcsfShorthandLayer; diff --git a/crates/openshell-ocsf/src/tracing_layers/relay_layer.rs b/crates/openshell-ocsf/src/tracing_layers/relay_layer.rs new file mode 100644 index 0000000000..a4949924d5 --- /dev/null +++ b/crates/openshell-ocsf/src/tracing_layers/relay_layer.rs @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Tracing layer that captures OCSF events and forwards them as JSON bytes +//! through a telemetry buffer sender for relay to the gateway. + +use std::sync::Arc; + +use tracing::Subscriber; +use tracing_subscriber::Layer; +use tracing_subscriber::layer::Context; + +use super::event_bridge::{OCSF_TARGET, clone_current_event}; + +/// Callback trait for delivering serialized OCSF events. +pub trait OcsfRelaySink: Send + Sync + 'static { + fn send(&self, json_bytes: Vec); +} + +/// A tracing layer that captures OCSF events and serializes them to JSON +/// for relay through the telemetry transport. +pub struct OcsfRelayLayer { + sink: Arc, +} + +impl OcsfRelayLayer { + pub fn new(sink: Arc) -> Self { + Self { sink } + } +} + +impl Layer for OcsfRelayLayer { + fn on_event(&self, event: &tracing::Event<'_>, _ctx: Context<'_, S>) { + if event.metadata().target() != OCSF_TARGET { + return; + } + + let Some(ocsf_event) = clone_current_event() else { + return; + }; + + if let Ok(json) = serde_json::to_vec(&ocsf_event) { + self.sink.send(json); + } + } +} diff --git a/crates/openshell-otel/src/lib.rs b/crates/openshell-otel/src/lib.rs index 7a9162ab92..819265fb1e 100644 --- a/crates/openshell-otel/src/lib.rs +++ b/crates/openshell-otel/src/lib.rs @@ -5,7 +5,7 @@ mod driver; mod grpc; -mod propagation; +pub mod propagation; pub use driver::{ BoxGrpcStream, ComputeDriverTracing, DriverTracingConfig, DriverTracingHandle, diff --git a/crates/openshell-otel/src/propagation.rs b/crates/openshell-otel/src/propagation.rs index c6e51a0a6b..6a9b1498c9 100644 --- a/crates/openshell-otel/src/propagation.rs +++ b/crates/openshell-otel/src/propagation.rs @@ -54,6 +54,22 @@ impl Injector for MetadataMapInjector<'_> { } } +/// Writes OpenTelemetry propagation fields to HTTP headers. +#[derive(Debug)] +pub struct HeaderMapInjector<'a>(pub &'a mut HeaderMap); + +impl Injector for HeaderMapInjector<'_> { + fn set(&mut self, key: &str, value: String) { + let Ok(key) = key.parse::() else { + return; + }; + let Ok(value) = value.parse() else { + return; + }; + self.0.insert(key, value); + } +} + #[derive(Debug)] struct TraceContextMapInjector<'a>(&'a mut BTreeMap); @@ -75,6 +91,15 @@ pub fn current_trace_context_carrier() -> Option> { carrier.contains_key("traceparent").then_some(carrier) } +/// Inject W3C traceparent into HTTP headers if not already present. +pub fn inject_traceparent_if_missing(headers: &mut HeaderMap) { + if headers.contains_key("traceparent") { + return; + } + let context = tracing::Span::current().context(); + TraceContextPropagator::new().inject_context(&context, &mut HeaderMapInjector(headers)); +} + /// Injects the active W3C trace context into an outbound tonic request. #[derive(Debug, Clone, Copy)] pub struct TraceContextInterceptor; diff --git a/crates/openshell-server/Cargo.toml b/crates/openshell-server/Cargo.toml index 1c206b70ec..09b21d5742 100644 --- a/crates/openshell-server/Cargo.toml +++ b/crates/openshell-server/Cargo.toml @@ -75,6 +75,7 @@ tracing-appender = { workspace = true } # OpenTelemetry (OTLP trace export, opt-in via [openshell.gateway.otlp]) opentelemetry = { workspace = true } opentelemetry_sdk = { workspace = true } +opentelemetry-proto = { workspace = true } tracing-opentelemetry = { workspace = true } # Metrics diff --git a/crates/openshell-server/src/config_file.rs b/crates/openshell-server/src/config_file.rs index e442607d9d..ddf94483f4 100644 --- a/crates/openshell-server/src/config_file.rs +++ b/crates/openshell-server/src/config_file.rs @@ -288,6 +288,19 @@ pub struct OtlpConfig { /// `service.name` resource attribute. Defaults to `openshell-gateway`. #[serde(default)] pub service_name: Option, + + /// OTLP/gRPC collector endpoint for relayed agent traces. When absent, + /// agent traces go to `endpoint`, so one collector serves both lanes + /// unless the operator splits them. + #[serde(default)] + pub agent_endpoint: Option, +} + +impl OtlpConfig { + /// The collector endpoint for the agent-trace lane. + pub fn agent_lane_endpoint(&self) -> &str { + self.agent_endpoint.as_deref().unwrap_or(&self.endpoint) + } } /// `[openshell.supervisor]` section. @@ -921,6 +934,21 @@ service_name = "openshell-gateway-dev" "http://otel-collector.observability.svc:4317" ); assert_eq!(otlp.service_name.as_deref(), Some("openshell-gateway-dev")); + assert_eq!(otlp.agent_lane_endpoint(), otlp.endpoint); + } + + #[test] + fn otlp_agent_endpoint_splits_the_agent_lane() { + let toml = r#" +[openshell.gateway.otlp] +endpoint = "http://infra-collector:4317" +agent_endpoint = "http://agent-collector:4317" +"#; + let tmp = write_tmp(toml); + let file = load(tmp.path()).expect("valid otlp config parses"); + let otlp = file.openshell.gateway.otlp.expect("otlp config"); + assert_eq!(otlp.endpoint, "http://infra-collector:4317"); + assert_eq!(otlp.agent_lane_endpoint(), "http://agent-collector:4317"); } #[test] diff --git a/crates/openshell-server/src/grpc/provider_readiness_tests.rs b/crates/openshell-server/src/grpc/provider_readiness_tests.rs index d83bb80ef4..f084230ae9 100644 --- a/crates/openshell-server/src/grpc/provider_readiness_tests.rs +++ b/crates/openshell-server/src/grpc/provider_readiness_tests.rs @@ -71,6 +71,7 @@ fn hello() -> SupervisorHello { sandbox_id: Uuid::new_v4().to_string(), instance_id: Uuid::new_v4().to_string(), connection_epoch: 0, + capabilities: Vec::new(), supports_provider_readiness: true, } } diff --git a/crates/openshell-server/src/grpc/sandbox.rs b/crates/openshell-server/src/grpc/sandbox.rs index 074b2590e0..32f58f51d2 100644 --- a/crates/openshell-server/src/grpc/sandbox.rs +++ b/crates/openshell-server/src/grpc/sandbox.rs @@ -553,6 +553,11 @@ async fn handle_create_sandbox_inner( *policy = validate_and_canonicalize_policy(policy.clone())?; } + // Point the workload's OTel SDK at the supervisor relay when this gateway + // can accept relayed telemetry. The values persist in the stored spec, so + // restarts inherit them and `sandbox get` shows where traces go. + inject_otel_relay_environment(&mut spec.environment, state.otel_relay_exporter.is_some()); + // Process identity and MCP default materialization can increase the // protobuf size. Recheck the exact canonical spec before any middleware or // compute boundary can observe or persist it. The initial check remains @@ -777,6 +782,31 @@ fn validate_create_sandbox_request_pre_io( Ok(()) } +/// Set `OTEL_EXPORTER_OTLP_ENDPOINT` and `OTEL_EXPORTER_OTLP_PROTOCOL` to the +/// supervisor relay when `otel_relay_enabled`, unless the caller already chose +/// an endpoint. A caller-supplied endpoint is left untouched together with its +/// protocol, since both describe their collector rather than ours. Returns +/// whether anything was injected. +fn inject_otel_relay_environment( + environment: &mut HashMap, + otel_relay_enabled: bool, +) -> bool { + use openshell_core::sandbox_env::{ + OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_PROTOCOL, OTLP_RELAY_ENDPOINT, + }; + if !otel_relay_enabled || environment.contains_key(OTEL_EXPORTER_OTLP_ENDPOINT) { + return false; + } + environment.insert( + OTEL_EXPORTER_OTLP_ENDPOINT.to_string(), + OTLP_RELAY_ENDPOINT.to_string(), + ); + environment + .entry(OTEL_EXPORTER_OTLP_PROTOCOL.to_string()) + .or_insert_with(|| "http/protobuf".to_string()); + true +} + fn validate_template_create_governance_spec(spec: &SandboxSpec) -> Result<(), Status> { if !spec.log_level.is_empty() { return Err(Status::invalid_argument( @@ -3937,6 +3967,123 @@ mod tests { GpuResourceRequirements, SandboxServiceExposure, ServiceAuthorizationMode, ServiceEndpoint, }; + // ---- OTLP relay environment ---- + + async fn test_server_state_with_otel_relay() -> Arc { + let mut state = test_server_state().await; + Arc::get_mut(&mut state) + .expect("test server state should be uniquely owned") + .otel_relay_exporter = Some(Arc::new( + crate::otel_relay::OtelRelayExporter::lazy_for_test(), + )); + state + } + + fn otel_create_request( + name: &str, + environment: HashMap, + ) -> CreateSandboxRequest { + CreateSandboxRequest { + name: name.to_string(), + spec: Some(SandboxSpec { + environment, + ..Default::default() + }), + labels: HashMap::new(), + annotations: HashMap::new(), + workspace_scope: Some(openshell_core::proto::workspace_selector("default")), + await_main_process_attachment: false, + workload_template: String::new(), + service_exposures: Vec::new(), + request_id: String::new(), + } + } + + #[test] + fn inject_otel_relay_environment_sets_endpoint_and_protocol_when_enabled() { + let mut env = HashMap::from([("HOME".to_string(), "/home/user".to_string())]); + assert!(inject_otel_relay_environment(&mut env, true)); + assert_eq!( + env.get("OTEL_EXPORTER_OTLP_ENDPOINT").map(String::as_str), + Some(openshell_core::sandbox_env::OTLP_RELAY_ENDPOINT) + ); + assert_eq!( + env.get("OTEL_EXPORTER_OTLP_PROTOCOL").map(String::as_str), + Some("http/protobuf") + ); + assert_eq!(env.get("HOME").map(String::as_str), Some("/home/user")); + } + + #[test] + fn inject_otel_relay_environment_is_a_noop_without_a_relay() { + let mut env = HashMap::new(); + assert!(!inject_otel_relay_environment(&mut env, false)); + assert!(env.is_empty()); + } + + #[test] + fn inject_otel_relay_environment_keeps_a_caller_supplied_endpoint() { + let mut env = HashMap::from([( + "OTEL_EXPORTER_OTLP_ENDPOINT".to_string(), + "https://collector.example:4317".to_string(), + )]); + assert!(!inject_otel_relay_environment(&mut env, true)); + assert_eq!( + env.get("OTEL_EXPORTER_OTLP_ENDPOINT").map(String::as_str), + Some("https://collector.example:4317") + ); + assert!( + !env.contains_key("OTEL_EXPORTER_OTLP_PROTOCOL"), + "the caller's collector keeps the caller's protocol choice" + ); + } + + #[tokio::test] + async fn create_sandbox_injects_relay_endpoint_when_gateway_has_an_exporter() { + let state = test_server_state_with_otel_relay().await; + let created = handle_create_sandbox( + &state, + authed_request(otel_create_request("otel-relay", HashMap::new())), + ) + .await + .expect("create should succeed") + .into_inner() + .sandbox + .expect("created sandbox"); + + let env = created.spec.expect("resolved sandbox spec").environment; + assert_eq!( + env.get("OTEL_EXPORTER_OTLP_ENDPOINT").map(String::as_str), + Some(openshell_core::sandbox_env::OTLP_RELAY_ENDPOINT) + ); + assert_eq!( + env.get("OTEL_EXPORTER_OTLP_PROTOCOL").map(String::as_str), + Some("http/protobuf") + ); + } + + #[tokio::test] + async fn create_sandbox_leaves_environment_alone_without_an_exporter() { + let state = test_server_state().await; + let created = handle_create_sandbox( + &state, + authed_request(otel_create_request( + "no-relay", + HashMap::from([("HOME".to_string(), "/home/user".to_string())]), + )), + ) + .await + .expect("create should succeed") + .into_inner() + .sandbox + .expect("created sandbox"); + + let env = created.spec.expect("resolved sandbox spec").environment; + assert_eq!(env.get("HOME").map(String::as_str), Some("/home/user")); + assert!(!env.contains_key("OTEL_EXPORTER_OTLP_ENDPOINT")); + assert!(!env.contains_key("OTEL_EXPORTER_OTLP_PROTOCOL")); + } + // ---- shell_escape ---- #[test] diff --git a/crates/openshell-server/src/lib.rs b/crates/openshell-server/src/lib.rs index 72efa01fbf..ef943e3870 100644 --- a/crates/openshell-server/src/lib.rs +++ b/crates/openshell-server/src/lib.rs @@ -28,6 +28,7 @@ mod http; mod middleware; mod multiplex; mod ocsf_log; +pub(crate) mod otel_relay; mod otel_tracing; mod pagination; mod persistence; @@ -356,6 +357,10 @@ pub struct ServerState { /// Empty when OIDC is not configured — `authorize_workspace()` treats /// every authenticated user as Platform Admin in that case. pub admin_role: String, + + /// Dedicated OTLP exporter for relayed telemetry from supervisors. + /// `None` when the gateway has no OTLP endpoint configured. + pub otel_relay_exporter: Option>, } fn is_benign_tls_handshake_failure(error: &std::io::Error) -> bool { @@ -450,6 +455,7 @@ impl ServerState { provider_profile_sources: provider_profile_sources::ProviderProfileSources::with_default_sources(), admin_role, + otel_relay_exporter: None, } } } @@ -741,6 +747,7 @@ pub(crate) async fn run_server( state.gateway_interceptors = gateway_interceptors; state.provider_profile_sources = provider_profile_sources; state.extension_jwt_issuer = extension_jwt_issuer.clone(); + state.otel_relay_exporter = otel_relay::try_create_exporter(config_file.as_ref()).await; state.sandbox_session_jwt_authority = sandbox_session_jwt_authority; if let Some(issuer) = extension_jwt_issuer { spawn_gateway_extension_token_refresh(issuer, gateway_extension_credentials); diff --git a/crates/openshell-server/src/otel_relay.rs b/crates/openshell-server/src/otel_relay.rs new file mode 100644 index 0000000000..ecf2eb4740 --- /dev/null +++ b/crates/openshell-server/src/otel_relay.rs @@ -0,0 +1,138 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Dedicated OTLP exporter for relayed telemetry from supervisors. +//! +//! Uses a separate gRPC client to forward pre-enriched trace data to the +//! configured OTLP collector, bypassing the gateway's own `SdkTracerProvider` +//! which would overwrite resource attributes. + +use std::sync::Arc; + +use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest; +use prost::Message; +use tonic::transport::Channel; +use tracing::{debug, info}; + +use opentelemetry_proto::tonic::collector::trace::v1::trace_service_client::TraceServiceClient; + +/// Exporter that forwards raw protobuf-encoded trace data to an OTLP collector. +#[derive(Debug, Clone)] +pub struct OtelRelayExporter { + client: TraceServiceClient, +} + +impl OtelRelayExporter { + /// Connect to the OTLP collector at the given gRPC endpoint. + pub async fn connect(endpoint: &str) -> Result { + let channel = Channel::from_shared(endpoint.to_string()) + .map_err(|e| ConnectError::InvalidUri(e.to_string()))? + .connect() + .await + .map_err(ConnectError::Transport)?; + Ok(Self { + client: TraceServiceClient::new(channel), + }) + } + + /// An exporter over a lazy channel that performs no I/O until the first + /// RPC, for tests that only need "a relay exporter is configured". + #[cfg(test)] + pub(crate) fn lazy_for_test() -> Self { + Self { + client: TraceServiceClient::new( + Channel::from_static("http://127.0.0.1:1").connect_lazy(), + ), + } + } + + /// Export raw protobuf-encoded `ExportTraceServiceRequest` bytes. + pub async fn export_raw(&self, trace_data: Vec) -> Result<(), ExportError> { + let request = ExportTraceServiceRequest::decode(trace_data.as_slice()) + .map_err(ExportError::Decode)?; + + let mut client = self.client.clone(); + client + .export(tonic::Request::new(request)) + .await + .map_err(ExportError::Grpc)?; + + Ok(()) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum ExportError { + #[error("failed to decode trace data: {0}")] + Decode(prost::DecodeError), + #[error("gRPC export failed: {0}")] + Grpc(tonic::Status), +} + +#[derive(Debug, thiserror::Error)] +pub enum ConnectError { + #[error("invalid OTLP endpoint URI: {0}")] + InvalidUri(String), + #[error("transport error: {0}")] + Transport(tonic::transport::Error), +} + +/// Create a relay exporter from the gateway's OTLP config, if configured. +pub async fn try_create_exporter( + config_file: Option<&crate::config_file::ConfigFile>, +) -> Option> { + let Some(cf) = config_file else { + debug!("no config file; OTEL relay disabled"); + return None; + }; + let Some(otlp) = cf.openshell.gateway.otlp.as_ref() else { + debug!("no [openshell.gateway.otlp] section in config; OTEL relay disabled"); + return None; + }; + // Agent traces ride their own lane: `agent_endpoint` when the operator + // split the collectors, otherwise the shared infrastructure endpoint. + let endpoint = otlp.agent_lane_endpoint(); + match OtelRelayExporter::connect(endpoint).await { + Ok(exporter) => { + info!( + endpoint, + dedicated_lane = otlp.agent_endpoint.is_some(), + "OTEL relay exporter connected" + ); + Some(Arc::new(exporter)) + } + Err(e) => { + tracing::warn!( + endpoint, + error = %e, + "failed to connect OTEL relay exporter; relay disabled" + ); + None + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn connect_rejects_invalid_endpoint_uri() { + let err = OtelRelayExporter::connect("not a valid uri") + .await + .expect_err("an unparsable endpoint must not connect"); + assert!(matches!(err, ConnectError::InvalidUri(_)), "{err}"); + } + + #[tokio::test] + async fn export_raw_rejects_undecodable_trace_bytes() { + // A lazy channel performs no I/O, so the decode failure surfaces + // before any RPC is attempted and no collector is needed. + let exporter = OtelRelayExporter::lazy_for_test(); + let err = exporter + .export_raw(vec![0xff, 0xff, 0xff]) + .await + .expect_err("garbage bytes must not decode as ExportTraceServiceRequest"); + assert!(matches!(err, ExportError::Decode(_)), "{err}"); + } +} diff --git a/crates/openshell-server/src/otel_tracing.rs b/crates/openshell-server/src/otel_tracing.rs index cdae552871..7e327a9a7c 100644 --- a/crates/openshell-server/src/otel_tracing.rs +++ b/crates/openshell-server/src/otel_tracing.rs @@ -296,6 +296,7 @@ mod tests { OtlpConfig { endpoint: "http://127.0.0.1:4317".into(), service_name: None, + agent_endpoint: None, } } diff --git a/crates/openshell-server/src/storage_proto.rs b/crates/openshell-server/src/storage_proto.rs index e6d8730aa0..da399bba07 100644 --- a/crates/openshell-server/src/storage_proto.rs +++ b/crates/openshell-server/src/storage_proto.rs @@ -129,7 +129,7 @@ mod tests { // Service authorization also extends both schemas additively. Legacy // payloads retain the safe Strip default. const PUBLIC_RPC_SCHEMA_SHA256: &str = - "2e156c6ad3c8eb51bcd30dc13b173fe339b38207a1b1f98f7be2e0cad8e3bd45"; + "6a4d7454ce750a9e8e256154ce1839188f9be6238b7a41f24373919a7cb38ee3"; const DURABLE_SCHEMA_SHA256: &str = "38165d9d76f49fcfe98a12f241e032838a2376c1d1a87ea2796fd33b9b1a3541"; const PUBLIC_DURABLE_OVERLAP_SHA256: &str = @@ -598,7 +598,7 @@ mod tests { overlap_hash.as_str(), ), ( - (306, 27), + (307, 27), (93, 21), (81, 21), PUBLIC_RPC_SCHEMA_SHA256, diff --git a/crates/openshell-server/src/supervisor_session.rs b/crates/openshell-server/src/supervisor_session.rs index 7fc11594af..41a6b53c36 100644 --- a/crates/openshell-server/src/supervisor_session.rs +++ b/crates/openshell-server/src/supervisor_session.rs @@ -1991,7 +1991,10 @@ async fn establish_supervisor_session( return Err(error); } - // Step 3: Send SessionAccepted. + // Step 3: Determine confirmed capabilities. + let confirmed_capabilities = confirm_capabilities(&hello.capabilities, &state); + + // Step 4: Send SessionAccepted. let accepted = GatewayMessage { payload: Some(gateway_message::Payload::SessionAccepted(SessionAccepted { session_id: session_id.clone(), @@ -1999,6 +2002,7 @@ async fn establish_supervisor_session( u64::from(HEARTBEAT_INTERVAL_SECS), )) .ok(), + capabilities: confirmed_capabilities, })), }; if tx.send(accepted).await.is_err() { @@ -2339,17 +2343,135 @@ async fn handle_supervisor_message( "supervisor session: relay closed by supervisor" ); } + Some(supervisor_message::Payload::OtelExport(otel_data)) => { + handle_otel_export(state, sandbox_id, session_id, otel_data); + } _ => { - warn!( + debug!( sandbox_id = %sandbox_id, session_id = %session_id, - "supervisor session: unexpected message type" + "supervisor session: unknown message type (future extension)" ); } } true } +/// Check which advertised capabilities the gateway can confirm. +fn confirm_capabilities(advertised: &[String], state: &Arc) -> Vec { + let confirmed = select_confirmed_capabilities(advertised, state.otel_relay_exporter.is_some()); + if !confirmed.is_empty() { + info!(capabilities = ?confirmed, "confirmed supervisor capabilities"); + } + confirmed +} + +/// Keep each advertised capability the gateway can actually serve and drop +/// the rest. `otel_relay_enabled` reflects whether `[openshell.gateway.otlp]` +/// produced a relay exporter at startup. +fn select_confirmed_capabilities(advertised: &[String], otel_relay_enabled: bool) -> Vec { + let mut confirmed = Vec::new(); + for cap in advertised { + match cap.as_str() { + "otel_export" if otel_relay_enabled => { + confirmed.push(cap.clone()); + } + "otel_export" => { + debug!( + capability = "otel_export", + "supervisor advertised otel_export but gateway has no \ + [openshell.gateway.otlp] config; capability not confirmed" + ); + } + other => { + debug!(capability = %other, "ignoring unknown supervisor capability"); + } + } + } + confirmed +} + +/// Upper bound on a single relayed OCSF event; larger events are skipped. +const MAX_OCSF_EVENT_SIZE: usize = 256 * 1024; + +/// Why a relayed OCSF event was not emitted. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum OcsfEventRejection { + TooLarge, + NotUtf8, + NotJson, +} + +/// Accept a relayed OCSF event only when it is within the size bound and is +/// valid UTF-8 JSON. Returns the event text ready for the `ocsf_relay` log +/// target. +fn relayable_ocsf_event(event: &[u8]) -> Result<&str, OcsfEventRejection> { + if event.len() > MAX_OCSF_EVENT_SIZE { + return Err(OcsfEventRejection::TooLarge); + } + let json_str = std::str::from_utf8(event).map_err(|_| OcsfEventRejection::NotUtf8)?; + if serde_json::from_str::(json_str).is_err() { + return Err(OcsfEventRejection::NotJson); + } + Ok(json_str) +} + +/// Handle incoming OTEL export data from the supervisor: forward trace data to +/// the configured OTLP collector and dispatch OCSF events to the log sink. +fn handle_otel_export( + state: &Arc, + sandbox_id: &str, + session_id: &str, + otel_data: openshell_core::proto::OtelExportData, +) { + if let Some(openshell_core::proto::otel_export_data::Signal::TraceData(trace_data)) = + otel_data.signal + && !trace_data.is_empty() + && let Some(relay_exporter) = state.otel_relay_exporter.as_ref() + { + let exporter = relay_exporter.clone(); + let sandbox_id = sandbox_id.to_string(); + tokio::spawn(async move { + match tokio::time::timeout(Duration::from_secs(10), exporter.export_raw(trace_data)) + .await + { + Ok(Err(e)) => { + debug!( + sandbox_id = %sandbox_id, + error = %e, + "OTEL relay: failed to export trace data" + ); + } + Err(_) => { + debug!( + sandbox_id = %sandbox_id, + "OTEL relay: export timed out" + ); + } + Ok(Ok(())) => {} + } + }); + } + + for ocsf_event in &otel_data.ocsf_events { + match relayable_ocsf_event(ocsf_event) { + Ok(json_str) => info!( + target: "ocsf_relay", + sandbox_id = %sandbox_id, + session_id = %session_id, + ocsf_json = %json_str, + "relayed OCSF event" + ), + Err(reason) => debug!( + sandbox_id = %sandbox_id, + size = ocsf_event.len(), + reason = ?reason, + "OTEL relay: skipping OCSF event" + ), + } + } +} + // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- @@ -2590,6 +2712,81 @@ mod tests { } } + // ---- capability negotiation and OCSF relay filtering ---- + + fn caps(list: &[&str]) -> Vec { + list.iter().map(|s| (*s).to_string()).collect() + } + + #[test] + fn select_confirmed_capabilities_keeps_otel_export_when_relay_enabled() { + let confirmed = select_confirmed_capabilities(&caps(&["otel_export"]), true); + assert_eq!(confirmed, caps(&["otel_export"])); + } + + #[test] + fn select_confirmed_capabilities_drops_otel_export_without_relay() { + let confirmed = select_confirmed_capabilities(&caps(&["otel_export"]), false); + assert!(confirmed.is_empty()); + } + + #[test] + fn select_confirmed_capabilities_ignores_unknown_capabilities() { + let confirmed = + select_confirmed_capabilities(&caps(&["metrics_export", "otel_export", "bogus"]), true); + assert_eq!(confirmed, caps(&["otel_export"])); + } + + #[test] + fn select_confirmed_capabilities_empty_advertisement_confirms_nothing() { + assert!(select_confirmed_capabilities(&[], true).is_empty()); + } + + #[test] + fn relayable_ocsf_event_accepts_bounded_valid_json() { + assert_eq!( + relayable_ocsf_event(br#"{"class_uid":4001}"#), + Ok(r#"{"class_uid":4001}"#) + ); + } + + #[test] + fn relayable_ocsf_event_accepts_event_exactly_at_size_limit() { + // The bound is strictly greater-than, so an event of exactly + // MAX_OCSF_EVENT_SIZE bytes still passes. `{` + padding + `}` is an + // empty JSON object. + let mut at_limit = vec![b' '; MAX_OCSF_EVENT_SIZE - 2]; + at_limit.insert(0, b'{'); + at_limit.push(b'}'); + assert_eq!(at_limit.len(), MAX_OCSF_EVENT_SIZE); + assert!(relayable_ocsf_event(&at_limit).is_ok()); + } + + #[test] + fn relayable_ocsf_event_rejects_oversized_event() { + let oversized = vec![b' '; MAX_OCSF_EVENT_SIZE + 1]; + assert_eq!( + relayable_ocsf_event(&oversized), + Err(OcsfEventRejection::TooLarge) + ); + } + + #[test] + fn relayable_ocsf_event_rejects_non_utf8() { + assert_eq!( + relayable_ocsf_event(&[0xff, 0xfe, b'{']), + Err(OcsfEventRejection::NotUtf8) + ); + } + + #[test] + fn relayable_ocsf_event_rejects_invalid_json() { + assert_eq!( + relayable_ocsf_event(b"{not json"), + Err(OcsfEventRejection::NotJson) + ); + } + #[test] fn endpoint_status_projection_requires_initialized_live_authority() { use openshell_core::proto::{ diff --git a/crates/openshell-supervisor-network/src/host.rs b/crates/openshell-supervisor-network/src/host.rs index 7e29b3d0ed..5ae44dfff9 100644 --- a/crates/openshell-supervisor-network/src/host.rs +++ b/crates/openshell-supervisor-network/src/host.rs @@ -222,6 +222,7 @@ pub async fn start_host_proxy(config: HostProxyConfig) -> Result + Send + 'static>>; + +/// Serves workload connections to a reserved, unroutable destination inside +/// the supervisor instead of dialing upstream. +/// +/// The proxy consults the handler before the sandbox commits the workload +/// socket. `None` refuses the open, which the workload observes as `EAGAIN`; +/// `Some` accepts it, after which the proxy answers `RelayReady` and drives +/// the returned future on its own task. Nothing may be read from `stream` +/// until that future is polled. +pub trait ReservedStreamHandler: Send + Sync { + fn serve(&self, stream: BoundaryDuplexStream) -> Option; +} + +/// A reserved destination the proxy serves locally. +#[derive(Clone)] +pub struct ReservedDestination { + pub addr: SocketAddr, + pub handler: Arc, +} + +/// The OTLP relay address, known to the proxy even when no handler is +/// installed so a workload connect to it never falls through to policy +/// evaluation and upstream dialing. +static OTLP_RELAY_ADDR: LazyLock = LazyLock::new(|| { + openshell_core::sandbox_env::OTLP_RELAY_ADDR + .parse() + .expect("OTLP_RELAY_ADDR is a valid socket address") +}); + +fn is_reserved_relay_destination(destination: SocketAddr) -> bool { + destination == *OTLP_RELAY_ADDR +} + use self::destination::{ DestinationDenial, DestinationDenialKind, DestinationRequest, DestinationValidationPlan, build_pinned_validation_plan, build_validation_plan, validate_destination, @@ -273,6 +310,7 @@ impl ProxyHandle { network_mediation_source: Option>, policy_dns_store: Option>, direct_listener_identity: Option, + reserved_destination: Option, ) -> Result { // Use override bind_addr, fall back to policy http_addr, then default // to loopback:3128. The default allows the proxy to function when no @@ -437,6 +475,7 @@ impl ProxyHandle { let trusted_gateway = *trusted_host_gateway; let dtx = denial_tx.clone(); let has_policy_local = policy_local_ctx.is_some(); + let reserved = reserved_destination.clone(); tokio::spawn(async move { if let Some(connection) = preauthorize_transparent_open( connection, @@ -447,6 +486,7 @@ impl ProxyHandle { trusted_gateway, has_policy_local, dtx.as_ref(), + reserved.as_ref(), ) .await { @@ -596,6 +636,7 @@ async fn preauthorize_transparent_open( trusted_host_gateway: Option, has_policy_local: bool, denial_tx: Option<&mpsc::UnboundedSender>, + reserved: Option<&ReservedDestination>, ) -> Option { let PendingTcpOpen { stream, @@ -610,6 +651,41 @@ async fn preauthorize_transparent_open( timing, operation: "tcp", }; + // A reserved destination is served inside the supervisor. It is decided + // before host mapping, policy, and SSRF validation because the address is + // an unroutable label, not a place anything could dial. + if let Some(reserved) = reserved.filter(|reserved| reserved.addr == destination) { + if let Some(served) = reserved.handler.serve(stream) { + debug!(%destination, "Serving staged connection on reserved destination"); + // If the sandbox side is already gone, dropping `served` closes + // the stream and releases the handler's slot. + if completion.send(TcpOpenDecision::RelayReady).is_ok() { + tokio::spawn(served); + } + } else { + warn!( + %destination, + "Refused staged reserved-destination connection: handler at capacity" + ); + let _ = completion.send(TcpOpenDecision::Denied(TcpOpenDenial::ResourceExhausted)); + } + return None; + } + if is_reserved_relay_destination(destination) { + warn!( + %destination, + "Denied staged reserved-destination connection: no handler installed" + ); + emit_staged_transparent_denial( + destination, + None, + &binary_identity, + "reserved destination has no handler", + "transparent_tcp_reserved_denied", + ); + let _ = completion.send(TcpOpenDecision::Denied(TcpOpenDenial::PolicyDenied)); + return None; + } if destination.ip() == IpAddr::V4(crate::policy_dns::POLICY_LOCAL_ADDRESS) { if destination.port() != 80 || !has_policy_local { emit_staged_transparent_denial( @@ -7197,7 +7273,8 @@ process: None, None, false, - Some(&denial_tx) + Some(&denial_tx), + None ) .await .is_some() @@ -7215,6 +7292,7 @@ process: None, false, Some(&denial_tx), + None, ) .await .is_none() @@ -7238,7 +7316,8 @@ process: None, None, false, - Some(&denial_tx) + Some(&denial_tx), + None ) .await .is_none() @@ -7403,6 +7482,7 @@ process: { run_as_user: sandbox, run_as_group: sandbox } None, false, None, + None, ) .await .expect("policy-backed mapping is admitted"); @@ -7439,6 +7519,7 @@ process: { run_as_user: sandbox, run_as_group: sandbox } None, false, Some(&denial_tx), + None, ) .await .is_none() @@ -7478,6 +7559,7 @@ process: { run_as_user: sandbox, run_as_group: sandbox } None, false, Some(denial_tx), + None, ) .await .is_some(); @@ -7643,10 +7725,11 @@ process: { run_as_user: sandbox, run_as_group: sandbox } timing: MediationTiming::default(), decision, }; - let (stream, supplied_identity, socket_addrs, transparent) = - preauthorize_transparent_open(pending, None, &engine, &cache, None, None, true, None) - .await - .expect("sandbox-local open is admitted"); + let (stream, supplied_identity, socket_addrs, transparent) = preauthorize_transparent_open( + pending, None, &engine, &cache, None, None, true, None, None, + ) + .await + .expect("sandbox-local open is admitted"); assert_eq!(completion.await.unwrap(), TcpOpenDecision::RelayReady); let proposals = AgentProposals::new(true); @@ -7774,10 +7857,11 @@ network_policies: timing: MediationTiming::default(), decision, }; - let (stream, supplied_identity, socket_addrs, transparent) = - preauthorize_transparent_open(pending, None, &engine, &cache, None, None, false, None) - .await - .expect("open is admitted"); + let (stream, supplied_identity, socket_addrs, transparent) = preauthorize_transparent_open( + pending, None, &engine, &cache, None, None, false, None, None, + ) + .await + .expect("open is admitted"); assert_eq!(completion.await.unwrap(), TcpOpenDecision::RelayReady); // The workload writes before the proxy reads anything, so its request sits // right behind the synthesized CONNECT header in the proxy's first read. @@ -7923,6 +8007,7 @@ process: None, None, false, + None, None ) .await @@ -8004,6 +8089,7 @@ process: None, None, false, + None, None ) .await @@ -8015,6 +8101,268 @@ process: ); } + // ---- reserved destinations ---- + + /// Policy that allows one public endpoint and nothing else. It never + /// mentions the relay address, so a served relay stream proves the + /// reserved path bypassed policy. + fn reserved_test_engine() -> OpaEngine { + OpaEngine::from_strings( + include_str!("../data/sandbox-policy.rego"), + r#" +network_policies: + allowed: + name: allowed + endpoints: + - host: 203.0.113.7 + port: 443 + binaries: + - path: /usr/bin/curl +filesystem_policy: + include_workdir: true + read_only: [] + read_write: [] +landlock: + compatibility: best_effort +process: + run_as_user: sandbox + run_as_group: sandbox +"#, + ) + .unwrap() + } + + /// A staged open from `/usr/bin/curl` to `destination`, plus the + /// decision receiver and the workload-side peer of the staged stream. + fn staged_open( + engine: &OpaEngine, + destination: &str, + ) -> ( + PendingTcpOpen, + tokio::sync::oneshot::Receiver, + tokio::io::DuplexStream, + ) { + let (stream, peer) = tokio::io::duplex(256); + let (decision, completion) = tokio::sync::oneshot::channel(); + let open = PendingTcpOpen { + stream: Box::new(stream), + binary_identity: Ok(ContractBinaryIdentity { + executable: ContractExecutableIdentity { + path: PathBuf::from("/usr/bin/curl"), + digest: Some("00".repeat(32).parse().unwrap()), + }, + ancestors: Vec::new(), + cmdline_paths: Vec::new(), + }), + destination: destination.parse().unwrap(), + socket: openshell_isolation_interface::contract::NetworkSocketMetadata { + socket_cookie: 7, + nonblocking: false, + process_generation: 1, + }, + policy_generation: engine.current_generation(), + timing: MediationTiming::default(), + decision, + }; + (open, completion, peer) + } + + /// Handler double: counts calls, optionally refuses, and hands every + /// accepted stream back to the test. + struct RecordingReservedHandler { + accept: bool, + served: mpsc::UnboundedSender, + calls: Arc, + } + + impl ReservedStreamHandler for RecordingReservedHandler { + fn serve(&self, stream: BoundaryDuplexStream) -> Option { + self.calls.fetch_add(1, Ordering::SeqCst); + if !self.accept { + return None; + } + let served = self.served.clone(); + Some(Box::pin(async move { + let _ = served.send(stream); + })) + } + } + + fn reserved_relay( + accept: bool, + ) -> ( + ReservedDestination, + mpsc::UnboundedReceiver, + Arc, + ) { + let (served, served_rx) = mpsc::unbounded_channel(); + let calls = Arc::new(std::sync::atomic::AtomicUsize::new(0)); + let handler = RecordingReservedHandler { + accept, + served, + calls: Arc::clone(&calls), + }; + let reserved = ReservedDestination { + addr: *OTLP_RELAY_ADDR, + handler: Arc::new(handler), + }; + (reserved, served_rx, calls) + } + + #[tokio::test] + async fn reserved_destination_is_served_without_policy_evaluation() { + let engine = reserved_test_engine(); + let (reserved, mut served_rx, calls) = reserved_relay(true); + let (open, decision, mut peer) = + staged_open(&engine, openshell_core::sandbox_env::OTLP_RELAY_ADDR); + + assert!( + preauthorize_transparent_open( + open, + None, + &engine, + &BinaryIdentityCache::new(), + None, + None, + false, + None, + Some(&reserved) + ) + .await + .is_none(), + "a reserved open never becomes a proxy connection" + ); + assert_eq!(decision.await.unwrap(), TcpOpenDecision::RelayReady); + assert_eq!(calls.load(Ordering::SeqCst), 1); + + // The handler holds the workload's stream: bytes written by the + // workload side arrive at the handler. + let mut stream = tokio::time::timeout(std::time::Duration::from_secs(1), served_rx.recv()) + .await + .expect("handler future ran") + .expect("handler received the stream"); + peer.write_all(b"POST /v1/traces").await.unwrap(); + let mut head = [0u8; 15]; + stream.read_exact(&mut head).await.unwrap(); + assert_eq!(&head, b"POST /v1/traces"); + } + + #[tokio::test] + async fn reserved_destination_is_refused_when_handler_is_at_capacity() { + let engine = reserved_test_engine(); + let (reserved, _served_rx, calls) = reserved_relay(false); + let (open, decision, _peer) = + staged_open(&engine, openshell_core::sandbox_env::OTLP_RELAY_ADDR); + + assert!( + preauthorize_transparent_open( + open, + None, + &engine, + &BinaryIdentityCache::new(), + None, + None, + false, + None, + Some(&reserved) + ) + .await + .is_none() + ); + assert_eq!( + decision.await.unwrap(), + TcpOpenDecision::Denied(TcpOpenDenial::ResourceExhausted), + "refusal happens before the sandbox commits the socket" + ); + assert_eq!(calls.load(Ordering::SeqCst), 1); + } + + #[tokio::test] + async fn reserved_relay_destination_is_denied_without_a_handler() { + let engine = reserved_test_engine(); + let (open, decision, _peer) = + staged_open(&engine, openshell_core::sandbox_env::OTLP_RELAY_ADDR); + + assert!( + preauthorize_transparent_open( + open, + None, + &engine, + &BinaryIdentityCache::new(), + None, + None, + false, + None, + None + ) + .await + .is_none() + ); + assert_eq!( + decision.await.unwrap(), + TcpOpenDecision::Denied(TcpOpenDenial::PolicyDenied), + "the relay address must never reach policy evaluation or dialing" + ); + } + + #[tokio::test] + async fn reserved_handler_ignores_other_destinations() { + let engine = reserved_test_engine(); + let (reserved, _served_rx, calls) = reserved_relay(true); + + let (denied, denied_result) = { + let (open, decision, _peer) = staged_open(&engine, "203.0.113.8:443"); + (open, decision) + }; + assert!( + preauthorize_transparent_open( + denied, + None, + &engine, + &BinaryIdentityCache::new(), + None, + None, + false, + None, + Some(&reserved) + ) + .await + .is_none() + ); + assert_eq!( + denied_result.await.unwrap(), + TcpOpenDecision::Denied(TcpOpenDenial::PolicyDenied) + ); + + let (allowed, allowed_result) = { + let (open, decision, _peer) = staged_open(&engine, "203.0.113.7:443"); + (open, decision) + }; + assert!( + preauthorize_transparent_open( + allowed, + None, + &engine, + &BinaryIdentityCache::new(), + None, + None, + false, + None, + Some(&reserved) + ) + .await + .is_some(), + "policy-allowed destinations still flow to the proxy" + ); + assert_eq!(allowed_result.await.unwrap(), TcpOpenDecision::RelayReady); + + assert_eq!( + calls.load(Ordering::SeqCst), + 0, + "the handler is consulted only for its own address" + ); + } + struct FailedMediationSource; #[tokio::test] @@ -8416,6 +8764,7 @@ network_policies: {} Some(Arc::new(FailedMediationSource)), None, None, + None, ) .await .expect("proxy starts before source accept"); diff --git a/crates/openshell-supervisor-network/src/run.rs b/crates/openshell-supervisor-network/src/run.rs index 1eba8a0742..5717a82e4a 100644 --- a/crates/openshell-supervisor-network/src/run.rs +++ b/crates/openshell-supervisor-network/src/run.rs @@ -201,6 +201,7 @@ pub async fn run_networking( host_gateway_ip: Option, #[cfg(target_os = "linux")] transparent_runtime: Option, network_mediation_source: Option>, + reserved_destination: Option, ) -> Result { // Build the policy-local route context. The orchestrator's policy poll // loop also holds an `Arc` clone (via `Networking::policy_local_ctx`) so @@ -492,6 +493,7 @@ pub async fn run_networking( .as_ref() .map(|runtime| runtime.store.clone()), None, + reserved_destination, ) .await?; Some(proxy_handle) diff --git a/crates/openshell-supervisor-process/Cargo.toml b/crates/openshell-supervisor-process/Cargo.toml index 39d48a96e5..82589fc883 100644 --- a/crates/openshell-supervisor-process/Cargo.toml +++ b/crates/openshell-supervisor-process/Cargo.toml @@ -20,12 +20,18 @@ async-trait = "0.1" base64 = { workspace = true } bytes = { workspace = true } hex = "0.4" +http-body-util = { workspace = true } +hyper = { workspace = true } +hyper-util = { workspace = true, features = ["tokio", "http1", "server-graceful"] } miette = { workspace = true } nix = { workspace = true } +opentelemetry-proto = { workspace = true } +prost = { workspace = true } rand = "0.10" russh = "0.62" serde_json = { workspace = true } sha2 = { workspace = true } +thiserror = { workspace = true } tokio = { workspace = true } tokio-stream = { workspace = true } tonic = { workspace = true, features = ["channel", "tls-native-roots"] } diff --git a/crates/openshell-supervisor-process/README.md b/crates/openshell-supervisor-process/README.md new file mode 100644 index 0000000000..23b3191963 --- /dev/null +++ b/crates/openshell-supervisor-process/README.md @@ -0,0 +1,65 @@ +# Process supervisor + +`openshell-supervisor-process` owns the supervisor's process-facing runtime: +SSH access, the gateway session, log forwarding, and the OTLP telemetry +relay described below. + +## Telemetry relay + +The supervisor relays OpenTelemetry trace data from agent processes to the +gateway over the session stream, so OTel-instrumented agents reach an +external collector without any sandbox egress. + +The relay is opt-in on the gateway. When `[openshell.gateway.otlp]` is +configured, `CreateSandbox` sets `OTEL_EXPORTER_OTLP_ENDPOINT` to +`http://192.0.0.8:4318` and `OTEL_EXPORTER_OTLP_PROTOCOL` to `http/protobuf` +in the sandbox environment unless the caller already set an endpoint. The +values persist in the stored spec, so restarts inherit them. + +`192.0.0.8` is the RFC 7600 dummy address and is never routed. A workload +connect to any non-loopback address is intercepted by the sandbox seccomp +broker and staged for the supervisor as a `PendingTcpOpen`, so the address is +a label the supervisor switches on. The proxy recognises it ahead of host +mapping, policy, and SSRF validation and hands the staged stream to the OTLP +receiver instead of dialing upstream. The receiver speaks HTTP/1.1 on that +stream and accepts `POST /v1/traces` as protobuf or JSON. No socket is bound, +the driver outer fence is untouched, and the isolation contract and boundary +protocol are unchanged. Loopback cannot serve this purpose because the broker +completes loopback connects locally without mediation. + +``` +Agent process --> connect(192.0.0.8:4318) --> seccomp broker stages the open + --> supervisor proxy, reserved destination --> OTLP receiver + --> enrichment (openshell.sandbox.* resource attributes) + --> bounded buffer (4096 items, newest dropped when full) + --> supervisor session (OtelExportData) --> gateway + --> dedicated OtelRelayExporter --> external OTLP collector +``` + +The relay starts before networking, so the first staged stream finds it. The +supervisor advertises `otel_export` in `SupervisorHello`; the gateway confirms +it in `SessionAccepted` only when it has a relay exporter. Forwarding is gated +per session on that confirmation. Items received before a confirming session, +or while a session declines, wait in the bounded buffer. The receiver serves +at most 64 concurrent connections and refuses further opens before the +sandbox commits the socket, which the agent observes as `EAGAIN`. + +Forwarded spans gain `openshell.sandbox.id`, `openshell.workspace.id`, +`openshell.sandbox.policy`, `openshell.sandbox.user`, +`openshell.sandbox.image`, and `openshell.sandbox.driver`, and always +`openshell.telemetry.source=agent` so collectors can separate agent spans +from gateway spans. Agent-supplied values for these keys are replaced. + +Both hops are non-blocking. The receiver uses `try_send` into the buffer and +the session uses `try_send` into its outbound channel, so telemetry can never +stall control traffic. After the main process exits and before the exit is +reported, the supervisor asks the session to stop the receiver (no new +streams, keep-alive disabled, two seconds for in-flight requests, then +stragglers cut) and flush the buffer onto the stream. The whole drain is +bounded at three seconds so an unreachable gateway cannot delay the report. + +The gateway forwards trace bytes through a dedicated `OtelRelayExporter` +rather than its own tracer provider, so the supervisor's resource attributes +survive. `OtelExportData` also carries OCSF events, which the gateway +re-emits on the `ocsf_relay` target; the supervisor-side OCSF sink is not +installed yet. diff --git a/crates/openshell-supervisor-process/src/delegated.rs b/crates/openshell-supervisor-process/src/delegated.rs index 87101f62a2..4c2c98e72c 100644 --- a/crates/openshell-supervisor-process/src/delegated.rs +++ b/crates/openshell-supervisor-process/src/delegated.rs @@ -3,8 +3,8 @@ //! Supervisor-owned access-plane assembly for a remote sandbox. -use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex, PoisonError}; use std::time::Duration; use miette::Result; @@ -13,10 +13,17 @@ use openshell_isolation_interface::contract::{ }; use openshell_ocsf::{ActivityId, AppLifecycleBuilder, SeverityId, StatusId, ocsf_emit}; +use crate::otlp::RelayLifecycle; +use crate::supervisor_session::{DrainRequest, SessionRuntimeContext, TelemetryRelay}; + fn ocsf_ctx() -> &'static openshell_ocsf::EventContext { openshell_ocsf::ctx::ctx() } +/// Upper bound on the final OTLP relay drain before the exit is reported, so +/// an unreachable gateway cannot delay the exit report. +pub const OTEL_FINAL_DRAIN_TIMEOUT: Duration = Duration::from_secs(3); + /// Supervisor-owned SSH and gateway-session tasks for a running sandbox. pub struct BoundaryAccess { instance_id: String, @@ -25,6 +32,8 @@ pub struct BoundaryAccess { session_task: Option>, session_readiness: Option>, main_session: Option>, + /// Sends the one-shot final drain request to the session task. + telemetry_drain: Mutex>>, } impl BoundaryAccess { @@ -34,6 +43,32 @@ impl BoundaryAccess { &self.instance_id } + /// Ask the session to stop the OTLP receiver and flush buffered telemetry + /// onto its stream. Returns when the session acks or `deadline` elapses. + /// A boundary without a relay or session returns at once. Only the first + /// call sends a request. + pub async fn drain_telemetry(&self, deadline: Duration) { + let request_tx = self + .telemetry_drain + .lock() + .unwrap_or_else(PoisonError::into_inner) + .take(); + let Some(request_tx) = request_tx else { + return; + }; + let (done_tx, done_rx) = tokio::sync::oneshot::channel(); + if request_tx.send(done_tx).is_err() { + tracing::debug!("OTEL relay drain skipped: session task is gone"); + return; + } + if tokio::time::timeout(deadline, done_rx).await.is_err() { + tracing::warn!( + ?deadline, + "OTEL relay final drain did not complete within deadline" + ); + } + } + /// Observe whether the gateway has accepted the current supervisor /// session. The value returns to false while the session reconnects. #[must_use] @@ -87,9 +122,13 @@ pub async fn start_boundary_access( port_forward: Arc, agent: Arc, supervisor_session_updates: Option>>, + telemetry: Option, ) -> Result { let instance_id = uuid::Uuid::new_v4().to_string(); let terminating = Arc::new(AtomicBool::new(false)); + // Without a gateway session the relay has nowhere to forward; dropping + // it here leaves the connection server accepting into a closed buffer, + // which counts drops and never blocks the workload. let Some(ssh_socket_path) = ssh_socket_path.map(std::path::PathBuf::from) else { return Ok(BoundaryAccess { instance_id, @@ -98,6 +137,7 @@ pub async fn start_boundary_access( session_task: None, session_readiness: None, main_session: None, + telemetry_drain: Mutex::new(None), }); }; @@ -154,8 +194,13 @@ pub async fn start_boundary_access( } } - let (session_task, session_readiness) = match (openshell_endpoint, sandbox_id) { + let (session_task, session_readiness, telemetry_drain) = match (openshell_endpoint, sandbox_id) + { (Some(endpoint), Some(id)) => { + let (telemetry, drain_tx) = telemetry.map_or((None, None), |relay| { + let (drain_tx, drain_rx) = tokio::sync::oneshot::channel(); + (Some(TelemetryRelay { relay, drain_rx }), Some(drain_tx)) + }); let (task, accepted) = crate::supervisor_session::spawn_with_readiness( endpoint.to_string(), id.to_string(), @@ -163,17 +208,18 @@ pub async fn start_boundary_access( port_forward, None, terminating.clone(), - crate::supervisor_session::SessionRuntimeContext { + SessionRuntimeContext { instance_id: instance_id.clone(), session_id_updates: supervisor_session_updates, + telemetry, }, ); // Session establishment retries through gateway restarts. The // readiness socket remains absent until the gateway accepts the // session, so a transient delay cannot kill the supervisor. - (Some(task), Some(accepted)) + (Some(task), Some(accepted), drain_tx) } - _ => (None, None), + _ => (None, None, None), }; Ok(BoundaryAccess { @@ -183,6 +229,7 @@ pub async fn start_boundary_access( session_task, session_readiness, main_session: Some(main_session), + telemetry_drain: Mutex::new(telemetry_drain), }) } @@ -238,6 +285,20 @@ pub async fn finalize_main_process_exit(endpoint: &str, sandbox_id: &str, instan mod tests { use super::*; + fn access_with_drain( + drain: Option>, + ) -> BoundaryAccess { + BoundaryAccess { + instance_id: "instance".to_string(), + terminating: Arc::new(AtomicBool::new(false)), + ssh_task: None, + session_task: None, + session_readiness: None, + main_session: None, + telemetry_drain: Mutex::new(drain), + } + } + #[tokio::test] async fn expected_post_exit_attachment_is_preserved_for_remote_main() { let main_session = crate::main_session::MainSession::inert(); @@ -248,6 +309,7 @@ mod tests { session_task: None, session_readiness: None, main_session: Some(main_session.clone()), + telemetry_drain: Mutex::new(None), }; access.publish_main_exit(7, true).await; @@ -257,4 +319,77 @@ mod tests { .expect("declared CLI attachment must remain valid after a fast remote main exits"); main_session.end_terminal_attachment(); } + + #[tokio::test] + async fn drain_telemetry_returns_at_once_without_a_relay() { + let access = access_with_drain(None); + tokio::time::timeout( + Duration::from_millis(100), + access.drain_telemetry(Duration::from_secs(5)), + ) + .await + .expect("no relay means nothing to wait for"); + } + + #[tokio::test] + async fn drain_telemetry_returns_at_once_when_the_session_is_gone() { + let (drain_tx, drain_rx) = tokio::sync::oneshot::channel::(); + drop(drain_rx); + let access = access_with_drain(Some(drain_tx)); + tokio::time::timeout( + Duration::from_millis(100), + access.drain_telemetry(Duration::from_secs(5)), + ) + .await + .expect("a dropped session receiver must not stall the exit path"); + } + + #[tokio::test] + async fn drain_telemetry_waits_for_the_session_ack_and_sends_once() { + let (drain_tx, drain_rx) = tokio::sync::oneshot::channel::(); + let access = access_with_drain(Some(drain_tx)); + + let session = tokio::spawn(async move { + let done_tx = drain_rx.await.expect("drain request arrives"); + tokio::time::sleep(Duration::from_millis(50)).await; + let _ = done_tx.send(()); + }); + + let started = std::time::Instant::now(); + access.drain_telemetry(Duration::from_secs(5)).await; + assert!( + started.elapsed() >= Duration::from_millis(50), + "drain waits for the session's ack" + ); + session.await.unwrap(); + + // Second call finds the request already sent and returns immediately. + tokio::time::timeout( + Duration::from_millis(100), + access.drain_telemetry(Duration::from_secs(5)), + ) + .await + .expect("only the first call sends a request"); + } + + #[tokio::test] + async fn drain_telemetry_gives_up_at_the_deadline() { + let (drain_tx, drain_rx) = tokio::sync::oneshot::channel::(); + let access = access_with_drain(Some(drain_tx)); + // Hold the request open and never ack. + let held = tokio::spawn(async move { + let done_tx = drain_rx.await.expect("drain request arrives"); + tokio::time::sleep(Duration::from_secs(30)).await; + drop(done_tx); + }); + + let started = std::time::Instant::now(); + access.drain_telemetry(Duration::from_millis(100)).await; + let elapsed = started.elapsed(); + assert!( + elapsed >= Duration::from_millis(100) && elapsed < Duration::from_secs(2), + "drain returns at the deadline, got {elapsed:?}" + ); + held.abort(); + } } diff --git a/crates/openshell-supervisor-process/src/lib.rs b/crates/openshell-supervisor-process/src/lib.rs index 023a6c8e73..8c2e4097eb 100644 --- a/crates/openshell-supervisor-process/src/lib.rs +++ b/crates/openshell-supervisor-process/src/lib.rs @@ -11,6 +11,7 @@ pub mod debug_rpc; pub mod delegated; pub mod log_push; pub mod main_session; +pub mod otlp; pub mod skills; pub mod ssh; pub mod supervisor_session; diff --git a/crates/openshell-supervisor-process/src/otlp/buffer.rs b/crates/openshell-supervisor-process/src/otlp/buffer.rs new file mode 100644 index 0000000000..a3aa0ea411 --- /dev/null +++ b/crates/openshell-supervisor-process/src/otlp/buffer.rs @@ -0,0 +1,210 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Bounded telemetry buffer with ring-buffer drop semantics. + +use std::sync::Arc; +use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering}; + +use tokio::sync::mpsc; + +/// Distinguishes trace data from OCSF events in the shared buffer. +#[derive(Debug)] +pub enum TelemetryItem { + Trace(Vec), + Ocsf(Vec), +} + +/// Shared drop/depth counters for the buffer. +#[derive(Debug, Clone)] +pub struct BufferMetrics { + drop_count: Arc, + queue_depth: Arc, +} + +impl BufferMetrics { + pub fn drops(&self) -> u64 { + self.drop_count.load(Ordering::Relaxed) + } + + pub fn depth(&self) -> usize { + self.queue_depth.load(Ordering::Relaxed) + } +} + +/// Sender half of the telemetry buffer. Sends never block: when the buffer +/// is full, the new item is dropped and the drop counter increments. +#[derive(Clone)] +pub struct TelemetrySender { + tx: mpsc::Sender, + metrics: BufferMetrics, +} + +impl TelemetrySender { + /// Send a telemetry item into the buffer. If the channel is full, the + /// item is dropped and the drop counter is incremented. + pub fn send(&self, item: TelemetryItem) { + match self.tx.try_send(item) { + Ok(()) => { + self.metrics.queue_depth.fetch_add(1, Ordering::Relaxed); + } + Err(_) => { + self.metrics.drop_count.fetch_add(1, Ordering::Relaxed); + } + } + } + + pub fn send_trace(&self, data: Vec) { + self.send(TelemetryItem::Trace(data)); + } + + pub fn send_ocsf(&self, data: Vec) { + self.send(TelemetryItem::Ocsf(data)); + } + + pub fn metrics(&self) -> &BufferMetrics { + &self.metrics + } +} + +/// Receiver half of the telemetry buffer. +pub struct TelemetryReceiver { + rx: mpsc::Receiver, + metrics: BufferMetrics, +} + +impl TelemetryReceiver { + /// Receive the next buffered entry, or `None` if all senders are dropped. + pub async fn recv(&mut self) -> Option { + let item = self.rx.recv().await; + if item.is_some() { + self.metrics.queue_depth.fetch_sub(1, Ordering::Relaxed); + } + item + } + + /// Drain all currently buffered entries without waiting. + pub fn drain(&mut self) -> Vec { + let mut items = Vec::new(); + while let Ok(item) = self.rx.try_recv() { + self.metrics.queue_depth.fetch_sub(1, Ordering::Relaxed); + items.push(item); + } + items + } + + pub fn metrics(&self) -> &BufferMetrics { + &self.metrics + } +} + +/// Create a new telemetry buffer pair with the given capacity. +pub fn new_telemetry_buffer(capacity: usize) -> (TelemetrySender, TelemetryReceiver) { + let (tx, rx) = mpsc::channel(capacity); + let metrics = BufferMetrics { + drop_count: Arc::new(AtomicU64::new(0)), + queue_depth: Arc::new(AtomicUsize::new(0)), + }; + ( + TelemetrySender { + tx, + metrics: metrics.clone(), + }, + TelemetryReceiver { rx, metrics }, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn buffer_drops_when_full() { + let (tx, mut rx) = new_telemetry_buffer(2); + + tx.send_trace(vec![1]); + tx.send_trace(vec![2]); + tx.send_trace(vec![3]); + + assert_eq!(tx.metrics().drops(), 1); + assert_eq!(tx.metrics().depth(), 2); + + let first = rx.recv().await.unwrap(); + assert!(matches!(first, TelemetryItem::Trace(v) if v == vec![1])); + assert_eq!(rx.metrics().depth(), 1); + } + + #[tokio::test] + async fn drain_empties_buffer() { + let (tx, mut rx) = new_telemetry_buffer(16); + + tx.send_trace(vec![1]); + tx.send_ocsf(vec![2]); + tx.send_trace(vec![3]); + + let items = rx.drain(); + assert_eq!(items.len(), 3); + assert!(matches!(&items[0], TelemetryItem::Trace(_))); + assert!(matches!(&items[1], TelemetryItem::Ocsf(_))); + assert_eq!(rx.metrics().depth(), 0); + } + + #[tokio::test] + async fn depth_tracks_send_and_recv() { + let (tx, mut rx) = new_telemetry_buffer(16); + + tx.send_trace(vec![1]); + tx.send_trace(vec![2]); + tx.send_trace(vec![3]); + assert_eq!(tx.metrics().depth(), 3); + + rx.recv().await.unwrap(); + assert_eq!(rx.metrics().depth(), 2); + + let remaining = rx.drain(); + assert_eq!(remaining.len(), 2); + assert_eq!(rx.metrics().depth(), 0); + } + + #[tokio::test] + async fn drop_count_increments_on_each_overflow() { + let (tx, _rx) = new_telemetry_buffer(2); + + tx.send_trace(vec![1]); + tx.send_trace(vec![2]); + assert_eq!(tx.metrics().drops(), 0); + + for _ in 0..5 { + tx.send_trace(vec![99]); + } + assert_eq!(tx.metrics().drops(), 5); + assert_eq!(tx.metrics().depth(), 2); + } + + #[tokio::test] + async fn metrics_shared_across_clones() { + let (tx, mut rx) = new_telemetry_buffer(16); + let tx2 = tx.clone(); + + tx.send_trace(vec![1]); + tx2.send_trace(vec![2]); + tx.send_ocsf(vec![3]); + + assert_eq!(tx.metrics().depth(), 3); + assert_eq!(tx2.metrics().depth(), 3); + + rx.recv().await.unwrap(); + assert_eq!(tx.metrics().depth(), 2); + assert_eq!(tx2.metrics().depth(), 2); + } + + #[tokio::test] + async fn recv_returns_none_when_all_senders_dropped() { + let (tx, mut rx) = new_telemetry_buffer(16); + tx.send_trace(vec![1]); + drop(tx); + + assert!(rx.recv().await.is_some()); + assert!(rx.recv().await.is_none()); + } +} diff --git a/crates/openshell-supervisor-process/src/otlp/enrichment.rs b/crates/openshell-supervisor-process/src/otlp/enrichment.rs new file mode 100644 index 0000000000..0d1a6db00f --- /dev/null +++ b/crates/openshell-supervisor-process/src/otlp/enrichment.rs @@ -0,0 +1,309 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Span enrichment: injects sandbox resource attributes into OTLP trace data. + +use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest; +use opentelemetry_proto::tonic::common::v1::{AnyValue, KeyValue}; +use opentelemetry_proto::tonic::resource::v1::Resource; +use prost::Message; + +use super::SandboxMetadata; + +/// Content type of the incoming OTLP request body. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ContentType { + Protobuf, + Json, +} + +/// Enrich spans with sandbox resource attributes. Input can be protobuf or +/// JSON-encoded `ExportTraceServiceRequest`. Output is always protobuf. +/// +/// When `enrichment_enabled` is false, sandbox metadata attributes are skipped +/// but `openshell.telemetry.source: "agent"` is always injected (relay marker). +pub fn enrich_spans( + raw: &[u8], + content_type: ContentType, + attrs: &SandboxMetadata, + enrichment_enabled: bool, +) -> Result, EnrichmentError> { + let mut request = match content_type { + ContentType::Protobuf => { + ExportTraceServiceRequest::decode(raw).map_err(EnrichmentError::ProtobufDecode)? + } + ContentType::Json => serde_json::from_slice::(raw) + .map_err(EnrichmentError::JsonDecode)?, + }; + + let extra_attrs = build_attributes(attrs, enrichment_enabled); + + let trusted_keys: Vec<&str> = extra_attrs + .iter() + .filter_map(|a| a.key.as_str().into()) + .collect(); + + for resource_spans in &mut request.resource_spans { + let resource = resource_spans + .resource + .get_or_insert_with(Resource::default); + + resource + .attributes + .retain(|a| !trusted_keys.contains(&a.key.as_str())); + + for attr in &extra_attrs { + resource.attributes.push(attr.clone()); + } + } + + Ok(request.encode_to_vec()) +} + +fn build_attributes(meta: &SandboxMetadata, enrichment_enabled: bool) -> Vec { + let mut attrs = Vec::new(); + + // Always inject the relay routing marker regardless of enrichment toggle + attrs.push(kv("openshell.telemetry.source", "agent")); + + if enrichment_enabled { + attrs.push(kv("openshell.sandbox.id", &meta.sandbox_id)); + attrs.push(kv("openshell.workspace.id", &meta.workspace_id)); + attrs.push(kv("openshell.sandbox.policy", &meta.policy)); + attrs.push(kv("openshell.sandbox.user", &meta.user)); + attrs.push(kv("openshell.sandbox.image", &meta.image)); + attrs.push(kv("openshell.sandbox.driver", &meta.driver)); + } + + attrs +} + +fn kv(key: &str, value: &str) -> KeyValue { + KeyValue { + key: key.to_string(), + value: Some(AnyValue { + value: Some( + opentelemetry_proto::tonic::common::v1::any_value::Value::StringValue( + value.to_string(), + ), + ), + }), + key_strindex: 0, + } +} + +#[derive(Debug, thiserror::Error)] +pub enum EnrichmentError { + #[error("protobuf decode failed: {0}")] + ProtobufDecode(prost::DecodeError), + #[error("JSON decode failed: {0}")] + JsonDecode(serde_json::Error), +} + +#[cfg(test)] +mod tests { + use super::*; + use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest; + use opentelemetry_proto::tonic::trace::v1::{ResourceSpans, ScopeSpans, Span}; + use prost::Message; + + fn test_metadata() -> SandboxMetadata { + SandboxMetadata { + sandbox_id: "sb-123".into(), + workspace_id: "ws-456".into(), + policy: "default".into(), + user: "test-user".into(), + image: "test-image:latest".into(), + driver: "docker".into(), + } + } + + fn make_trace_request() -> Vec { + let req = ExportTraceServiceRequest { + resource_spans: vec![ResourceSpans { + resource: None, + scope_spans: vec![ScopeSpans { + scope: None, + spans: vec![Span { + name: "test-span".into(), + ..Default::default() + }], + schema_url: String::new(), + }], + schema_url: String::new(), + }], + }; + req.encode_to_vec() + } + + #[test] + fn enrichment_adds_all_attributes() { + let raw = make_trace_request(); + let result = enrich_spans(&raw, ContentType::Protobuf, &test_metadata(), true).unwrap(); + + let decoded = ExportTraceServiceRequest::decode(result.as_slice()).unwrap(); + let resource = decoded.resource_spans[0].resource.as_ref().unwrap(); + + let attr_keys: Vec<&str> = resource.attributes.iter().map(|a| a.key.as_str()).collect(); + assert!(attr_keys.contains(&"openshell.sandbox.id")); + assert!(attr_keys.contains(&"openshell.workspace.id")); + assert!(attr_keys.contains(&"openshell.telemetry.source")); + } + + #[test] + fn enrichment_disabled_only_adds_source() { + let raw = make_trace_request(); + let result = enrich_spans(&raw, ContentType::Protobuf, &test_metadata(), false).unwrap(); + + let decoded = ExportTraceServiceRequest::decode(result.as_slice()).unwrap(); + let resource = decoded.resource_spans[0].resource.as_ref().unwrap(); + + assert_eq!(resource.attributes.len(), 1); + assert_eq!(resource.attributes[0].key, "openshell.telemetry.source"); + } + + #[test] + fn enrichment_strips_agent_supplied_trusted_keys() { + use opentelemetry_proto::tonic::common::v1::{AnyValue, KeyValue, any_value}; + use opentelemetry_proto::tonic::resource::v1::Resource; + + let req = ExportTraceServiceRequest { + resource_spans: vec![ResourceSpans { + resource: Some(Resource { + attributes: vec![ + KeyValue { + key: "openshell.sandbox.id".into(), + value: Some(AnyValue { + value: Some(any_value::Value::StringValue( + "agent-spoofed-id".into(), + )), + }), + key_strindex: 0, + }, + KeyValue { + key: "openshell.telemetry.source".into(), + value: Some(AnyValue { + value: Some(any_value::Value::StringValue("fake".into())), + }), + key_strindex: 0, + }, + ], + dropped_attributes_count: 0, + entity_refs: Vec::new(), + }), + scope_spans: vec![], + schema_url: String::new(), + }], + }; + let raw = req.encode_to_vec(); + + let result = enrich_spans(&raw, ContentType::Protobuf, &test_metadata(), true).unwrap(); + let decoded = ExportTraceServiceRequest::decode(result.as_slice()).unwrap(); + let attrs = &decoded.resource_spans[0] + .resource + .as_ref() + .unwrap() + .attributes; + + let sandbox_ids: Vec<_> = attrs + .iter() + .filter(|a| a.key == "openshell.sandbox.id") + .collect(); + assert_eq!(sandbox_ids.len(), 1, "should have exactly one sandbox.id"); + + let sources = attrs + .iter() + .filter(|a| a.key == "openshell.telemetry.source") + .count(); + assert_eq!(sources, 1, "should have exactly one telemetry.source"); + + if let Some(AnyValue { + value: Some(any_value::Value::StringValue(v)), + }) = &sandbox_ids[0].value + { + assert_eq!(v, "sb-123", "should use supervisor's value, not agent's"); + } + } + + #[test] + fn enrichment_preserves_non_trusted_agent_attributes() { + use opentelemetry_proto::tonic::common::v1::{AnyValue, KeyValue, any_value}; + use opentelemetry_proto::tonic::resource::v1::Resource; + + let req = ExportTraceServiceRequest { + resource_spans: vec![ResourceSpans { + resource: Some(Resource { + attributes: vec![KeyValue { + key: "my.custom.attr".into(), + value: Some(AnyValue { + value: Some(any_value::Value::StringValue("keep-me".into())), + }), + key_strindex: 0, + }], + dropped_attributes_count: 0, + entity_refs: Vec::new(), + }), + scope_spans: vec![], + schema_url: String::new(), + }], + }; + let raw = req.encode_to_vec(); + + let result = enrich_spans(&raw, ContentType::Protobuf, &test_metadata(), true).unwrap(); + let decoded = ExportTraceServiceRequest::decode(result.as_slice()).unwrap(); + let attrs = &decoded.resource_spans[0] + .resource + .as_ref() + .unwrap() + .attributes; + + assert!( + attrs.iter().any(|a| a.key == "my.custom.attr"), + "custom agent attribute should be preserved" + ); + assert!( + attrs.iter().any(|a| a.key == "openshell.sandbox.id"), + "enrichment attributes should also be present" + ); + } + + #[test] + fn enrichment_handles_json_content_type() { + let req = ExportTraceServiceRequest { + resource_spans: vec![ResourceSpans { + resource: None, + scope_spans: vec![ScopeSpans { + scope: None, + spans: vec![Span { + name: "json-span".into(), + ..Default::default() + }], + schema_url: String::new(), + }], + schema_url: String::new(), + }], + }; + let json = serde_json::to_vec(&req).unwrap(); + + let result = enrich_spans(&json, ContentType::Json, &test_metadata(), true).unwrap(); + + let decoded = ExportTraceServiceRequest::decode(result.as_slice()).unwrap(); + let resource = decoded.resource_spans[0].resource.as_ref().unwrap(); + assert!( + resource + .attributes + .iter() + .any(|a| a.key == "openshell.telemetry.source") + ); + } + + #[test] + fn enrichment_rejects_invalid_protobuf() { + let garbage = vec![0xFF, 0xFE, 0xFD, 0xFC]; + let result = enrich_spans(&garbage, ContentType::Protobuf, &test_metadata(), true); + assert!( + matches!(result, Err(EnrichmentError::ProtobufDecode(_))), + "should return ProtobufDecode error" + ); + } +} diff --git a/crates/openshell-supervisor-process/src/otlp/mod.rs b/crates/openshell-supervisor-process/src/otlp/mod.rs new file mode 100644 index 0000000000..5abe19ad81 --- /dev/null +++ b/crates/openshell-supervisor-process/src/otlp/mod.rs @@ -0,0 +1,391 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! OTLP relay for the sandbox supervisor. +//! +//! Agent processes export OTLP traces to the reserved relay address +//! (`openshell_core::sandbox_env::OTLP_RELAY_ADDR`). The sandbox's seccomp +//! broker stages that connect for the supervisor, whose proxy hands the +//! staged stream to the [`OtlpConnectionServer`] instead of dialing upstream. +//! Spans are enriched with sandbox resource attributes and buffered in a +//! bounded channel. The supervisor session drains the buffer into its stream +//! once the gateway confirms the `otel_export` capability, and stops the +//! receiver before the main-process exit is reported so final spans still +//! reach the gateway. + +pub mod buffer; +pub mod enrichment; +pub mod receiver; + +use std::sync::Arc; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::time::Duration; + +use tokio::sync::mpsc; +use tracing::info; + +use openshell_core::proto::supervisor_message; +use openshell_core::proto::{OtelExportData, SupervisorMessage, otel_export_data}; + +use buffer::{BufferMetrics, TelemetryItem, TelemetryReceiver, TelemetrySender}; +pub use receiver::{ConnectionPermit, OtlpConnectionServer, ReceiverHandle}; + +/// Bounded time the receiver gets to finish in-flight requests on shutdown. +pub const RECEIVER_SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(2); + +/// Rate-limited OCSF relay sink that implements token bucket rate limiting +/// and sends accepted events through the OTEL buffer as OCSF bytes. +pub struct RateLimitedOcsfSink { + buf_tx: TelemetrySender, + tokens: std::sync::atomic::AtomicU32, + max_tokens: u32, + drop_count: AtomicU64, + last_refill: std::sync::Mutex, +} + +impl RateLimitedOcsfSink { + pub fn new(buf_tx: TelemetrySender, rate_per_sec: u32) -> Self { + Self { + buf_tx, + tokens: std::sync::atomic::AtomicU32::new(rate_per_sec), + max_tokens: rate_per_sec, + drop_count: AtomicU64::new(0), + last_refill: std::sync::Mutex::new(std::time::Instant::now()), + } + } + + fn try_acquire(&self) -> bool { + self.refill(); + let mut current = self.tokens.load(Ordering::Relaxed); + loop { + if current == 0 { + return false; + } + match self.tokens.compare_exchange_weak( + current, + current - 1, + Ordering::Relaxed, + Ordering::Relaxed, + ) { + Ok(_) => return true, + Err(updated) => current = updated, + } + } + } + + fn refill(&self) { + let Ok(mut last) = self.last_refill.lock() else { + return; + }; + let now = std::time::Instant::now(); + let elapsed = now.duration_since(*last); + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let new_tokens = (elapsed.as_secs_f64() * f64::from(self.max_tokens)) as u32; + if new_tokens > 0 { + *last = now; + let max = self.max_tokens; + self.tokens + .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |current| { + Some(current.saturating_add(new_tokens).min(max)) + }) + .ok(); + } + } + + pub fn drops(&self) -> u64 { + self.drop_count.load(Ordering::Relaxed) + } +} + +impl openshell_ocsf::OcsfRelaySink for RateLimitedOcsfSink { + fn send(&self, json_bytes: Vec) { + if self.try_acquire() { + self.buf_tx.send_ocsf(json_bytes); + } else { + self.drop_count.fetch_add(1, Ordering::Relaxed); + } + } +} + +/// Configuration for the OTEL relay. +#[derive(Debug, Clone)] +pub struct RelayConfig { + pub buffer_capacity: usize, + pub enrichment_enabled: bool, + pub ocsf_rate_limit: u32, +} + +impl Default for RelayConfig { + fn default() -> Self { + Self { + buffer_capacity: 4096, + enrichment_enabled: true, + ocsf_rate_limit: 100, + } + } +} + +/// Sandbox identity used for span enrichment. +#[derive(Debug, Clone)] +pub struct SandboxMetadata { + pub sandbox_id: String, + pub workspace_id: String, + pub policy: String, + pub user: String, + pub image: String, + pub driver: String, +} + +/// Wrap a buffered telemetry item in the session message the gateway expects. +pub fn export_message(sandbox_id: &str, item: TelemetryItem) -> SupervisorMessage { + let export = match item { + TelemetryItem::Trace(data) => OtelExportData { + sandbox_id: sandbox_id.to_string(), + signal: Some(otel_export_data::Signal::TraceData(data)), + ocsf_events: Vec::new(), + }, + TelemetryItem::Ocsf(data) => OtelExportData { + sandbox_id: sandbox_id.to_string(), + signal: None, + ocsf_events: vec![data], + }, + }; + SupervisorMessage { + payload: Some(supervisor_message::Payload::OtelExport(export)), + } +} + +/// Start the relay: build the bounded buffer and the connection server. +/// +/// Called before networking starts so the server is ready for the first +/// staged stream. Returns the server the proxy serves reserved-destination +/// streams with, and the lifecycle the supervisor session drives. +pub fn start( + config: &RelayConfig, + metadata: SandboxMetadata, +) -> (Arc, RelayLifecycle) { + let (buf_tx, buffer) = buffer::new_telemetry_buffer(config.buffer_capacity); + let (server, receiver) = OtlpConnectionServer::new(buf_tx, metadata, config.enrichment_enabled); + info!( + relay = openshell_core::sandbox_env::OTLP_RELAY_ADDR, + buffer_capacity = config.buffer_capacity, + enrichment = config.enrichment_enabled, + "OTEL relay started" + ); + (server, RelayLifecycle::Running { receiver, buffer }) +} + +/// Relay state owned by the supervisor session loop. +/// +/// Lives in the session's reconnect loop frame so the receiver and the buffer +/// survive gateway reconnects. Forwarding is gated per session on the +/// confirmed `otel_export` capability; between sessions, items accumulate in +/// the bounded buffer. +pub enum RelayLifecycle { + /// Receiver serving and buffer accepting. + Running { + receiver: ReceiverHandle, + buffer: TelemetryReceiver, + }, + /// Relay disabled or already drained. + Stopped, +} + +impl RelayLifecycle { + /// The relay is unavailable on this platform or disabled by configuration. + pub const fn stopped() -> Self { + Self::Stopped + } + + pub fn is_running(&self) -> bool { + matches!(self, Self::Running { .. }) + } + + /// Buffer counters while running. + pub fn buffer_metrics(&self) -> Option { + match self { + Self::Running { buffer, .. } => Some(buffer.metrics().clone()), + Self::Stopped => None, + } + } + + /// Next buffered item. Pends forever unless running, so it is safe to use + /// as a `select!` arm. Yields `None` once the receiver and all its + /// connections are gone. + pub async fn next_item(&mut self) -> Option { + match self { + Self::Running { buffer, .. } => buffer.recv().await, + Self::Stopped => std::future::pending().await, + } + } + + /// Stop accepting, close connections (bounded by + /// [`RECEIVER_SHUTDOWN_TIMEOUT`]), then push everything still buffered into + /// `tx`. Ends in [`RelayLifecycle::Stopped`]. Uses the non-blocking + /// `drain()` so a straggling connection task cannot stall the flush. + pub async fn stop_and_drain(&mut self, sandbox_id: &str, tx: &mpsc::Sender) { + let Self::Running { + receiver, + mut buffer, + } = std::mem::replace(self, Self::Stopped) + else { + return; + }; + + receiver.shutdown().await; + + let items = buffer.drain(); + let buffered = items.len(); + let mut forwarded = 0usize; + for item in items { + if tx.send(export_message(sandbox_id, item)).await.is_err() { + break; + } + forwarded += 1; + } + info!( + buffered, + forwarded, + buffer_drops = buffer.metrics().drops(), + "OTEL relay stopped" + ); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use openshell_ocsf::OcsfRelaySink; + use receiver::test_util::{connect, metadata, request, sample_trace_body, send}; + + fn small_config() -> RelayConfig { + RelayConfig { + buffer_capacity: 16, + ..RelayConfig::default() + } + } + + #[test] + fn rate_limiter_acquires_initial_tokens() { + let (buf_tx, _rx) = buffer::new_telemetry_buffer(64); + let sink = RateLimitedOcsfSink::new(buf_tx, 10); + + for i in 0..10 { + assert!(sink.try_acquire(), "token {i} should be available"); + } + assert!(!sink.try_acquire(), "11th token should fail"); + } + + #[test] + fn rate_limiter_drops_when_exhausted() { + let (buf_tx, mut rx) = buffer::new_telemetry_buffer(64); + let sink = RateLimitedOcsfSink::new(buf_tx, 2); + + sink.send(vec![1]); + sink.send(vec![2]); + sink.send(vec![3]); + + assert_eq!(sink.drops(), 1); + let items = rx.drain(); + assert_eq!(items.len(), 2); + } + + #[test] + fn rate_limiter_refills_after_time() { + let (buf_tx, _rx) = buffer::new_telemetry_buffer(64); + let sink = RateLimitedOcsfSink::new(buf_tx, 100); + + for _ in 0..100 { + sink.try_acquire(); + } + assert!(!sink.try_acquire(), "should be exhausted"); + + std::thread::sleep(Duration::from_millis(50)); + assert!(sink.try_acquire(), "should have refilled after 50ms"); + } + + #[test] + fn export_message_wraps_trace_and_ocsf() { + let msg = export_message("sb-1", TelemetryItem::Trace(vec![1, 2, 3])); + let Some(supervisor_message::Payload::OtelExport(export)) = msg.payload else { + panic!("expected OtelExport payload"); + }; + assert_eq!(export.sandbox_id, "sb-1"); + assert_eq!( + export.signal, + Some(otel_export_data::Signal::TraceData(vec![1, 2, 3])) + ); + assert!(export.ocsf_events.is_empty()); + + let msg = export_message("sb-1", TelemetryItem::Ocsf(vec![9])); + let Some(supervisor_message::Payload::OtelExport(export)) = msg.payload else { + panic!("expected OtelExport payload"); + }; + assert_eq!(export.signal, None); + assert_eq!(export.ocsf_events, vec![vec![9]]); + } + + #[tokio::test] + async fn stopped_lifecycle_pends_and_drains_as_noop() { + let mut relay = RelayLifecycle::stopped(); + assert!(!relay.is_running()); + assert!(relay.buffer_metrics().is_none()); + + let pended = tokio::time::timeout(Duration::from_millis(50), relay.next_item()).await; + assert!(pended.is_err(), "a stopped relay must pend, not yield"); + + let (tx, mut rx) = mpsc::channel(8); + relay.stop_and_drain("sb-test", &tx).await; + assert!(matches!(relay, RelayLifecycle::Stopped)); + assert!(rx.try_recv().is_err(), "nothing to drain"); + } + + #[tokio::test] + async fn start_is_running_before_any_stream_arrives() { + let (server, relay) = start(&small_config(), metadata()); + assert!(relay.is_running()); + assert_eq!(relay.buffer_metrics().unwrap().depth(), 0); + assert!( + server.try_reserve().is_some(), + "the server accepts streams before the session confirms anything" + ); + } + + #[tokio::test] + async fn stop_and_drain_flushes_buffered_items_into_tx() { + let (server, mut relay) = start(&small_config(), metadata()); + let (tx, mut rx) = mpsc::channel(8); + + let mut client = connect(&server); + let status = send( + &mut client, + &request( + "POST", + "/v1/traces", + "application/x-protobuf", + &sample_trace_body(), + ), + ) + .await; + assert!(status.contains("200"), "unexpected status: {status}"); + assert_eq!(relay.buffer_metrics().unwrap().depth(), 1); + + relay.stop_and_drain("sb-test", &tx).await; + assert!(matches!(relay, RelayLifecycle::Stopped)); + assert!( + server.try_reserve().is_none(), + "the server refuses streams once drained" + ); + + let msg = rx.try_recv().expect("one drained export message"); + let Some(supervisor_message::Payload::OtelExport(export)) = msg.payload else { + panic!("expected OtelExport payload"); + }; + assert_eq!(export.sandbox_id, "sb-test"); + assert!(matches!( + export.signal, + Some(otel_export_data::Signal::TraceData(_)) + )); + assert!(rx.try_recv().is_err(), "no further messages expected"); + } +} diff --git a/crates/openshell-supervisor-process/src/otlp/receiver.rs b/crates/openshell-supervisor-process/src/otlp/receiver.rs new file mode 100644 index 0000000000..f11c55aaa1 --- /dev/null +++ b/crates/openshell-supervisor-process/src/otlp/receiver.rs @@ -0,0 +1,492 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! OTLP HTTP receiver served on supervisor-mediated streams. +//! +//! The agent exports to the reserved relay address. The sandbox's seccomp +//! broker stages that connect for the supervisor, whose proxy hands the +//! resulting duplex stream to [`OtlpConnectionServer::serve`] instead of +//! dialing upstream. The server speaks HTTP/1.1 on that stream and accepts +//! `POST /v1/traces` with protobuf or JSON bodies. No socket is ever bound. + +use std::convert::Infallible; +use std::sync::{Arc, Mutex, MutexGuard, PoisonError}; +use std::time::Duration; + +use bytes::Bytes; +use http_body_util::Full; +use hyper::body::Incoming; +use hyper::server::conn::http1; +use hyper::service::service_fn; +use hyper::{Method, Request, Response, StatusCode}; +use hyper_util::rt::{TokioIo, TokioTimer}; +use hyper_util::server::graceful::{GracefulShutdown, Watcher}; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio::sync::{OwnedSemaphorePermit, Semaphore, watch}; +use tracing::{debug, warn}; + +use super::buffer::TelemetrySender; +use super::enrichment::{self, ContentType, EnrichmentError}; +use super::{RECEIVER_SHUTDOWN_TIMEOUT, SandboxMetadata}; + +/// Upper bound on concurrently served relay connections. +pub const MAX_CONCURRENT_CONNECTIONS: usize = 64; +const MAX_BODY_SIZE: usize = 4 * 1024 * 1024; // 4 MiB + +/// Time a client has to send request headers before the connection is closed. +pub const HEADER_READ_TIMEOUT: Duration = Duration::from_secs(10); + +/// Reservation for one relay connection. +/// +/// Taken before the sandbox commits the workload socket, so an exhausted +/// server can refuse the open instead of resetting it after `RelayReady`. +pub struct ConnectionPermit { + _permit: OwnedSemaphorePermit, +} + +/// Serves OTLP HTTP on streams handed over by the mediation path. +/// +/// One instance lives for the sandbox lifetime. Each stream is served on the +/// caller's task through [`OtlpConnectionServer::serve`]; the paired +/// [`ReceiverHandle`] owns shutdown. +pub struct OtlpConnectionServer { + buf_tx: TelemetrySender, + metadata: SandboxMetadata, + enrichment_enabled: bool, + connections: Arc, + /// `None` once shutdown has started. Watchers are minted under this lock, + /// so none can be created after the shutdown signal is sent. + graceful: Mutex>, + abort_rx: watch::Receiver, +} + +/// Owns receiver shutdown. +pub struct ReceiverHandle { + server: Arc, + abort_tx: watch::Sender, +} + +fn lock(mutex: &Mutex) -> MutexGuard<'_, T> { + mutex.lock().unwrap_or_else(PoisonError::into_inner) +} + +impl OtlpConnectionServer { + /// Create a server and its shutdown handle. + pub fn new( + buf_tx: TelemetrySender, + metadata: SandboxMetadata, + enrichment_enabled: bool, + ) -> (Arc, ReceiverHandle) { + let (abort_tx, abort_rx) = watch::channel(false); + let server = Arc::new(Self { + buf_tx, + metadata, + enrichment_enabled, + connections: Arc::new(Semaphore::new(MAX_CONCURRENT_CONNECTIONS)), + graceful: Mutex::new(Some(GracefulShutdown::new())), + abort_rx, + }); + let handle = ReceiverHandle { + server: Arc::clone(&server), + abort_tx, + }; + (server, handle) + } + + /// Sender side of the relay buffer, for additional producers such as the + /// OCSF sink. + pub fn sender(&self) -> &TelemetrySender { + &self.buf_tx + } + + /// Reserve a connection slot. `None` when the server is shutting down or + /// already serving [`MAX_CONCURRENT_CONNECTIONS`] streams. + pub fn try_reserve(&self) -> Option { + if lock(&self.graceful).is_none() { + return None; + } + let permit = Arc::clone(&self.connections).try_acquire_owned().ok()?; + Some(ConnectionPermit { _permit: permit }) + } + + fn watcher(&self) -> Option { + lock(&self.graceful).as_ref().map(GracefulShutdown::watcher) + } + + /// Serve one HTTP/1.1 connection on `stream` until the peer closes it, + /// graceful shutdown drains it, or the abort deadline cuts it. Runs on the + /// caller's task and releases `permit` when it returns. + pub async fn serve(&self, permit: ConnectionPermit, stream: S) + where + S: AsyncRead + AsyncWrite + Unpin + Send + 'static, + { + let Some(watcher) = self.watcher() else { + debug!("OTLP receiver: stream arrived during shutdown, dropping"); + return; + }; + + let buf_tx = self.buf_tx.clone(); + let metadata = self.metadata.clone(); + let enrichment_enabled = self.enrichment_enabled; + let svc = service_fn(move |req| { + let buf_tx = buf_tx.clone(); + let metadata = metadata.clone(); + async move { handle_request(req, &buf_tx, &metadata, enrichment_enabled).await } + }); + + let mut builder = http1::Builder::new(); + // A timer is mandatory once any timeout is configured; hyper panics in + // `serve_connection` otherwise. + builder + .timer(TokioTimer::new()) + .header_read_timeout(HEADER_READ_TIMEOUT); + let conn = watcher.watch(builder.serve_connection(TokioIo::new(stream), svc)); + + let mut abort_rx = self.abort_rx.clone(); + tokio::select! { + result = conn => { + if let Err(e) = result { + debug!(error = %e, "OTLP HTTP connection error"); + } + } + _ = abort_rx.wait_for(|aborted| *aborted) => { + debug!("OTLP receiver: aborting connection after graceful timeout"); + } + } + drop(permit); + } +} + +impl ReceiverHandle { + /// Stop accepting new streams, disable keep-alive on every open + /// connection, wait up to [`RECEIVER_SHUTDOWN_TIMEOUT`] for in-flight + /// requests, then cut stragglers. Always returns within roughly the + /// timeout. A second call is a no-op. + pub async fn shutdown(self) { + let Some(graceful) = lock(&self.server.graceful).take() else { + return; + }; + if tokio::time::timeout(RECEIVER_SHUTDOWN_TIMEOUT, graceful.shutdown()) + .await + .is_err() + { + warn!( + open_connections = + MAX_CONCURRENT_CONNECTIONS - self.server.connections.available_permits(), + "OTLP receiver: aborting connections still open after graceful timeout" + ); + self.abort_tx.send_replace(true); + } + } +} + +async fn handle_request( + req: Request, + buf_tx: &TelemetrySender, + metadata: &SandboxMetadata, + enrichment_enabled: bool, +) -> Result>, Infallible> { + if req.method() != Method::POST || req.uri().path() != "/v1/traces" { + return Ok(Response::builder() + .status(StatusCode::NOT_FOUND) + .body(Full::new(Bytes::from("{\"error\":\"not found\"}"))) + .unwrap()); + } + + let Some(content_type) = parse_content_type(req.headers()) else { + return Ok(Response::builder() + .status(StatusCode::UNSUPPORTED_MEDIA_TYPE) + .body(Full::new(Bytes::from( + "{\"error\":\"unsupported content type\"}", + ))) + .unwrap()); + }; + + let limited = http_body_util::Limited::new(req.into_body(), MAX_BODY_SIZE); + let body = match http_body_util::BodyExt::collect(limited).await { + Ok(collected) => collected.to_bytes(), + Err(e) => { + let status = if e.to_string().contains("length limit exceeded") { + warn!("OTLP request body exceeds 4 MiB limit"); + StatusCode::PAYLOAD_TOO_LARGE + } else { + warn!(error = %e, "failed to read OTLP request body"); + StatusCode::BAD_REQUEST + }; + return Ok(Response::builder() + .status(status) + .body(Full::new(Bytes::from( + "{\"error\":\"failed to read body\"}", + ))) + .unwrap()); + } + }; + + match enrichment::enrich_spans(&body, content_type, metadata, enrichment_enabled) { + Ok(enriched) => { + buf_tx.send_trace(enriched); + Ok(Response::builder() + .status(StatusCode::OK) + .header("content-type", "application/x-protobuf") + .body(Full::new(Bytes::new())) + .unwrap()) + } + Err(EnrichmentError::ProtobufDecode(e)) => { + warn!(error = %e, "malformed protobuf OTLP request"); + Ok(Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Full::new(Bytes::from( + "{\"error\":\"malformed protobuf request\"}", + ))) + .unwrap()) + } + Err(EnrichmentError::JsonDecode(e)) => { + warn!(error = %e, "malformed JSON OTLP request"); + Ok(Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Full::new(Bytes::from( + "{\"error\":\"malformed JSON request\"}", + ))) + .unwrap()) + } + } +} + +fn parse_content_type(headers: &hyper::HeaderMap) -> Option { + let ct = headers.get("content-type")?.to_str().ok()?; + if ct.starts_with("application/x-protobuf") { + Some(ContentType::Protobuf) + } else if ct.starts_with("application/json") { + Some(ContentType::Json) + } else { + None + } +} + +/// In-memory HTTP helpers shared by the receiver and lifecycle tests. They +/// stand in for the boundary duplex stream the proxy hands over in +/// production. +#[cfg(test)] +pub(crate) mod test_util { + use std::sync::Arc; + use std::time::Duration; + + use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest; + use opentelemetry_proto::tonic::trace::v1::{ResourceSpans, ScopeSpans, Span}; + use prost::Message; + use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, DuplexStream}; + + use super::OtlpConnectionServer; + use crate::otlp::SandboxMetadata; + + pub fn metadata() -> SandboxMetadata { + SandboxMetadata { + sandbox_id: "sb-test".into(), + workspace_id: "ws-test".into(), + policy: "policy".into(), + user: "1000".into(), + image: "image".into(), + driver: "docker".into(), + } + } + + /// A minimal but non-empty protobuf `ExportTraceServiceRequest`. + pub fn sample_trace_body() -> Vec { + ExportTraceServiceRequest { + resource_spans: vec![ResourceSpans { + scope_spans: vec![ScopeSpans { + spans: vec![Span { + name: "test-span".into(), + ..Default::default() + }], + ..Default::default() + }], + ..Default::default() + }], + } + .encode_to_vec() + } + + pub fn request(method: &str, path: &str, content_type: &str, body: &[u8]) -> Vec { + let mut req = format!( + "{method} {path} HTTP/1.1\r\nHost: localhost\r\nContent-Type: {content_type}\r\nContent-Length: {}\r\n\r\n", + body.len() + ) + .into_bytes(); + req.extend_from_slice(body); + req + } + + /// Open a client stream whose server half is served on a spawned task, + /// the way the proxy hook serves a staged boundary stream. + pub fn connect(server: &Arc) -> DuplexStream { + let (client, server_half) = tokio::io::duplex(64 * 1024); + let permit = server.try_reserve().expect("connection slot"); + let server = Arc::clone(server); + tokio::spawn(async move { server.serve(permit, server_half).await }); + client + } + + /// Write `req` and read until the response head is complete. Returns the + /// status line; the stream stays open. + pub async fn send(stream: &mut S, req: &[u8]) -> String { + stream.write_all(req).await.expect("write request"); + let mut buf = Vec::new(); + let mut chunk = [0u8; 1024]; + loop { + let n = tokio::time::timeout(Duration::from_secs(5), stream.read(&mut chunk)) + .await + .expect("response within 5s") + .expect("read response"); + assert!(n > 0, "connection closed before response head"); + buf.extend_from_slice(&chunk[..n]); + if buf.windows(4).any(|w| w == b"\r\n\r\n") { + break; + } + } + let head = String::from_utf8_lossy(&buf); + head.lines().next().unwrap_or_default().to_string() + } +} + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use tokio::io::{AsyncReadExt, AsyncWriteExt}; + + use super::test_util::{connect, metadata, request, sample_trace_body, send}; + use super::*; + use crate::otlp::buffer::{TelemetryReceiver, new_telemetry_buffer}; + + fn start() -> (Arc, ReceiverHandle, TelemetryReceiver) { + let (buf_tx, buf_rx) = new_telemetry_buffer(16); + let (server, handle) = OtlpConnectionServer::new(buf_tx, metadata(), true); + (server, handle, buf_rx) + } + + #[tokio::test] + async fn shutdown_completes_with_idle_keepalive_connection() { + let (server, handle, buf_rx) = start(); + + let mut client = connect(&server); + let status = send( + &mut client, + &request( + "POST", + "/v1/traces", + "application/x-protobuf", + &sample_trace_body(), + ), + ) + .await; + assert!(status.contains("200"), "unexpected status: {status}"); + assert_eq!(buf_rx.metrics().depth(), 1); + + // The client keeps the HTTP/1.1 connection open. Shutdown must not + // wait for it to go away on its own. + let deadline = RECEIVER_SHUTDOWN_TIMEOUT + Duration::from_secs(1); + tokio::time::timeout(deadline, handle.shutdown()) + .await + .expect("shutdown must complete despite an open keep-alive connection"); + + // The server closed the idle connection. + let mut buf = [0u8; 16]; + let n = tokio::time::timeout(Duration::from_secs(2), client.read(&mut buf)) + .await + .expect("server close within 2s") + .unwrap_or(0); + assert_eq!(n, 0, "expected EOF from server after shutdown"); + } + + #[tokio::test] + async fn shutdown_completes_with_half_sent_headers() { + let (server, handle, _buf_rx) = start(); + + let mut client = connect(&server); + client + .write_all(b"POST /v1/traces HTTP/1.1\r\n") + .await + .unwrap(); + tokio::time::sleep(Duration::from_millis(50)).await; + + let deadline = RECEIVER_SHUTDOWN_TIMEOUT + Duration::from_secs(1); + tokio::time::timeout(deadline, handle.shutdown()) + .await + .expect("shutdown must complete with an in-progress request"); + } + + #[tokio::test] + async fn status_codes_for_not_found_unsupported_media_type_and_ok() { + let (server, handle, buf_rx) = start(); + + let mut client = connect(&server); + let status = send( + &mut client, + &request("GET", "/v1/traces", "application/x-protobuf", b""), + ) + .await; + assert!(status.contains("404"), "GET should be 404, got {status}"); + + let mut client = connect(&server); + let status = send( + &mut client, + &request("POST", "/v1/traces", "text/plain", b"x"), + ) + .await; + assert!( + status.contains("415"), + "text/plain should be 415, got {status}" + ); + + let mut client = connect(&server); + let status = send( + &mut client, + &request( + "POST", + "/v1/traces", + "application/x-protobuf", + &sample_trace_body(), + ), + ) + .await; + assert!( + status.contains("200"), + "valid protobuf should be 200, got {status}" + ); + assert_eq!(buf_rx.metrics().depth(), 1); + + handle.shutdown().await; + } + + #[tokio::test] + async fn try_reserve_refuses_beyond_max_connections_and_recovers() { + let (server, _handle, _buf_rx) = start(); + + let mut permits: Vec = (0..MAX_CONCURRENT_CONNECTIONS) + .map(|i| server.try_reserve().unwrap_or_else(|| panic!("slot {i}"))) + .collect(); + assert!( + server.try_reserve().is_none(), + "slot beyond the maximum must be refused" + ); + + drop(permits.pop()); + assert!( + server.try_reserve().is_some(), + "a released slot is available again" + ); + } + + #[tokio::test] + async fn try_reserve_refuses_after_shutdown() { + let (server, handle, _buf_rx) = start(); + assert!(server.try_reserve().is_some()); + + handle.shutdown().await; + assert!( + server.try_reserve().is_none(), + "no new streams once shutdown has started" + ); + } +} diff --git a/crates/openshell-supervisor-process/src/supervisor_session.rs b/crates/openshell-supervisor-process/src/supervisor_session.rs index 5be01017eb..64f15e4cde 100644 --- a/crates/openshell-supervisor-process/src/supervisor_session.rs +++ b/crates/openshell-supervisor-process/src/supervisor_session.rs @@ -18,8 +18,9 @@ use std::time::Duration; use openshell_core::proto::open_shell_client::OpenShellClient; use openshell_core::proto::{ FinalizeMainProcessExitRequest, GatewayMessage, RelayFrame, RelayInit, RelayOpen, - RelayOpenResult, ReportMainProcessExitRequest, SupervisorHeartbeat, SupervisorHello, - SupervisorMessage, TcpRelayTarget, gateway_message, relay_open, supervisor_message, + RelayOpenResult, ReportMainProcessExitRequest, SessionAccepted, SupervisorHeartbeat, + SupervisorHello, SupervisorMessage, TcpRelayTarget, gateway_message, relay_open, + supervisor_message, }; use openshell_isolation_interface::contract::{BoundaryLoopbackConnector, LoopbackTarget}; use openshell_ocsf::{ @@ -27,22 +28,64 @@ use openshell_ocsf::{ OcsfEvent, SeverityId, StatusId, ocsf_emit, }; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; -use tokio::sync::{mpsc, watch}; +use tokio::sync::{mpsc, oneshot, watch}; use tokio_stream::StreamExt; -use tracing::{debug, warn}; +use tracing::{debug, info, warn}; use openshell_core::grpc_client; use openshell_core::transport_errors::is_expected_transport_close_status; +use crate::otlp::{RelayLifecycle, export_message}; + const INITIAL_BACKOFF: Duration = Duration::from_secs(1); const MAX_BACKOFF: Duration = Duration::from_secs(30); +/// Capability name under which the supervisor offers OTLP relaying. +const OTEL_EXPORT_CAPABILITY: &str = "otel_export"; + +/// Final-drain request: the session acks on this sender once the OTLP +/// receiver is stopped and the buffer is flushed onto the session stream. +pub type DrainRequest = oneshot::Sender<()>; + +/// OTLP relay state handed to the session task. +/// +/// The session owns the relay for the sandbox lifetime: it forwards buffered +/// telemetry while the gateway has confirmed `otel_export`, and services one +/// final drain request before the main-process exit is reported. +pub struct TelemetryRelay { + /// Running relay: receiver handle and bounded buffer. + pub relay: RelayLifecycle, + /// Receives the final drain request from the access plane. + pub drain_rx: oneshot::Receiver, +} + /// Runtime identity and status channel shared with a supervisor session task. pub struct SessionRuntimeContext { /// Identifies the local supervisor process across gateway reconnects. pub instance_id: String, /// Publishes the currently accepted gateway session to sibling reporters. pub session_id_updates: Option>>, + /// OTLP relay, when the supervisor started one. + pub telemetry: Option, +} + +/// Capabilities the supervisor advertises in `SupervisorHello`. +fn advertised_capabilities(relay_running: bool) -> Vec { + if relay_running { + vec![OTEL_EXPORT_CAPABILITY.to_string()] + } else { + Vec::new() + } +} + +/// Whether buffered telemetry may be forwarded on this session: the relay +/// must be running and the gateway must have confirmed `otel_export`. +fn otel_forwarding_active(relay_running: bool, accepted: &SessionAccepted) -> bool { + relay_running + && accepted + .capabilities + .iter() + .any(|capability| capability == OTEL_EXPORT_CAPABILITY) } /// Parse a gRPC endpoint URI into an OCSF `Endpoint` (host + port). Falls back @@ -336,6 +379,7 @@ pub fn spawn_with_readiness( instance_id: runtime.instance_id, session_id_updates: runtime.session_id_updates, ready_tx, + telemetry: runtime.telemetry, }; (tokio::spawn(run_session_loop(config)), ready_rx) } @@ -351,16 +395,25 @@ struct SessionConfig { /// Publishes the currently accepted session to sibling control-plane reporters. session_id_updates: Option>>, ready_tx: watch::Sender, + telemetry: Option, } -async fn run_session_loop(config: SessionConfig) { +async fn run_session_loop(mut config: SessionConfig) { let mut backoff = INITIAL_BACKOFF; let mut attempt: u64 = 0; + // The relay and the pending drain request live here, outside the + // reconnect loop, so the receiver and its buffer survive gateway + // reconnects instead of being re-created per session. + let (mut relay, mut drain_rx) = match config.telemetry.take() { + Some(TelemetryRelay { relay, drain_rx }) => (relay, Some(drain_rx)), + None => (RelayLifecycle::stopped(), None), + }; + loop { attempt += 1; - let result = run_single_session(&config, attempt).await; + let result = run_single_session(&config, attempt, &mut relay, &mut drain_rx).await; if let Some(updates) = &config.session_id_updates { updates.send_replace(None); } @@ -394,6 +447,8 @@ async fn run_session_loop(config: SessionConfig) { async fn run_single_session( config: &SessionConfig, connection_epoch: u64, + relay: &mut RelayLifecycle, + drain_rx: &mut Option>, ) -> Result<(), Box> { // Connect to the gateway. The same `Channel` is used for both the // long-lived control stream and all data-plane `RelayStream` calls, so @@ -414,6 +469,8 @@ async fn run_single_session( sandbox_id: config.sandbox_id.clone(), instance_id: config.instance_id.clone(), connection_epoch, + // The gateway confirms only capabilities it can serve. + capabilities: advertised_capabilities(relay.is_running()), supports_provider_readiness: true, })), }) @@ -441,6 +498,16 @@ async fn run_single_session( _ => return Err("expected SessionAccepted or SessionRejected".into()), }; + // Forwarding is gated per session on the negotiated capability. The + // receiver keeps serving either way; a declining session just leaves + // items in the bounded buffer until a later session confirms. + let mut otel_active = otel_forwarding_active(relay.is_running(), &accepted); + if otel_active { + info!("gateway confirmed otel_export capability; OTLP forwarding active"); + } else if relay.is_running() { + debug!("gateway did not confirm otel_export; OTLP forwarding paused for this session"); + } + let heartbeat_secs = accepted .heartbeat_interval .as_ref() @@ -458,12 +525,41 @@ async fn run_single_session( ocsf_emit!(event); config.ready_tx.send_replace(true); - // Main loop: receive gateway messages + send heartbeats. + // Main loop: receive gateway messages, send heartbeats, forward OTLP + // exports, and service the final telemetry drain request. let mut heartbeat_interval = tokio::time::interval(Duration::from_secs(heartbeat_secs)); heartbeat_interval.tick().await; // skip immediate tick loop { tokio::select! { + item = relay.next_item(), if otel_active => { + match item { + Some(item) => { + // Non-blocking on purpose: telemetry must never stall + // control traffic on the shared outbound channel. + if tx.try_send(export_message(&config.sandbox_id, item)).is_err() { + debug!("OTEL relay: session channel full or closed, dropping message"); + } + } + None => otel_active = false, + } + } + request = async { + match drain_rx.as_mut() { + Some(rx) => rx.await, + None => std::future::pending().await, + } + } => { + // A completed oneshot must not be polled again. + *drain_rx = None; + otel_active = false; + if let Ok(done_tx) = request { + relay.stop_and_drain(&config.sandbox_id, &tx).await; + let _ = done_tx.send(()); + } + // Keep the session up: heartbeats and relays must continue + // until the gateway finalizes the main-process exit. + } msg = inbound.message() => { let msg = match map_session_stream_message( msg, @@ -864,6 +960,48 @@ async fn open_tcp_target( Ok(Box::new(stream)) } +#[cfg(test)] +mod telemetry_tests { + use super::*; + + fn accepted_with(capabilities: &[&str]) -> SessionAccepted { + SessionAccepted { + session_id: "s1".to_string(), + capabilities: capabilities.iter().map(|c| (*c).to_string()).collect(), + heartbeat_interval: None, + } + } + + #[test] + fn otel_export_is_advertised_only_with_a_running_relay() { + assert_eq!( + advertised_capabilities(true), + vec!["otel_export".to_string()] + ); + assert!(advertised_capabilities(false).is_empty()); + } + + #[test] + fn forwarding_requires_both_a_running_relay_and_gateway_confirmation() { + assert!(otel_forwarding_active( + true, + &accepted_with(&["otel_export"]) + )); + assert!( + !otel_forwarding_active(true, &accepted_with(&[])), + "a declining gateway pauses forwarding" + ); + assert!( + !otel_forwarding_active(false, &accepted_with(&["otel_export"])), + "a stopped relay never forwards, whatever the gateway says" + ); + assert!( + !otel_forwarding_active(true, &accepted_with(&["metrics_export"])), + "only the exact capability name counts" + ); + } +} + #[cfg(test)] fn validate_tcp_target(target: &TcpRelayTarget) -> Result<(), String> { normalize_tcp_target_host(target).map(|_| ()) diff --git a/crates/openshell-supervisor/Cargo.toml b/crates/openshell-supervisor/Cargo.toml index 483dd50bff..0563d006ce 100644 --- a/crates/openshell-supervisor/Cargo.toml +++ b/crates/openshell-supervisor/Cargo.toml @@ -54,6 +54,8 @@ bundled-ca-roots = ["openshell-supervisor-network/bundled-ca-roots"] [dev-dependencies] tokio = { workspace = true, features = ["test-util"] } futures = { workspace = true } +opentelemetry-proto = { workspace = true } +prost = { workspace = true } temp-env = "0.3" tokio-tungstenite = { workspace = true } diff --git a/crates/openshell-supervisor/src/lib.rs b/crates/openshell-supervisor/src/lib.rs index 99607dbedb..3c03d16634 100644 --- a/crates/openshell-supervisor/src/lib.rs +++ b/crates/openshell-supervisor/src/lib.rs @@ -19,6 +19,7 @@ mod activity_aggregator; mod denial_aggregator; mod endpoint_status; mod mechanistic_mapper; +mod otlp_relay; mod provider_readiness; use miette::{IntoDiagnostic, Result, WrapErr}; @@ -572,6 +573,7 @@ pub async fn run_network_proxy( #[cfg(target_os = "linux")] None, None, + None, ) .await?; @@ -835,6 +837,9 @@ pub async fn run_sandbox( let (backend, verified) = registry .resolve(backend_descriptor, &admitted_backend_name) .map_err(|error| miette::miette!(error.to_string()))?; + // Span enrichment reads these after the descriptor's identity moves into + // the sandbox context below. + let workload_uid = runtime_descriptor.workload_identity.uid; let context = openshell_isolation_interface::contract::SandboxContext { sandbox_id: sandbox_id.clone().unwrap_or_default(), session_id, @@ -918,6 +923,27 @@ pub async fn run_sandbox( // API read the current value so proposals target the correct workspace. let (workspace_tx, workspace_rx) = tokio::sync::watch::channel(String::new()); + // OTLP relay: agent processes export to the reserved relay address and + // the proxy serves those staged streams with this server. It starts + // before networking so the first staged stream finds it; forwarding to + // the gateway waits until a session confirms `otel_export`. + let (otlp_server, otlp_relay) = openshell_supervisor_process::otlp::start( + &openshell_supervisor_process::otlp::RelayConfig::default(), + openshell_supervisor_process::otlp::SandboxMetadata { + sandbox_id: sandbox_id.clone().unwrap_or_default(), + workspace_id: workspace_rx.borrow().clone(), + policy: sandbox_name_for_agg.clone().unwrap_or_default(), + user: workload_uid.to_string(), + // Only the Podman driver sets this today; the OCSF context reads + // the same variable and shares the gap. + image: std::env::var("OPENSHELL_CONTAINER_IMAGE").unwrap_or_default(), + // The backend-neutral runtime descriptor no longer names the + // compute driver; empty until the gateway injects it. + driver: String::new(), + }, + ); + let otlp_destination = otlp_relay::reserved_destination(otlp_server); + let remote_network_source = remote_boundary.0.network_mediation_source(); let remote_host_gateway_ip = remote_boundary.0.host_gateway_ip(); let (remote_ready, backend_name, ca_file_paths) = { @@ -957,6 +983,7 @@ pub async fn run_sandbox( #[cfg(target_os = "linux")] None, Some(remote_network_source), + Some(otlp_destination), ) .await?, ); @@ -1168,6 +1195,7 @@ pub async fn run_sandbox( running.loopback_connector(), agent.clone(), Some(supervisor_session_updates), + Some(otlp_relay), ) .await?; info!(backend = %backend_name, "Control-mode access plane started"); @@ -1256,6 +1284,15 @@ pub async fn run_sandbox( .into_diagnostic() .wrap_err("persist canonical-process completion marker")?; } + // Flush relayed telemetry before the exit is reported so final spans + // still reach the gateway; bounded so an unreachable gateway cannot + // delay the report. + if !completion_cancelled { + let drain = boundary_access + .drain_telemetry(openshell_supervisor_process::delegated::OTEL_FINAL_DRAIN_TIMEOUT); + completion_cancelled = + completion_phase_or_shutdown(drain, shutdown_requested.as_mut()).await; + } if !completion_cancelled && let (Some(endpoint), Some(id)) = (openshell_endpoint.as_deref(), sandbox_id.as_deref()) diff --git a/crates/openshell-supervisor/src/otlp_relay.rs b/crates/openshell-supervisor/src/otlp_relay.rs new file mode 100644 index 0000000000..880dc23ab8 --- /dev/null +++ b/crates/openshell-supervisor/src/otlp_relay.rs @@ -0,0 +1,138 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Binds the OTLP relay server to the proxy's reserved-destination hook. + +use std::net::SocketAddr; +use std::sync::Arc; + +use openshell_isolation_interface::contract::BoundaryDuplexStream; +use openshell_supervisor_network::proxy::{ + ReservedDestination, ReservedStreamFuture, ReservedStreamHandler, +}; +use openshell_supervisor_process::otlp::OtlpConnectionServer; + +/// Serves staged workload connections to the relay address with the OTLP +/// connection server. +struct OtlpRelayHandler(Arc); + +impl ReservedStreamHandler for OtlpRelayHandler { + fn serve(&self, stream: BoundaryDuplexStream) -> Option { + // Reserve before the proxy answers `RelayReady`, so an exhausted or + // stopped server refuses the open instead of resetting it later. + let permit = self.0.try_reserve()?; + let server = Arc::clone(&self.0); + Some(Box::pin(async move { server.serve(permit, stream).await })) + } +} + +/// The reserved destination the proxy serves with `server`. +pub fn reserved_destination(server: Arc) -> ReservedDestination { + let addr: SocketAddr = openshell_core::sandbox_env::OTLP_RELAY_ADDR + .parse() + .expect("OTLP_RELAY_ADDR is a valid socket address"); + ReservedDestination { + addr, + handler: Arc::new(OtlpRelayHandler(server)), + } +} + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest; + use opentelemetry_proto::tonic::trace::v1::{ResourceSpans, ScopeSpans, Span}; + use prost::Message; + use tokio::io::{AsyncReadExt, AsyncWriteExt}; + + use openshell_supervisor_process::otlp::{RelayConfig, SandboxMetadata}; + + use super::*; + + fn metadata() -> SandboxMetadata { + SandboxMetadata { + sandbox_id: "sb-test".into(), + workspace_id: "ws-test".into(), + policy: "policy".into(), + user: "1000".into(), + image: String::new(), + driver: "kubernetes".into(), + } + } + + /// A minimal but non-empty protobuf `ExportTraceServiceRequest`. + fn sample_trace_body() -> Vec { + ExportTraceServiceRequest { + resource_spans: vec![ResourceSpans { + scope_spans: vec![ScopeSpans { + spans: vec![Span { + name: "relay-span".into(), + ..Default::default() + }], + ..Default::default() + }], + ..Default::default() + }], + } + .encode_to_vec() + } + + #[tokio::test] + async fn reserved_destination_serves_otlp_over_a_boundary_stream() { + let (server, relay) = + openshell_supervisor_process::otlp::start(&RelayConfig::default(), metadata()); + let reserved = reserved_destination(server); + assert_eq!( + reserved.addr.to_string(), + openshell_core::sandbox_env::OTLP_RELAY_ADDR + ); + + let (mut client, boundary) = tokio::io::duplex(64 * 1024); + let serve_future = reserved + .handler + .serve(Box::new(boundary)) + .expect("a fresh server accepts a stream"); + let conn = tokio::spawn(serve_future); + + let body = sample_trace_body(); + let request = format!( + "POST /v1/traces HTTP/1.1\r\nHost: relay\r\nContent-Type: application/x-protobuf\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", + body.len() + ); + client.write_all(request.as_bytes()).await.unwrap(); + client.write_all(&body).await.unwrap(); + + let mut response = Vec::new(); + tokio::time::timeout(Duration::from_secs(5), client.read_to_end(&mut response)) + .await + .expect("response within 5s") + .unwrap(); + let head = String::from_utf8_lossy(&response); + assert!( + head.starts_with("HTTP/1.1 200"), + "expected 200 from the relay, got: {head}" + ); + assert_eq!(relay.buffer_metrics().unwrap().depth(), 1); + + tokio::time::timeout(Duration::from_secs(5), conn) + .await + .expect("connection task ends after Connection: close") + .unwrap(); + } + + #[tokio::test] + async fn reserved_destination_refuses_streams_once_the_relay_is_drained() { + let (server, mut relay) = + openshell_supervisor_process::otlp::start(&RelayConfig::default(), metadata()); + let reserved = reserved_destination(server); + let (tx, _rx) = tokio::sync::mpsc::channel(4); + relay.stop_and_drain("sb-test", &tx).await; + + let (_client, boundary) = tokio::io::duplex(1024); + assert!( + reserved.handler.serve(Box::new(boundary)).is_none(), + "the proxy must refuse the open rather than reset it after RelayReady" + ); + } +} diff --git a/deploy/docker/gateway.toml b/deploy/docker/gateway.toml index c0cbda4ef6..ac69a7f6f7 100644 --- a/deploy/docker/gateway.toml +++ b/deploy/docker/gateway.toml @@ -53,3 +53,13 @@ grpc_endpoint = "http://host.openshell.internal:8080" # Explicit supervisor-compatible Docker default. Set RuntimeDefault or # Localhost/ only when the daemon host has AppArmor available. app_armor_profile = "Unconfined" + +# Uncomment to enable the OTLP telemetry relay. The supervisor collects +# traces from sandbox agents and forwards them through the gateway to the +# configured collector endpoint (gRPC, typically port 4317). +# [openshell.gateway.otlp] +# endpoint = "http://host.docker.internal:4317" +# service_name = "openshell-gateway-dev" +# Optional separate collector for relayed agent traces; omit to send agent +# traces to `endpoint`. +# agent_endpoint = "http://host.docker.internal:4319" diff --git a/deploy/helm/openshell/README.md b/deploy/helm/openshell/README.md index df3ea173ad..3da2916032 100644 --- a/deploy/helm/openshell/README.md +++ b/deploy/helm/openshell/README.md @@ -445,6 +445,7 @@ discovery endpoint or its TLS CA. | server.oidc.rolesClaim | string | `""` | Dot-separated path to the roles array in the JWT claims. Keycloak: "realm_access.roles", Entra ID: "roles", Okta: "groups". | | server.oidc.scopesClaim | string | `""` | Dot-separated path to the scopes array in the JWT claims. | | server.oidc.userRole | string | `""` | Role name for standard user access. | +| server.otlp.agentEndpoint | string | `""` | Separate OTLP/gRPC collector endpoint for relayed agent traces. Empty sends agent traces to `endpoint`. | | server.otlp.endpoint | string | `""` | OTLP/gRPC collector endpoint, conventionally using port 4317. | | server.otlp.serviceName | string | `""` | Gateway OpenTelemetry service name. Empty uses openshell-gateway. | | server.policyValidationFailureMode | string | `"fail_closed"` | Posture when a candidate sandbox policy fails validation. `fail_closed` deactivates the previous policy; `retain_last_valid` keeps it active. | diff --git a/deploy/helm/openshell/templates/gateway-config.yaml b/deploy/helm/openshell/templates/gateway-config.yaml index a3e0210a35..bd1f40c1cf 100644 --- a/deploy/helm/openshell/templates/gateway-config.yaml +++ b/deploy/helm/openshell/templates/gateway-config.yaml @@ -87,6 +87,9 @@ data: {{- if $otlp.serviceName }} service_name = {{ $otlp.serviceName | quote }} {{- end }} + {{- if $otlp.agentEndpoint }} + agent_endpoint = {{ $otlp.agentEndpoint | quote }} + {{- end }} {{- end }} {{- if $ocsfLog.enabled }} diff --git a/deploy/helm/openshell/tests/gateway_config_test.yaml b/deploy/helm/openshell/tests/gateway_config_test.yaml index 16ccfaee6b..576adc5f93 100644 --- a/deploy/helm/openshell/tests/gateway_config_test.yaml +++ b/deploy/helm/openshell/tests/gateway_config_test.yaml @@ -273,6 +273,16 @@ tests: path: data["gateway.toml"] pattern: '(?ms)\[openshell\.gateway\.otlp\].*?endpoint\s*=\s*"http://otel-collector\.observability\.svc:4317".*?service_name\s*=\s*"production-gateway"' + - it: renders a separate agent-trace collector endpoint when configured + template: templates/gateway-config.yaml + set: + server.otlp.endpoint: http://otel-collector.observability.svc:4317 + server.otlp.agentEndpoint: http://agent-collector.observability.svc:4317 + asserts: + - matchRegex: + path: data["gateway.toml"] + pattern: '(?ms)\[openshell\.gateway\.otlp\].*?endpoint\s*=\s*"http://otel-collector\.observability\.svc:4317".*?agent_endpoint\s*=\s*"http://agent-collector\.observability\.svc:4317"' + - it: omits OTLP tracing configuration by default template: templates/gateway-config.yaml asserts: diff --git a/deploy/helm/openshell/values.yaml b/deploy/helm/openshell/values.yaml index 4bf21b88a8..69d78de94b 100644 --- a/deploy/helm/openshell/values.yaml +++ b/deploy/helm/openshell/values.yaml @@ -270,6 +270,8 @@ server: endpoint: "" # -- Gateway OpenTelemetry service name. Empty uses openshell-gateway. serviceName: "" + # -- Separate OTLP/gRPC collector endpoint for relayed agent traces. Empty sends agent traces to `endpoint`. + agentEndpoint: "" # Gateway-native OCSF JSONL output. ocsfLog: # -- Write gateway OCSF events as JSONL. diff --git a/docs/how-it-works/gateways/configuration.mdx b/docs/how-it-works/gateways/configuration.mdx index 3b70608661..7ef8e98bba 100644 --- a/docs/how-it-works/gateways/configuration.mdx +++ b/docs/how-it-works/gateways/configuration.mdx @@ -320,6 +320,8 @@ The gateway already uses the Rust `tracing` framework for structured logs sent t [openshell.gateway.otlp] endpoint = "http://otel-collector.observability.svc:4317" service_name = "openshell-gateway" +# Optional separate collector for relayed agent traces. +# agent_endpoint = "http://agent-collector.observability.svc:4317" ``` `endpoint` is required and must be a valid URI. If it is malformed, the gateway logs the configuration error and continues with export disabled. It does not connect at startup: an unreachable collector produces export failures, never a failure to serve. @@ -334,9 +336,16 @@ Only OpenTelemetry traces are exported. Inbound gRPC and HTTP requests produce s The gateway forwards the OTLP configuration, configured gateway name, configured compute driver, and W3C trace context to managed external drivers. Built-in drivers also export their spans to the same collector through dedicated in-process providers. Driver spans retain the gateway trace context, use a distinct service name such as `openshell-driver-docker` or `openshell-driver-podman`, and carry the same `openshell.gateway.name` and `openshell.gateway.compute_driver` resource attributes as gateway spans. Compute-driver client and server spans use the same fully qualified protobuf operation name, such as `openshell.compute.v1.ComputeDriver/CreateSandbox`, in both the span name and `rpc.method`. The service name and span kind distinguish each side. Backend-prefixed child spans identify implementation work. A streaming watch records a terminal status when observed; consumer teardown without a terminal status leaves the span status unset. Operator-run external drivers own their own telemetry configuration. +### Agent Telemetry Relay + +Configuring this table also enables the sandbox telemetry relay. `CreateSandbox` sets `OTEL_EXPORTER_OTLP_ENDPOINT=http://192.0.0.8:4318` and `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf` in each new sandbox's environment unless the caller already supplied an endpoint. The values persist in the sandbox spec and survive restarts. OTel SDKs inside the sandbox export to that address over HTTP/protobuf or HTTP/JSON. + +The address is never routed. The sandbox stages the connection for the supervisor, which serves the OTLP receiver itself, adds `openshell.sandbox.id`, `openshell.workspace.id`, `openshell.sandbox.policy`, `openshell.sandbox.user`, `openshell.sandbox.image`, `openshell.sandbox.driver`, and `openshell.telemetry.source=agent`, and forwards the spans to the gateway. The gateway exports them through a dedicated exporter, so agent spans arrive with the supervisor's resource attributes rather than the gateway's. Agent traces ride their own lane: set `agent_endpoint` to send them to a separate collector, for example one deployed near the workloads or scaled independently of infrastructure tracing. When `agent_endpoint` is absent, agent traces go to `endpoint` and one collector serves both lanes; the `openshell.telemetry.source=agent` attribute still distinguishes them. Agent traces need no sandbox egress and no network policy entry. Telemetry never blocks sandbox control traffic: the relay buffers up to 4096 items per sandbox and drops the newest on overflow. + For Helm deployments, set `server.otlp.endpoint` to render this table. The optional `server.otlp.serviceName` value overrides the gateway service name; -driver service names remain fixed. +driver service names remain fixed. The optional `server.otlp.agentEndpoint` +value renders `agent_endpoint` for a separate agent-trace collector. The local `mise run helm:k3s:create` workflow installs a trace collector and UI, enables Agent Sandbox controller tracing on supported releases, and configures diff --git a/docs/how-it-works/sandboxes/runtimes.mdx b/docs/how-it-works/sandboxes/runtimes.mdx index 505f2cc69b..de65fc4ead 100644 --- a/docs/how-it-works/sandboxes/runtimes.mdx +++ b/docs/how-it-works/sandboxes/runtimes.mdx @@ -254,6 +254,10 @@ After upgrading Agent Sandbox, restart the gateway. Workspace storage settings cannot change after a sandbox is created. Delete and recreate the sandbox to change them. +### Relay Agent Telemetry + +When the gateway has `[openshell.gateway.otlp]` configured, agents in the workload Pod export OpenTelemetry traces to `http://192.0.0.8:4318`. That address is never routed. The sandbox seccomp broker stages the connection for the supervisor over the existing boundary channel, and the supervisor serves the OTLP receiver on that stream, adds sandbox attributes to the spans, and forwards them to the collector through the gateway. The workload egress fence stays empty and no NetworkPolicy change is needed. See [OTLP Export](/how-it-works/gateways/configuration#otlp-export). + ### Kubernetes PVC Mounts Mount existing PersistentVolumeClaims into the agent container through driver config. Any mount under `/sandbox` replaces the default workspace PVC for that sandbox. diff --git a/proto/openshell.proto b/proto/openshell.proto index 83ffbe75ad..c93529387d 100644 --- a/proto/openshell.proto +++ b/proto/openshell.proto @@ -2969,6 +2969,26 @@ message GetSandboxLogsResponse { // Supervisor session messages // --------------------------------------------------------------------------- +// OpenTelemetry export data forwarded from supervisor to gateway. +// Contains agent trace spans and/or OCSF events from a single sandbox. +message OtelExportData { + // Source sandbox identifier. + string sandbox_id = 1; + + // The OTLP signal being forwarded. Each variant carries the serialized + // OTLP Export*ServiceRequest in protobuf encoding, pre-enriched with + // sandbox resource attributes. + oneof signal { + bytes trace_data = 2; + // bytes metrics_data = 4; // future + // bytes logs_data = 5; // future + } + + // Serialized OCSF events (each entry is a JSON-encoded OCSF event). + // Empty when only OTLP signal data is being forwarded. + repeated bytes ocsf_events = 3; +} + // Envelope for supervisor-to-gateway messages on the ConnectSupervisor stream. message SupervisorMessage { oneof payload { @@ -2976,6 +2996,7 @@ message SupervisorMessage { SupervisorHeartbeat heartbeat = 2; RelayOpenResult relay_open_result = 3; RelayClose relay_close = 4; + OtelExportData otel_export = 5; } } @@ -3001,6 +3022,8 @@ message SupervisorHello { uint64 connection_epoch = 3; // The supervisor can report credential, policy, and launch-environment installation. bool supports_provider_readiness = 4; + // Capabilities this supervisor supports (e.g. "otel_export"). + repeated string capabilities = 5; } // Gateway accepts the supervisor session. @@ -3009,6 +3032,8 @@ message SessionAccepted { reserved "heartbeat_interval_secs"; // Gateway-assigned session ID for this connection. string session_id = 1; + // Capabilities confirmed by the gateway (subset of what supervisor advertised). + repeated string capabilities = 3; // Recommended heartbeat interval. google.protobuf.Duration heartbeat_interval = 102; } diff --git a/sdk/go/proto/openshellv1/openshell.pb.go b/sdk/go/proto/openshellv1/openshell.pb.go index 109d2ef454..ecd69aaf43 100644 --- a/sdk/go/proto/openshellv1/openshell.pb.go +++ b/sdk/go/proto/openshellv1/openshell.pb.go @@ -13130,6 +13130,97 @@ func (x *GetSandboxLogsResponse) GetBufferTotal() uint32 { return 0 } +// OpenTelemetry export data forwarded from supervisor to gateway. +// Contains agent trace spans and/or OCSF events from a single sandbox. +type OtelExportData struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Source sandbox identifier. + SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` + // The OTLP signal being forwarded. Each variant carries the serialized + // OTLP Export*ServiceRequest in protobuf encoding, pre-enriched with + // sandbox resource attributes. + // + // Types that are valid to be assigned to Signal: + // + // *OtelExportData_TraceData + Signal isOtelExportData_Signal `protobuf_oneof:"signal"` + // Serialized OCSF events (each entry is a JSON-encoded OCSF event). + // Empty when only OTLP signal data is being forwarded. + OcsfEvents [][]byte `protobuf:"bytes,3,rep,name=ocsf_events,json=ocsfEvents,proto3" json:"ocsf_events,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *OtelExportData) Reset() { + *x = OtelExportData{} + mi := &file_openshell_proto_msgTypes[164] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *OtelExportData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OtelExportData) ProtoMessage() {} + +func (x *OtelExportData) ProtoReflect() protoreflect.Message { + mi := &file_openshell_proto_msgTypes[164] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OtelExportData.ProtoReflect.Descriptor instead. +func (*OtelExportData) Descriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{164} +} + +func (x *OtelExportData) GetSandboxId() string { + if x != nil { + return x.SandboxId + } + return "" +} + +func (x *OtelExportData) GetSignal() isOtelExportData_Signal { + if x != nil { + return x.Signal + } + return nil +} + +func (x *OtelExportData) GetTraceData() []byte { + if x != nil { + if x, ok := x.Signal.(*OtelExportData_TraceData); ok { + return x.TraceData + } + } + return nil +} + +func (x *OtelExportData) GetOcsfEvents() [][]byte { + if x != nil { + return x.OcsfEvents + } + return nil +} + +type isOtelExportData_Signal interface { + isOtelExportData_Signal() +} + +type OtelExportData_TraceData struct { + TraceData []byte `protobuf:"bytes,2,opt,name=trace_data,json=traceData,proto3,oneof"` +} + +func (*OtelExportData_TraceData) isOtelExportData_Signal() {} + // Envelope for supervisor-to-gateway messages on the ConnectSupervisor stream. type SupervisorMessage struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -13139,6 +13230,7 @@ type SupervisorMessage struct { // *SupervisorMessage_Heartbeat // *SupervisorMessage_RelayOpenResult // *SupervisorMessage_RelayClose + // *SupervisorMessage_OtelExport Payload isSupervisorMessage_Payload `protobuf_oneof:"payload"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -13146,7 +13238,7 @@ type SupervisorMessage struct { func (x *SupervisorMessage) Reset() { *x = SupervisorMessage{} - mi := &file_openshell_proto_msgTypes[164] + mi := &file_openshell_proto_msgTypes[165] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13158,7 +13250,7 @@ func (x *SupervisorMessage) String() string { func (*SupervisorMessage) ProtoMessage() {} func (x *SupervisorMessage) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[164] + mi := &file_openshell_proto_msgTypes[165] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13171,7 +13263,7 @@ func (x *SupervisorMessage) ProtoReflect() protoreflect.Message { // Deprecated: Use SupervisorMessage.ProtoReflect.Descriptor instead. func (*SupervisorMessage) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{164} + return file_openshell_proto_rawDescGZIP(), []int{165} } func (x *SupervisorMessage) GetPayload() isSupervisorMessage_Payload { @@ -13217,6 +13309,15 @@ func (x *SupervisorMessage) GetRelayClose() *RelayClose { return nil } +func (x *SupervisorMessage) GetOtelExport() *OtelExportData { + if x != nil { + if x, ok := x.Payload.(*SupervisorMessage_OtelExport); ok { + return x.OtelExport + } + } + return nil +} + type isSupervisorMessage_Payload interface { isSupervisorMessage_Payload() } @@ -13237,6 +13338,10 @@ type SupervisorMessage_RelayClose struct { RelayClose *RelayClose `protobuf:"bytes,4,opt,name=relay_close,json=relayClose,proto3,oneof"` } +type SupervisorMessage_OtelExport struct { + OtelExport *OtelExportData `protobuf:"bytes,5,opt,name=otel_export,json=otelExport,proto3,oneof"` +} + func (*SupervisorMessage_Hello) isSupervisorMessage_Payload() {} func (*SupervisorMessage_Heartbeat) isSupervisorMessage_Payload() {} @@ -13245,6 +13350,8 @@ func (*SupervisorMessage_RelayOpenResult) isSupervisorMessage_Payload() {} func (*SupervisorMessage_RelayClose) isSupervisorMessage_Payload() {} +func (*SupervisorMessage_OtelExport) isSupervisorMessage_Payload() {} + // Envelope for gateway-to-supervisor messages on the ConnectSupervisor stream. type GatewayMessage struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -13262,7 +13369,7 @@ type GatewayMessage struct { func (x *GatewayMessage) Reset() { *x = GatewayMessage{} - mi := &file_openshell_proto_msgTypes[165] + mi := &file_openshell_proto_msgTypes[166] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13274,7 +13381,7 @@ func (x *GatewayMessage) String() string { func (*GatewayMessage) ProtoMessage() {} func (x *GatewayMessage) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[165] + mi := &file_openshell_proto_msgTypes[166] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13287,7 +13394,7 @@ func (x *GatewayMessage) ProtoReflect() protoreflect.Message { // Deprecated: Use GatewayMessage.ProtoReflect.Descriptor instead. func (*GatewayMessage) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{165} + return file_openshell_proto_rawDescGZIP(), []int{166} } func (x *GatewayMessage) GetPayload() isGatewayMessage_Payload { @@ -13388,13 +13495,15 @@ type SupervisorHello struct { ConnectionEpoch uint64 `protobuf:"varint,3,opt,name=connection_epoch,json=connectionEpoch,proto3" json:"connection_epoch,omitempty"` // The supervisor can report credential, policy, and launch-environment installation. SupportsProviderReadiness bool `protobuf:"varint,4,opt,name=supports_provider_readiness,json=supportsProviderReadiness,proto3" json:"supports_provider_readiness,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Capabilities this supervisor supports (e.g. "otel_export"). + Capabilities []string `protobuf:"bytes,5,rep,name=capabilities,proto3" json:"capabilities,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SupervisorHello) Reset() { *x = SupervisorHello{} - mi := &file_openshell_proto_msgTypes[166] + mi := &file_openshell_proto_msgTypes[167] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13406,7 +13515,7 @@ func (x *SupervisorHello) String() string { func (*SupervisorHello) ProtoMessage() {} func (x *SupervisorHello) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[166] + mi := &file_openshell_proto_msgTypes[167] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13419,7 +13528,7 @@ func (x *SupervisorHello) ProtoReflect() protoreflect.Message { // Deprecated: Use SupervisorHello.ProtoReflect.Descriptor instead. func (*SupervisorHello) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{166} + return file_openshell_proto_rawDescGZIP(), []int{167} } func (x *SupervisorHello) GetSandboxId() string { @@ -13450,11 +13559,20 @@ func (x *SupervisorHello) GetSupportsProviderReadiness() bool { return false } +func (x *SupervisorHello) GetCapabilities() []string { + if x != nil { + return x.Capabilities + } + return nil +} + // Gateway accepts the supervisor session. type SessionAccepted struct { state protoimpl.MessageState `protogen:"open.v1"` // Gateway-assigned session ID for this connection. SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` + // Capabilities confirmed by the gateway (subset of what supervisor advertised). + Capabilities []string `protobuf:"bytes,3,rep,name=capabilities,proto3" json:"capabilities,omitempty"` // Recommended heartbeat interval. HeartbeatInterval *durationpb.Duration `protobuf:"bytes,102,opt,name=heartbeat_interval,json=heartbeatInterval,proto3" json:"heartbeat_interval,omitempty"` unknownFields protoimpl.UnknownFields @@ -13463,7 +13581,7 @@ type SessionAccepted struct { func (x *SessionAccepted) Reset() { *x = SessionAccepted{} - mi := &file_openshell_proto_msgTypes[167] + mi := &file_openshell_proto_msgTypes[168] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13475,7 +13593,7 @@ func (x *SessionAccepted) String() string { func (*SessionAccepted) ProtoMessage() {} func (x *SessionAccepted) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[167] + mi := &file_openshell_proto_msgTypes[168] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13488,7 +13606,7 @@ func (x *SessionAccepted) ProtoReflect() protoreflect.Message { // Deprecated: Use SessionAccepted.ProtoReflect.Descriptor instead. func (*SessionAccepted) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{167} + return file_openshell_proto_rawDescGZIP(), []int{168} } func (x *SessionAccepted) GetSessionId() string { @@ -13498,6 +13616,13 @@ func (x *SessionAccepted) GetSessionId() string { return "" } +func (x *SessionAccepted) GetCapabilities() []string { + if x != nil { + return x.Capabilities + } + return nil +} + func (x *SessionAccepted) GetHeartbeatInterval() *durationpb.Duration { if x != nil { return x.HeartbeatInterval @@ -13516,7 +13641,7 @@ type SessionRejected struct { func (x *SessionRejected) Reset() { *x = SessionRejected{} - mi := &file_openshell_proto_msgTypes[168] + mi := &file_openshell_proto_msgTypes[169] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13528,7 +13653,7 @@ func (x *SessionRejected) String() string { func (*SessionRejected) ProtoMessage() {} func (x *SessionRejected) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[168] + mi := &file_openshell_proto_msgTypes[169] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13541,7 +13666,7 @@ func (x *SessionRejected) ProtoReflect() protoreflect.Message { // Deprecated: Use SessionRejected.ProtoReflect.Descriptor instead. func (*SessionRejected) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{168} + return file_openshell_proto_rawDescGZIP(), []int{169} } func (x *SessionRejected) GetReason() string { @@ -13560,7 +13685,7 @@ type SupervisorHeartbeat struct { func (x *SupervisorHeartbeat) Reset() { *x = SupervisorHeartbeat{} - mi := &file_openshell_proto_msgTypes[169] + mi := &file_openshell_proto_msgTypes[170] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13572,7 +13697,7 @@ func (x *SupervisorHeartbeat) String() string { func (*SupervisorHeartbeat) ProtoMessage() {} func (x *SupervisorHeartbeat) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[169] + mi := &file_openshell_proto_msgTypes[170] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13585,7 +13710,7 @@ func (x *SupervisorHeartbeat) ProtoReflect() protoreflect.Message { // Deprecated: Use SupervisorHeartbeat.ProtoReflect.Descriptor instead. func (*SupervisorHeartbeat) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{169} + return file_openshell_proto_rawDescGZIP(), []int{170} } // Gateway heartbeat. @@ -13597,7 +13722,7 @@ type GatewayHeartbeat struct { func (x *GatewayHeartbeat) Reset() { *x = GatewayHeartbeat{} - mi := &file_openshell_proto_msgTypes[170] + mi := &file_openshell_proto_msgTypes[171] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13609,7 +13734,7 @@ func (x *GatewayHeartbeat) String() string { func (*GatewayHeartbeat) ProtoMessage() {} func (x *GatewayHeartbeat) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[170] + mi := &file_openshell_proto_msgTypes[171] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13622,7 +13747,7 @@ func (x *GatewayHeartbeat) ProtoReflect() protoreflect.Message { // Deprecated: Use GatewayHeartbeat.ProtoReflect.Descriptor instead. func (*GatewayHeartbeat) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{170} + return file_openshell_proto_rawDescGZIP(), []int{171} } // Terminal result reported before the supervisor shuts down. A successful RPC @@ -13639,7 +13764,7 @@ type ReportMainProcessExitRequest struct { func (x *ReportMainProcessExitRequest) Reset() { *x = ReportMainProcessExitRequest{} - mi := &file_openshell_proto_msgTypes[171] + mi := &file_openshell_proto_msgTypes[172] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13651,7 +13776,7 @@ func (x *ReportMainProcessExitRequest) String() string { func (*ReportMainProcessExitRequest) ProtoMessage() {} func (x *ReportMainProcessExitRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[171] + mi := &file_openshell_proto_msgTypes[172] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13664,7 +13789,7 @@ func (x *ReportMainProcessExitRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportMainProcessExitRequest.ProtoReflect.Descriptor instead. func (*ReportMainProcessExitRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{171} + return file_openshell_proto_rawDescGZIP(), []int{172} } func (x *ReportMainProcessExitRequest) GetSandboxId() string { @@ -13696,7 +13821,7 @@ type ReportMainProcessExitResponse struct { func (x *ReportMainProcessExitResponse) Reset() { *x = ReportMainProcessExitResponse{} - mi := &file_openshell_proto_msgTypes[172] + mi := &file_openshell_proto_msgTypes[173] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13708,7 +13833,7 @@ func (x *ReportMainProcessExitResponse) String() string { func (*ReportMainProcessExitResponse) ProtoMessage() {} func (x *ReportMainProcessExitResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[172] + mi := &file_openshell_proto_msgTypes[173] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13721,7 +13846,7 @@ func (x *ReportMainProcessExitResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportMainProcessExitResponse.ProtoReflect.Descriptor instead. func (*ReportMainProcessExitResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{172} + return file_openshell_proto_rawDescGZIP(), []int{173} } // Terminal-delivery completion reported after all expected foreground SSH @@ -13736,7 +13861,7 @@ type FinalizeMainProcessExitRequest struct { func (x *FinalizeMainProcessExitRequest) Reset() { *x = FinalizeMainProcessExitRequest{} - mi := &file_openshell_proto_msgTypes[173] + mi := &file_openshell_proto_msgTypes[174] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13748,7 +13873,7 @@ func (x *FinalizeMainProcessExitRequest) String() string { func (*FinalizeMainProcessExitRequest) ProtoMessage() {} func (x *FinalizeMainProcessExitRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[173] + mi := &file_openshell_proto_msgTypes[174] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13761,7 +13886,7 @@ func (x *FinalizeMainProcessExitRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use FinalizeMainProcessExitRequest.ProtoReflect.Descriptor instead. func (*FinalizeMainProcessExitRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{173} + return file_openshell_proto_rawDescGZIP(), []int{174} } func (x *FinalizeMainProcessExitRequest) GetSandboxId() string { @@ -13786,7 +13911,7 @@ type FinalizeMainProcessExitResponse struct { func (x *FinalizeMainProcessExitResponse) Reset() { *x = FinalizeMainProcessExitResponse{} - mi := &file_openshell_proto_msgTypes[174] + mi := &file_openshell_proto_msgTypes[175] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13798,7 +13923,7 @@ func (x *FinalizeMainProcessExitResponse) String() string { func (*FinalizeMainProcessExitResponse) ProtoMessage() {} func (x *FinalizeMainProcessExitResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[174] + mi := &file_openshell_proto_msgTypes[175] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13811,7 +13936,7 @@ func (x *FinalizeMainProcessExitResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use FinalizeMainProcessExitResponse.ProtoReflect.Descriptor instead. func (*FinalizeMainProcessExitResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{174} + return file_openshell_proto_rawDescGZIP(), []int{175} } // Gateway requests the supervisor to open a relay channel. @@ -13840,7 +13965,7 @@ type RelayOpen struct { func (x *RelayOpen) Reset() { *x = RelayOpen{} - mi := &file_openshell_proto_msgTypes[175] + mi := &file_openshell_proto_msgTypes[176] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13852,7 +13977,7 @@ func (x *RelayOpen) String() string { func (*RelayOpen) ProtoMessage() {} func (x *RelayOpen) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[175] + mi := &file_openshell_proto_msgTypes[176] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13865,7 +13990,7 @@ func (x *RelayOpen) ProtoReflect() protoreflect.Message { // Deprecated: Use RelayOpen.ProtoReflect.Descriptor instead. func (*RelayOpen) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{175} + return file_openshell_proto_rawDescGZIP(), []int{176} } func (x *RelayOpen) GetChannelId() string { @@ -13932,7 +14057,7 @@ type SshRelayTarget struct { func (x *SshRelayTarget) Reset() { *x = SshRelayTarget{} - mi := &file_openshell_proto_msgTypes[176] + mi := &file_openshell_proto_msgTypes[177] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13944,7 +14069,7 @@ func (x *SshRelayTarget) String() string { func (*SshRelayTarget) ProtoMessage() {} func (x *SshRelayTarget) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[176] + mi := &file_openshell_proto_msgTypes[177] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13957,7 +14082,7 @@ func (x *SshRelayTarget) ProtoReflect() protoreflect.Message { // Deprecated: Use SshRelayTarget.ProtoReflect.Descriptor instead. func (*SshRelayTarget) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{176} + return file_openshell_proto_rawDescGZIP(), []int{177} } // TCP target dialed by the supervisor from inside the sandbox. @@ -13973,7 +14098,7 @@ type TcpRelayTarget struct { func (x *TcpRelayTarget) Reset() { *x = TcpRelayTarget{} - mi := &file_openshell_proto_msgTypes[177] + mi := &file_openshell_proto_msgTypes[178] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13985,7 +14110,7 @@ func (x *TcpRelayTarget) String() string { func (*TcpRelayTarget) ProtoMessage() {} func (x *TcpRelayTarget) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[177] + mi := &file_openshell_proto_msgTypes[178] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13998,7 +14123,7 @@ func (x *TcpRelayTarget) ProtoReflect() protoreflect.Message { // Deprecated: Use TcpRelayTarget.ProtoReflect.Descriptor instead. func (*TcpRelayTarget) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{177} + return file_openshell_proto_rawDescGZIP(), []int{178} } func (x *TcpRelayTarget) GetHost() string { @@ -14026,7 +14151,7 @@ type RelayInit struct { func (x *RelayInit) Reset() { *x = RelayInit{} - mi := &file_openshell_proto_msgTypes[178] + mi := &file_openshell_proto_msgTypes[179] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14038,7 +14163,7 @@ func (x *RelayInit) String() string { func (*RelayInit) ProtoMessage() {} func (x *RelayInit) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[178] + mi := &file_openshell_proto_msgTypes[179] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14051,7 +14176,7 @@ func (x *RelayInit) ProtoReflect() protoreflect.Message { // Deprecated: Use RelayInit.ProtoReflect.Descriptor instead. func (*RelayInit) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{178} + return file_openshell_proto_rawDescGZIP(), []int{179} } func (x *RelayInit) GetChannelId() string { @@ -14078,7 +14203,7 @@ type RelayFrame struct { func (x *RelayFrame) Reset() { *x = RelayFrame{} - mi := &file_openshell_proto_msgTypes[179] + mi := &file_openshell_proto_msgTypes[180] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14090,7 +14215,7 @@ func (x *RelayFrame) String() string { func (*RelayFrame) ProtoMessage() {} func (x *RelayFrame) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[179] + mi := &file_openshell_proto_msgTypes[180] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14103,7 +14228,7 @@ func (x *RelayFrame) ProtoReflect() protoreflect.Message { // Deprecated: Use RelayFrame.ProtoReflect.Descriptor instead. func (*RelayFrame) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{179} + return file_openshell_proto_rawDescGZIP(), []int{180} } func (x *RelayFrame) GetPayload() isRelayFrame_Payload { @@ -14163,7 +14288,7 @@ type PeerRelayInit struct { func (x *PeerRelayInit) Reset() { *x = PeerRelayInit{} - mi := &file_openshell_proto_msgTypes[180] + mi := &file_openshell_proto_msgTypes[181] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14175,7 +14300,7 @@ func (x *PeerRelayInit) String() string { func (*PeerRelayInit) ProtoMessage() {} func (x *PeerRelayInit) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[180] + mi := &file_openshell_proto_msgTypes[181] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14188,7 +14313,7 @@ func (x *PeerRelayInit) ProtoReflect() protoreflect.Message { // Deprecated: Use PeerRelayInit.ProtoReflect.Descriptor instead. func (*PeerRelayInit) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{180} + return file_openshell_proto_rawDescGZIP(), []int{181} } func (x *PeerRelayInit) GetSandboxId() string { @@ -14226,7 +14351,7 @@ type PeerRelayFrame struct { func (x *PeerRelayFrame) Reset() { *x = PeerRelayFrame{} - mi := &file_openshell_proto_msgTypes[181] + mi := &file_openshell_proto_msgTypes[182] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14238,7 +14363,7 @@ func (x *PeerRelayFrame) String() string { func (*PeerRelayFrame) ProtoMessage() {} func (x *PeerRelayFrame) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[181] + mi := &file_openshell_proto_msgTypes[182] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14251,7 +14376,7 @@ func (x *PeerRelayFrame) ProtoReflect() protoreflect.Message { // Deprecated: Use PeerRelayFrame.ProtoReflect.Descriptor instead. func (*PeerRelayFrame) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{181} + return file_openshell_proto_rawDescGZIP(), []int{182} } func (x *PeerRelayFrame) GetPayload() isPeerRelayFrame_Payload { @@ -14310,7 +14435,7 @@ type RelayOpenResult struct { func (x *RelayOpenResult) Reset() { *x = RelayOpenResult{} - mi := &file_openshell_proto_msgTypes[182] + mi := &file_openshell_proto_msgTypes[183] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14322,7 +14447,7 @@ func (x *RelayOpenResult) String() string { func (*RelayOpenResult) ProtoMessage() {} func (x *RelayOpenResult) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[182] + mi := &file_openshell_proto_msgTypes[183] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14335,7 +14460,7 @@ func (x *RelayOpenResult) ProtoReflect() protoreflect.Message { // Deprecated: Use RelayOpenResult.ProtoReflect.Descriptor instead. func (*RelayOpenResult) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{182} + return file_openshell_proto_rawDescGZIP(), []int{183} } func (x *RelayOpenResult) GetChannelId() string { @@ -14372,7 +14497,7 @@ type RelayClose struct { func (x *RelayClose) Reset() { *x = RelayClose{} - mi := &file_openshell_proto_msgTypes[183] + mi := &file_openshell_proto_msgTypes[184] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14384,7 +14509,7 @@ func (x *RelayClose) String() string { func (*RelayClose) ProtoMessage() {} func (x *RelayClose) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[183] + mi := &file_openshell_proto_msgTypes[184] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14397,7 +14522,7 @@ func (x *RelayClose) ProtoReflect() protoreflect.Message { // Deprecated: Use RelayClose.ProtoReflect.Descriptor instead. func (*RelayClose) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{183} + return file_openshell_proto_rawDescGZIP(), []int{184} } func (x *RelayClose) GetChannelId() string { @@ -14431,7 +14556,7 @@ type L7RequestSample struct { func (x *L7RequestSample) Reset() { *x = L7RequestSample{} - mi := &file_openshell_proto_msgTypes[184] + mi := &file_openshell_proto_msgTypes[185] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14443,7 +14568,7 @@ func (x *L7RequestSample) String() string { func (*L7RequestSample) ProtoMessage() {} func (x *L7RequestSample) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[184] + mi := &file_openshell_proto_msgTypes[185] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14456,7 +14581,7 @@ func (x *L7RequestSample) ProtoReflect() protoreflect.Message { // Deprecated: Use L7RequestSample.ProtoReflect.Descriptor instead. func (*L7RequestSample) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{184} + return file_openshell_proto_rawDescGZIP(), []int{185} } func (x *L7RequestSample) GetMethod() string { @@ -14530,7 +14655,7 @@ type DenialSummary struct { func (x *DenialSummary) Reset() { *x = DenialSummary{} - mi := &file_openshell_proto_msgTypes[185] + mi := &file_openshell_proto_msgTypes[186] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14542,7 +14667,7 @@ func (x *DenialSummary) String() string { func (*DenialSummary) ProtoMessage() {} func (x *DenialSummary) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[185] + mi := &file_openshell_proto_msgTypes[186] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14555,7 +14680,7 @@ func (x *DenialSummary) ProtoReflect() protoreflect.Message { // Deprecated: Use DenialSummary.ProtoReflect.Descriptor instead. func (*DenialSummary) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{185} + return file_openshell_proto_rawDescGZIP(), []int{186} } func (x *DenialSummary) GetSandboxId() string { @@ -14690,7 +14815,7 @@ type DenialGroupCount struct { func (x *DenialGroupCount) Reset() { *x = DenialGroupCount{} - mi := &file_openshell_proto_msgTypes[186] + mi := &file_openshell_proto_msgTypes[187] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14702,7 +14827,7 @@ func (x *DenialGroupCount) String() string { func (*DenialGroupCount) ProtoMessage() {} func (x *DenialGroupCount) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[186] + mi := &file_openshell_proto_msgTypes[187] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14715,7 +14840,7 @@ func (x *DenialGroupCount) ProtoReflect() protoreflect.Message { // Deprecated: Use DenialGroupCount.ProtoReflect.Descriptor instead. func (*DenialGroupCount) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{186} + return file_openshell_proto_rawDescGZIP(), []int{187} } func (x *DenialGroupCount) GetDenyGroup() string { @@ -14748,7 +14873,7 @@ type NetworkActivitySummary struct { func (x *NetworkActivitySummary) Reset() { *x = NetworkActivitySummary{} - mi := &file_openshell_proto_msgTypes[187] + mi := &file_openshell_proto_msgTypes[188] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14760,7 +14885,7 @@ func (x *NetworkActivitySummary) String() string { func (*NetworkActivitySummary) ProtoMessage() {} func (x *NetworkActivitySummary) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[187] + mi := &file_openshell_proto_msgTypes[188] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14773,7 +14898,7 @@ func (x *NetworkActivitySummary) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkActivitySummary.ProtoReflect.Descriptor instead. func (*NetworkActivitySummary) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{187} + return file_openshell_proto_rawDescGZIP(), []int{188} } func (x *NetworkActivitySummary) GetNetworkActivityCount() uint32 { @@ -14861,7 +14986,7 @@ type PolicyChunk struct { func (x *PolicyChunk) Reset() { *x = PolicyChunk{} - mi := &file_openshell_proto_msgTypes[188] + mi := &file_openshell_proto_msgTypes[189] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14873,7 +14998,7 @@ func (x *PolicyChunk) String() string { func (*PolicyChunk) ProtoMessage() {} func (x *PolicyChunk) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[188] + mi := &file_openshell_proto_msgTypes[189] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14886,7 +15011,7 @@ func (x *PolicyChunk) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyChunk.ProtoReflect.Descriptor instead. func (*PolicyChunk) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{188} + return file_openshell_proto_rawDescGZIP(), []int{189} } func (x *PolicyChunk) GetId() string { @@ -15074,7 +15199,7 @@ type DraftPolicyUpdate struct { func (x *DraftPolicyUpdate) Reset() { *x = DraftPolicyUpdate{} - mi := &file_openshell_proto_msgTypes[189] + mi := &file_openshell_proto_msgTypes[190] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15086,7 +15211,7 @@ func (x *DraftPolicyUpdate) String() string { func (*DraftPolicyUpdate) ProtoMessage() {} func (x *DraftPolicyUpdate) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[189] + mi := &file_openshell_proto_msgTypes[190] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15099,7 +15224,7 @@ func (x *DraftPolicyUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use DraftPolicyUpdate.ProtoReflect.Descriptor instead. func (*DraftPolicyUpdate) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{189} + return file_openshell_proto_rawDescGZIP(), []int{190} } func (x *DraftPolicyUpdate) GetDraftVersion() uint64 { @@ -15158,7 +15283,7 @@ type SubmitPolicyAnalysisRequest struct { func (x *SubmitPolicyAnalysisRequest) Reset() { *x = SubmitPolicyAnalysisRequest{} - mi := &file_openshell_proto_msgTypes[190] + mi := &file_openshell_proto_msgTypes[191] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15170,7 +15295,7 @@ func (x *SubmitPolicyAnalysisRequest) String() string { func (*SubmitPolicyAnalysisRequest) ProtoMessage() {} func (x *SubmitPolicyAnalysisRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[190] + mi := &file_openshell_proto_msgTypes[191] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15183,7 +15308,7 @@ func (x *SubmitPolicyAnalysisRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SubmitPolicyAnalysisRequest.ProtoReflect.Descriptor instead. func (*SubmitPolicyAnalysisRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{190} + return file_openshell_proto_rawDescGZIP(), []int{191} } func (x *SubmitPolicyAnalysisRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { @@ -15246,7 +15371,7 @@ type SubmitPolicyAnalysisResponse struct { func (x *SubmitPolicyAnalysisResponse) Reset() { *x = SubmitPolicyAnalysisResponse{} - mi := &file_openshell_proto_msgTypes[191] + mi := &file_openshell_proto_msgTypes[192] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15258,7 +15383,7 @@ func (x *SubmitPolicyAnalysisResponse) String() string { func (*SubmitPolicyAnalysisResponse) ProtoMessage() {} func (x *SubmitPolicyAnalysisResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[191] + mi := &file_openshell_proto_msgTypes[192] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15271,7 +15396,7 @@ func (x *SubmitPolicyAnalysisResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SubmitPolicyAnalysisResponse.ProtoReflect.Descriptor instead. func (*SubmitPolicyAnalysisResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{191} + return file_openshell_proto_rawDescGZIP(), []int{192} } func (x *SubmitPolicyAnalysisResponse) GetAcceptedChunks() uint32 { @@ -15316,7 +15441,7 @@ type GetDraftPolicyRequest struct { func (x *GetDraftPolicyRequest) Reset() { *x = GetDraftPolicyRequest{} - mi := &file_openshell_proto_msgTypes[192] + mi := &file_openshell_proto_msgTypes[193] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15328,7 +15453,7 @@ func (x *GetDraftPolicyRequest) String() string { func (*GetDraftPolicyRequest) ProtoMessage() {} func (x *GetDraftPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[192] + mi := &file_openshell_proto_msgTypes[193] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15341,7 +15466,7 @@ func (x *GetDraftPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDraftPolicyRequest.ProtoReflect.Descriptor instead. func (*GetDraftPolicyRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{192} + return file_openshell_proto_rawDescGZIP(), []int{193} } func (x *GetDraftPolicyRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { @@ -15381,7 +15506,7 @@ type GetDraftPolicyResponse struct { func (x *GetDraftPolicyResponse) Reset() { *x = GetDraftPolicyResponse{} - mi := &file_openshell_proto_msgTypes[193] + mi := &file_openshell_proto_msgTypes[194] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15393,7 +15518,7 @@ func (x *GetDraftPolicyResponse) String() string { func (*GetDraftPolicyResponse) ProtoMessage() {} func (x *GetDraftPolicyResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[193] + mi := &file_openshell_proto_msgTypes[194] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15406,7 +15531,7 @@ func (x *GetDraftPolicyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDraftPolicyResponse.ProtoReflect.Descriptor instead. func (*GetDraftPolicyResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{193} + return file_openshell_proto_rawDescGZIP(), []int{194} } func (x *GetDraftPolicyResponse) GetChunks() []*PolicyChunk { @@ -15457,7 +15582,7 @@ type ApproveDraftChunkRequest struct { func (x *ApproveDraftChunkRequest) Reset() { *x = ApproveDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[194] + mi := &file_openshell_proto_msgTypes[195] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15469,7 +15594,7 @@ func (x *ApproveDraftChunkRequest) String() string { func (*ApproveDraftChunkRequest) ProtoMessage() {} func (x *ApproveDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[194] + mi := &file_openshell_proto_msgTypes[195] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15482,7 +15607,7 @@ func (x *ApproveDraftChunkRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ApproveDraftChunkRequest.ProtoReflect.Descriptor instead. func (*ApproveDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{194} + return file_openshell_proto_rawDescGZIP(), []int{195} } func (x *ApproveDraftChunkRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { @@ -15532,7 +15657,7 @@ type ApproveDraftChunkResponse struct { func (x *ApproveDraftChunkResponse) Reset() { *x = ApproveDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[195] + mi := &file_openshell_proto_msgTypes[196] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15544,7 +15669,7 @@ func (x *ApproveDraftChunkResponse) String() string { func (*ApproveDraftChunkResponse) ProtoMessage() {} func (x *ApproveDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[195] + mi := &file_openshell_proto_msgTypes[196] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15557,7 +15682,7 @@ func (x *ApproveDraftChunkResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ApproveDraftChunkResponse.ProtoReflect.Descriptor instead. func (*ApproveDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{195} + return file_openshell_proto_rawDescGZIP(), []int{196} } func (x *ApproveDraftChunkResponse) GetPolicyVersion() uint32 { @@ -15593,7 +15718,7 @@ type RejectDraftChunkRequest struct { func (x *RejectDraftChunkRequest) Reset() { *x = RejectDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[196] + mi := &file_openshell_proto_msgTypes[197] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15605,7 +15730,7 @@ func (x *RejectDraftChunkRequest) String() string { func (*RejectDraftChunkRequest) ProtoMessage() {} func (x *RejectDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[196] + mi := &file_openshell_proto_msgTypes[197] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15618,7 +15743,7 @@ func (x *RejectDraftChunkRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RejectDraftChunkRequest.ProtoReflect.Descriptor instead. func (*RejectDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{196} + return file_openshell_proto_rawDescGZIP(), []int{197} } func (x *RejectDraftChunkRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { @@ -15664,7 +15789,7 @@ type RejectDraftChunkResponse struct { func (x *RejectDraftChunkResponse) Reset() { *x = RejectDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[197] + mi := &file_openshell_proto_msgTypes[198] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15676,7 +15801,7 @@ func (x *RejectDraftChunkResponse) String() string { func (*RejectDraftChunkResponse) ProtoMessage() {} func (x *RejectDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[197] + mi := &file_openshell_proto_msgTypes[198] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15689,7 +15814,7 @@ func (x *RejectDraftChunkResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RejectDraftChunkResponse.ProtoReflect.Descriptor instead. func (*RejectDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{197} + return file_openshell_proto_rawDescGZIP(), []int{198} } // Approve all pending chunks. @@ -15703,7 +15828,7 @@ type DraftChunkApproval struct { func (x *DraftChunkApproval) Reset() { *x = DraftChunkApproval{} - mi := &file_openshell_proto_msgTypes[198] + mi := &file_openshell_proto_msgTypes[199] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15715,7 +15840,7 @@ func (x *DraftChunkApproval) String() string { func (*DraftChunkApproval) ProtoMessage() {} func (x *DraftChunkApproval) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[198] + mi := &file_openshell_proto_msgTypes[199] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15728,7 +15853,7 @@ func (x *DraftChunkApproval) ProtoReflect() protoreflect.Message { // Deprecated: Use DraftChunkApproval.ProtoReflect.Descriptor instead. func (*DraftChunkApproval) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{198} + return file_openshell_proto_rawDescGZIP(), []int{199} } func (x *DraftChunkApproval) GetChunkId() string { @@ -15764,7 +15889,7 @@ type ApproveAllDraftChunksRequest struct { func (x *ApproveAllDraftChunksRequest) Reset() { *x = ApproveAllDraftChunksRequest{} - mi := &file_openshell_proto_msgTypes[199] + mi := &file_openshell_proto_msgTypes[200] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15776,7 +15901,7 @@ func (x *ApproveAllDraftChunksRequest) String() string { func (*ApproveAllDraftChunksRequest) ProtoMessage() {} func (x *ApproveAllDraftChunksRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[199] + mi := &file_openshell_proto_msgTypes[200] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15789,7 +15914,7 @@ func (x *ApproveAllDraftChunksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ApproveAllDraftChunksRequest.ProtoReflect.Descriptor instead. func (*ApproveAllDraftChunksRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{199} + return file_openshell_proto_rawDescGZIP(), []int{200} } func (x *ApproveAllDraftChunksRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { @@ -15844,7 +15969,7 @@ type ApproveAllDraftChunksResponse struct { func (x *ApproveAllDraftChunksResponse) Reset() { *x = ApproveAllDraftChunksResponse{} - mi := &file_openshell_proto_msgTypes[200] + mi := &file_openshell_proto_msgTypes[201] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15856,7 +15981,7 @@ func (x *ApproveAllDraftChunksResponse) String() string { func (*ApproveAllDraftChunksResponse) ProtoMessage() {} func (x *ApproveAllDraftChunksResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[200] + mi := &file_openshell_proto_msgTypes[201] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15869,7 +15994,7 @@ func (x *ApproveAllDraftChunksResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ApproveAllDraftChunksResponse.ProtoReflect.Descriptor instead. func (*ApproveAllDraftChunksResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{200} + return file_openshell_proto_rawDescGZIP(), []int{201} } func (x *ApproveAllDraftChunksResponse) GetPolicyVersion() uint32 { @@ -15919,7 +16044,7 @@ type EditDraftChunkRequest struct { func (x *EditDraftChunkRequest) Reset() { *x = EditDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[201] + mi := &file_openshell_proto_msgTypes[202] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15931,7 +16056,7 @@ func (x *EditDraftChunkRequest) String() string { func (*EditDraftChunkRequest) ProtoMessage() {} func (x *EditDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[201] + mi := &file_openshell_proto_msgTypes[202] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15944,7 +16069,7 @@ func (x *EditDraftChunkRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use EditDraftChunkRequest.ProtoReflect.Descriptor instead. func (*EditDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{201} + return file_openshell_proto_rawDescGZIP(), []int{202} } func (x *EditDraftChunkRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { @@ -15990,7 +16115,7 @@ type EditDraftChunkResponse struct { func (x *EditDraftChunkResponse) Reset() { *x = EditDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[202] + mi := &file_openshell_proto_msgTypes[203] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16002,7 +16127,7 @@ func (x *EditDraftChunkResponse) String() string { func (*EditDraftChunkResponse) ProtoMessage() {} func (x *EditDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[202] + mi := &file_openshell_proto_msgTypes[203] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16015,7 +16140,7 @@ func (x *EditDraftChunkResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use EditDraftChunkResponse.ProtoReflect.Descriptor instead. func (*EditDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{202} + return file_openshell_proto_rawDescGZIP(), []int{203} } // Reverse an approval (remove merged rule from active policy). @@ -16035,7 +16160,7 @@ type UndoDraftChunkRequest struct { func (x *UndoDraftChunkRequest) Reset() { *x = UndoDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[203] + mi := &file_openshell_proto_msgTypes[204] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16047,7 +16172,7 @@ func (x *UndoDraftChunkRequest) String() string { func (*UndoDraftChunkRequest) ProtoMessage() {} func (x *UndoDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[203] + mi := &file_openshell_proto_msgTypes[204] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16060,7 +16185,7 @@ func (x *UndoDraftChunkRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UndoDraftChunkRequest.ProtoReflect.Descriptor instead. func (*UndoDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{203} + return file_openshell_proto_rawDescGZIP(), []int{204} } func (x *UndoDraftChunkRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { @@ -16103,7 +16228,7 @@ type UndoDraftChunkResponse struct { func (x *UndoDraftChunkResponse) Reset() { *x = UndoDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[204] + mi := &file_openshell_proto_msgTypes[205] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16115,7 +16240,7 @@ func (x *UndoDraftChunkResponse) String() string { func (*UndoDraftChunkResponse) ProtoMessage() {} func (x *UndoDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[204] + mi := &file_openshell_proto_msgTypes[205] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16128,7 +16253,7 @@ func (x *UndoDraftChunkResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UndoDraftChunkResponse.ProtoReflect.Descriptor instead. func (*UndoDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{204} + return file_openshell_proto_rawDescGZIP(), []int{205} } func (x *UndoDraftChunkResponse) GetPolicyVersion() uint32 { @@ -16160,7 +16285,7 @@ type ClearDraftChunksRequest struct { func (x *ClearDraftChunksRequest) Reset() { *x = ClearDraftChunksRequest{} - mi := &file_openshell_proto_msgTypes[205] + mi := &file_openshell_proto_msgTypes[206] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16172,7 +16297,7 @@ func (x *ClearDraftChunksRequest) String() string { func (*ClearDraftChunksRequest) ProtoMessage() {} func (x *ClearDraftChunksRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[205] + mi := &file_openshell_proto_msgTypes[206] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16185,7 +16310,7 @@ func (x *ClearDraftChunksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ClearDraftChunksRequest.ProtoReflect.Descriptor instead. func (*ClearDraftChunksRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{205} + return file_openshell_proto_rawDescGZIP(), []int{206} } func (x *ClearDraftChunksRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { @@ -16219,7 +16344,7 @@ type ClearDraftChunksResponse struct { func (x *ClearDraftChunksResponse) Reset() { *x = ClearDraftChunksResponse{} - mi := &file_openshell_proto_msgTypes[206] + mi := &file_openshell_proto_msgTypes[207] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16231,7 +16356,7 @@ func (x *ClearDraftChunksResponse) String() string { func (*ClearDraftChunksResponse) ProtoMessage() {} func (x *ClearDraftChunksResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[206] + mi := &file_openshell_proto_msgTypes[207] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16244,7 +16369,7 @@ func (x *ClearDraftChunksResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ClearDraftChunksResponse.ProtoReflect.Descriptor instead. func (*ClearDraftChunksResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{206} + return file_openshell_proto_rawDescGZIP(), []int{207} } func (x *ClearDraftChunksResponse) GetChunksCleared() uint32 { @@ -16266,7 +16391,7 @@ type GetDraftHistoryRequest struct { func (x *GetDraftHistoryRequest) Reset() { *x = GetDraftHistoryRequest{} - mi := &file_openshell_proto_msgTypes[207] + mi := &file_openshell_proto_msgTypes[208] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16278,7 +16403,7 @@ func (x *GetDraftHistoryRequest) String() string { func (*GetDraftHistoryRequest) ProtoMessage() {} func (x *GetDraftHistoryRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[207] + mi := &file_openshell_proto_msgTypes[208] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16291,7 +16416,7 @@ func (x *GetDraftHistoryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDraftHistoryRequest.ProtoReflect.Descriptor instead. func (*GetDraftHistoryRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{207} + return file_openshell_proto_rawDescGZIP(), []int{208} } func (x *GetDraftHistoryRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { @@ -16325,7 +16450,7 @@ type DraftHistoryEntry struct { func (x *DraftHistoryEntry) Reset() { *x = DraftHistoryEntry{} - mi := &file_openshell_proto_msgTypes[208] + mi := &file_openshell_proto_msgTypes[209] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16337,7 +16462,7 @@ func (x *DraftHistoryEntry) String() string { func (*DraftHistoryEntry) ProtoMessage() {} func (x *DraftHistoryEntry) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[208] + mi := &file_openshell_proto_msgTypes[209] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16350,7 +16475,7 @@ func (x *DraftHistoryEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use DraftHistoryEntry.ProtoReflect.Descriptor instead. func (*DraftHistoryEntry) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{208} + return file_openshell_proto_rawDescGZIP(), []int{209} } func (x *DraftHistoryEntry) GetEventTime() *timestamppb.Timestamp { @@ -16391,7 +16516,7 @@ type GetDraftHistoryResponse struct { func (x *GetDraftHistoryResponse) Reset() { *x = GetDraftHistoryResponse{} - mi := &file_openshell_proto_msgTypes[209] + mi := &file_openshell_proto_msgTypes[210] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16403,7 +16528,7 @@ func (x *GetDraftHistoryResponse) String() string { func (*GetDraftHistoryResponse) ProtoMessage() {} func (x *GetDraftHistoryResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[209] + mi := &file_openshell_proto_msgTypes[210] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16416,7 +16541,7 @@ func (x *GetDraftHistoryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDraftHistoryResponse.ProtoReflect.Descriptor instead. func (*GetDraftHistoryResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{209} + return file_openshell_proto_rawDescGZIP(), []int{210} } func (x *GetDraftHistoryResponse) GetEntries() []*DraftHistoryEntry { @@ -16441,7 +16566,7 @@ type CreateWorkspaceRequest struct { func (x *CreateWorkspaceRequest) Reset() { *x = CreateWorkspaceRequest{} - mi := &file_openshell_proto_msgTypes[210] + mi := &file_openshell_proto_msgTypes[211] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16453,7 +16578,7 @@ func (x *CreateWorkspaceRequest) String() string { func (*CreateWorkspaceRequest) ProtoMessage() {} func (x *CreateWorkspaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[210] + mi := &file_openshell_proto_msgTypes[211] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16466,7 +16591,7 @@ func (x *CreateWorkspaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateWorkspaceRequest.ProtoReflect.Descriptor instead. func (*CreateWorkspaceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{210} + return file_openshell_proto_rawDescGZIP(), []int{211} } func (x *CreateWorkspaceRequest) GetName() string { @@ -16500,7 +16625,7 @@ type CreateWorkspaceResponse struct { func (x *CreateWorkspaceResponse) Reset() { *x = CreateWorkspaceResponse{} - mi := &file_openshell_proto_msgTypes[211] + mi := &file_openshell_proto_msgTypes[212] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16512,7 +16637,7 @@ func (x *CreateWorkspaceResponse) String() string { func (*CreateWorkspaceResponse) ProtoMessage() {} func (x *CreateWorkspaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[211] + mi := &file_openshell_proto_msgTypes[212] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16525,7 +16650,7 @@ func (x *CreateWorkspaceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateWorkspaceResponse.ProtoReflect.Descriptor instead. func (*CreateWorkspaceResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{211} + return file_openshell_proto_rawDescGZIP(), []int{212} } func (x *CreateWorkspaceResponse) GetWorkspace() *datamodelv1.Workspace { @@ -16546,7 +16671,7 @@ type GetWorkspaceRequest struct { func (x *GetWorkspaceRequest) Reset() { *x = GetWorkspaceRequest{} - mi := &file_openshell_proto_msgTypes[212] + mi := &file_openshell_proto_msgTypes[213] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16558,7 +16683,7 @@ func (x *GetWorkspaceRequest) String() string { func (*GetWorkspaceRequest) ProtoMessage() {} func (x *GetWorkspaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[212] + mi := &file_openshell_proto_msgTypes[213] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16571,7 +16696,7 @@ func (x *GetWorkspaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetWorkspaceRequest.ProtoReflect.Descriptor instead. func (*GetWorkspaceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{212} + return file_openshell_proto_rawDescGZIP(), []int{213} } func (x *GetWorkspaceRequest) GetName() string { @@ -16591,7 +16716,7 @@ type GetWorkspaceResponse struct { func (x *GetWorkspaceResponse) Reset() { *x = GetWorkspaceResponse{} - mi := &file_openshell_proto_msgTypes[213] + mi := &file_openshell_proto_msgTypes[214] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16603,7 +16728,7 @@ func (x *GetWorkspaceResponse) String() string { func (*GetWorkspaceResponse) ProtoMessage() {} func (x *GetWorkspaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[213] + mi := &file_openshell_proto_msgTypes[214] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16616,7 +16741,7 @@ func (x *GetWorkspaceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetWorkspaceResponse.ProtoReflect.Descriptor instead. func (*GetWorkspaceResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{213} + return file_openshell_proto_rawDescGZIP(), []int{214} } func (x *GetWorkspaceResponse) GetWorkspace() *datamodelv1.Workspace { @@ -16643,7 +16768,7 @@ type ListWorkspacesRequest struct { func (x *ListWorkspacesRequest) Reset() { *x = ListWorkspacesRequest{} - mi := &file_openshell_proto_msgTypes[214] + mi := &file_openshell_proto_msgTypes[215] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16655,7 +16780,7 @@ func (x *ListWorkspacesRequest) String() string { func (*ListWorkspacesRequest) ProtoMessage() {} func (x *ListWorkspacesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[214] + mi := &file_openshell_proto_msgTypes[215] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16668,7 +16793,7 @@ func (x *ListWorkspacesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkspacesRequest.ProtoReflect.Descriptor instead. func (*ListWorkspacesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{214} + return file_openshell_proto_rawDescGZIP(), []int{215} } func (x *ListWorkspacesRequest) GetPageSize() int32 { @@ -16704,7 +16829,7 @@ type ListWorkspacesResponse struct { func (x *ListWorkspacesResponse) Reset() { *x = ListWorkspacesResponse{} - mi := &file_openshell_proto_msgTypes[215] + mi := &file_openshell_proto_msgTypes[216] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16716,7 +16841,7 @@ func (x *ListWorkspacesResponse) String() string { func (*ListWorkspacesResponse) ProtoMessage() {} func (x *ListWorkspacesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[215] + mi := &file_openshell_proto_msgTypes[216] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16729,7 +16854,7 @@ func (x *ListWorkspacesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkspacesResponse.ProtoReflect.Descriptor instead. func (*ListWorkspacesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{215} + return file_openshell_proto_rawDescGZIP(), []int{216} } func (x *ListWorkspacesResponse) GetWorkspaces() []*datamodelv1.Workspace { @@ -16760,7 +16885,7 @@ type DeleteWorkspaceRequest struct { func (x *DeleteWorkspaceRequest) Reset() { *x = DeleteWorkspaceRequest{} - mi := &file_openshell_proto_msgTypes[216] + mi := &file_openshell_proto_msgTypes[217] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16772,7 +16897,7 @@ func (x *DeleteWorkspaceRequest) String() string { func (*DeleteWorkspaceRequest) ProtoMessage() {} func (x *DeleteWorkspaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[216] + mi := &file_openshell_proto_msgTypes[217] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16785,7 +16910,7 @@ func (x *DeleteWorkspaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteWorkspaceRequest.ProtoReflect.Descriptor instead. func (*DeleteWorkspaceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{216} + return file_openshell_proto_rawDescGZIP(), []int{217} } func (x *DeleteWorkspaceRequest) GetName() string { @@ -16819,7 +16944,7 @@ type DeleteWorkspaceResponse struct { func (x *DeleteWorkspaceResponse) Reset() { *x = DeleteWorkspaceResponse{} - mi := &file_openshell_proto_msgTypes[217] + mi := &file_openshell_proto_msgTypes[218] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16831,7 +16956,7 @@ func (x *DeleteWorkspaceResponse) String() string { func (*DeleteWorkspaceResponse) ProtoMessage() {} func (x *DeleteWorkspaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[217] + mi := &file_openshell_proto_msgTypes[218] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16844,7 +16969,7 @@ func (x *DeleteWorkspaceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteWorkspaceResponse.ProtoReflect.Descriptor instead. func (*DeleteWorkspaceResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{217} + return file_openshell_proto_rawDescGZIP(), []int{218} } func (x *DeleteWorkspaceResponse) GetOutcome() DeletionOutcome { @@ -16868,7 +16993,7 @@ type WorkspaceMember struct { func (x *WorkspaceMember) Reset() { *x = WorkspaceMember{} - mi := &file_openshell_proto_msgTypes[218] + mi := &file_openshell_proto_msgTypes[219] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16880,7 +17005,7 @@ func (x *WorkspaceMember) String() string { func (*WorkspaceMember) ProtoMessage() {} func (x *WorkspaceMember) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[218] + mi := &file_openshell_proto_msgTypes[219] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16893,7 +17018,7 @@ func (x *WorkspaceMember) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkspaceMember.ProtoReflect.Descriptor instead. func (*WorkspaceMember) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{218} + return file_openshell_proto_rawDescGZIP(), []int{219} } func (x *WorkspaceMember) GetMetadata() *datamodelv1.ObjectMeta { @@ -16934,7 +17059,7 @@ type AddWorkspaceMemberRequest struct { func (x *AddWorkspaceMemberRequest) Reset() { *x = AddWorkspaceMemberRequest{} - mi := &file_openshell_proto_msgTypes[219] + mi := &file_openshell_proto_msgTypes[220] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16946,7 +17071,7 @@ func (x *AddWorkspaceMemberRequest) String() string { func (*AddWorkspaceMemberRequest) ProtoMessage() {} func (x *AddWorkspaceMemberRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[219] + mi := &file_openshell_proto_msgTypes[220] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16959,7 +17084,7 @@ func (x *AddWorkspaceMemberRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AddWorkspaceMemberRequest.ProtoReflect.Descriptor instead. func (*AddWorkspaceMemberRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{219} + return file_openshell_proto_rawDescGZIP(), []int{220} } func (x *AddWorkspaceMemberRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { @@ -17000,7 +17125,7 @@ type AddWorkspaceMemberResponse struct { func (x *AddWorkspaceMemberResponse) Reset() { *x = AddWorkspaceMemberResponse{} - mi := &file_openshell_proto_msgTypes[220] + mi := &file_openshell_proto_msgTypes[221] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17012,7 +17137,7 @@ func (x *AddWorkspaceMemberResponse) String() string { func (*AddWorkspaceMemberResponse) ProtoMessage() {} func (x *AddWorkspaceMemberResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[220] + mi := &file_openshell_proto_msgTypes[221] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17025,7 +17150,7 @@ func (x *AddWorkspaceMemberResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AddWorkspaceMemberResponse.ProtoReflect.Descriptor instead. func (*AddWorkspaceMemberResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{220} + return file_openshell_proto_rawDescGZIP(), []int{221} } func (x *AddWorkspaceMemberResponse) GetMember() *WorkspaceMember { @@ -17051,7 +17176,7 @@ type RemoveWorkspaceMemberRequest struct { func (x *RemoveWorkspaceMemberRequest) Reset() { *x = RemoveWorkspaceMemberRequest{} - mi := &file_openshell_proto_msgTypes[221] + mi := &file_openshell_proto_msgTypes[222] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17063,7 +17188,7 @@ func (x *RemoveWorkspaceMemberRequest) String() string { func (*RemoveWorkspaceMemberRequest) ProtoMessage() {} func (x *RemoveWorkspaceMemberRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[221] + mi := &file_openshell_proto_msgTypes[222] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17076,7 +17201,7 @@ func (x *RemoveWorkspaceMemberRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveWorkspaceMemberRequest.ProtoReflect.Descriptor instead. func (*RemoveWorkspaceMemberRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{221} + return file_openshell_proto_rawDescGZIP(), []int{222} } func (x *RemoveWorkspaceMemberRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { @@ -17117,7 +17242,7 @@ type RemoveWorkspaceMemberResponse struct { func (x *RemoveWorkspaceMemberResponse) Reset() { *x = RemoveWorkspaceMemberResponse{} - mi := &file_openshell_proto_msgTypes[222] + mi := &file_openshell_proto_msgTypes[223] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17129,7 +17254,7 @@ func (x *RemoveWorkspaceMemberResponse) String() string { func (*RemoveWorkspaceMemberResponse) ProtoMessage() {} func (x *RemoveWorkspaceMemberResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[222] + mi := &file_openshell_proto_msgTypes[223] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17142,7 +17267,7 @@ func (x *RemoveWorkspaceMemberResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveWorkspaceMemberResponse.ProtoReflect.Descriptor instead. func (*RemoveWorkspaceMemberResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{222} + return file_openshell_proto_rawDescGZIP(), []int{223} } func (x *RemoveWorkspaceMemberResponse) GetOutcome() DeletionOutcome { @@ -17169,7 +17294,7 @@ type ListWorkspaceMembersRequest struct { func (x *ListWorkspaceMembersRequest) Reset() { *x = ListWorkspaceMembersRequest{} - mi := &file_openshell_proto_msgTypes[223] + mi := &file_openshell_proto_msgTypes[224] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17181,7 +17306,7 @@ func (x *ListWorkspaceMembersRequest) String() string { func (*ListWorkspaceMembersRequest) ProtoMessage() {} func (x *ListWorkspaceMembersRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[223] + mi := &file_openshell_proto_msgTypes[224] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17194,7 +17319,7 @@ func (x *ListWorkspaceMembersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkspaceMembersRequest.ProtoReflect.Descriptor instead. func (*ListWorkspaceMembersRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{223} + return file_openshell_proto_rawDescGZIP(), []int{224} } func (x *ListWorkspaceMembersRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { @@ -17230,7 +17355,7 @@ type ListWorkspaceMembersResponse struct { func (x *ListWorkspaceMembersResponse) Reset() { *x = ListWorkspaceMembersResponse{} - mi := &file_openshell_proto_msgTypes[224] + mi := &file_openshell_proto_msgTypes[225] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17242,7 +17367,7 @@ func (x *ListWorkspaceMembersResponse) String() string { func (*ListWorkspaceMembersResponse) ProtoMessage() {} func (x *ListWorkspaceMembersResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[224] + mi := &file_openshell_proto_msgTypes[225] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17255,7 +17380,7 @@ func (x *ListWorkspaceMembersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkspaceMembersResponse.ProtoReflect.Descriptor instead. func (*ListWorkspaceMembersResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{224} + return file_openshell_proto_rawDescGZIP(), []int{225} } func (x *ListWorkspaceMembersResponse) GetMembers() []*WorkspaceMember { @@ -17290,7 +17415,7 @@ type ExtensionServiceCredential struct { func (x *ExtensionServiceCredential) Reset() { *x = ExtensionServiceCredential{} - mi := &file_openshell_proto_msgTypes[225] + mi := &file_openshell_proto_msgTypes[226] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17302,7 +17427,7 @@ func (x *ExtensionServiceCredential) String() string { func (*ExtensionServiceCredential) ProtoMessage() {} func (x *ExtensionServiceCredential) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[225] + mi := &file_openshell_proto_msgTypes[226] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17315,7 +17440,7 @@ func (x *ExtensionServiceCredential) ProtoReflect() protoreflect.Message { // Deprecated: Use ExtensionServiceCredential.ProtoReflect.Descriptor instead. func (*ExtensionServiceCredential) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{225} + return file_openshell_proto_rawDescGZIP(), []int{226} } func (x *ExtensionServiceCredential) GetServiceName() string { @@ -17352,7 +17477,7 @@ type EndpointObservation struct { func (x *EndpointObservation) Reset() { *x = EndpointObservation{} - mi := &file_openshell_proto_msgTypes[226] + mi := &file_openshell_proto_msgTypes[227] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17364,7 +17489,7 @@ func (x *EndpointObservation) String() string { func (*EndpointObservation) ProtoMessage() {} func (x *EndpointObservation) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[226] + mi := &file_openshell_proto_msgTypes[227] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17377,7 +17502,7 @@ func (x *EndpointObservation) ProtoReflect() protoreflect.Message { // Deprecated: Use EndpointObservation.ProtoReflect.Descriptor instead. func (*EndpointObservation) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{226} + return file_openshell_proto_rawDescGZIP(), []int{227} } func (x *EndpointObservation) GetEndpointId() string { @@ -17420,7 +17545,7 @@ type ReportEndpointStatusRequest struct { func (x *ReportEndpointStatusRequest) Reset() { *x = ReportEndpointStatusRequest{} - mi := &file_openshell_proto_msgTypes[227] + mi := &file_openshell_proto_msgTypes[228] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17432,7 +17557,7 @@ func (x *ReportEndpointStatusRequest) String() string { func (*ReportEndpointStatusRequest) ProtoMessage() {} func (x *ReportEndpointStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[227] + mi := &file_openshell_proto_msgTypes[228] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17445,7 +17570,7 @@ func (x *ReportEndpointStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportEndpointStatusRequest.ProtoReflect.Descriptor instead. func (*ReportEndpointStatusRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{227} + return file_openshell_proto_rawDescGZIP(), []int{228} } func (x *ReportEndpointStatusRequest) GetSandboxId() string { @@ -17506,7 +17631,7 @@ type ReportEndpointStatusResponse struct { func (x *ReportEndpointStatusResponse) Reset() { *x = ReportEndpointStatusResponse{} - mi := &file_openshell_proto_msgTypes[228] + mi := &file_openshell_proto_msgTypes[229] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17518,7 +17643,7 @@ func (x *ReportEndpointStatusResponse) String() string { func (*ReportEndpointStatusResponse) ProtoMessage() {} func (x *ReportEndpointStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[228] + mi := &file_openshell_proto_msgTypes[229] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17531,7 +17656,7 @@ func (x *ReportEndpointStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportEndpointStatusResponse.ProtoReflect.Descriptor instead. func (*ReportEndpointStatusResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{228} + return file_openshell_proto_rawDescGZIP(), []int{229} } // A configured endpoint and its last accepted network result in one record. @@ -17560,7 +17685,7 @@ type EndpointStatus struct { func (x *EndpointStatus) Reset() { *x = EndpointStatus{} - mi := &file_openshell_proto_msgTypes[229] + mi := &file_openshell_proto_msgTypes[230] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17572,7 +17697,7 @@ func (x *EndpointStatus) String() string { func (*EndpointStatus) ProtoMessage() {} func (x *EndpointStatus) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[229] + mi := &file_openshell_proto_msgTypes[230] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17585,7 +17710,7 @@ func (x *EndpointStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use EndpointStatus.ProtoReflect.Descriptor instead. func (*EndpointStatus) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{229} + return file_openshell_proto_rawDescGZIP(), []int{230} } func (x *EndpointStatus) GetEndpointId() string { @@ -17655,7 +17780,7 @@ type SandboxProvisioning struct { func (x *SandboxProvisioning) Reset() { *x = SandboxProvisioning{} - mi := &file_openshell_proto_msgTypes[230] + mi := &file_openshell_proto_msgTypes[231] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17667,7 +17792,7 @@ func (x *SandboxProvisioning) String() string { func (*SandboxProvisioning) ProtoMessage() {} func (x *SandboxProvisioning) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[230] + mi := &file_openshell_proto_msgTypes[231] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17680,7 +17805,7 @@ func (x *SandboxProvisioning) ProtoReflect() protoreflect.Message { // Deprecated: Use SandboxProvisioning.ProtoReflect.Descriptor instead. func (*SandboxProvisioning) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{230} + return file_openshell_proto_rawDescGZIP(), []int{231} } func (x *SandboxProvisioning) GetAttemptId() string { @@ -17775,7 +17900,7 @@ type SandboxServiceExposure struct { func (x *SandboxServiceExposure) Reset() { *x = SandboxServiceExposure{} - mi := &file_openshell_proto_msgTypes[231] + mi := &file_openshell_proto_msgTypes[232] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17787,7 +17912,7 @@ func (x *SandboxServiceExposure) String() string { func (*SandboxServiceExposure) ProtoMessage() {} func (x *SandboxServiceExposure) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[231] + mi := &file_openshell_proto_msgTypes[232] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17800,7 +17925,7 @@ func (x *SandboxServiceExposure) ProtoReflect() protoreflect.Message { // Deprecated: Use SandboxServiceExposure.ProtoReflect.Descriptor instead. func (*SandboxServiceExposure) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{231} + return file_openshell_proto_rawDescGZIP(), []int{232} } func (x *SandboxServiceExposure) GetService() string { @@ -18805,13 +18930,23 @@ const file_openshell_proto_rawDesc = "" + "\x17PushSandboxLogsResponse\"m\n" + "\x16GetSandboxLogsResponse\x120\n" + "\x04logs\x18\x01 \x03(\v2\x1c.openshell.v1.SandboxLogLineR\x04logs\x12!\n" + - "\fbuffer_total\x18\x02 \x01(\rR\vbufferTotal\"\xa2\x02\n" + + "\fbuffer_total\x18\x02 \x01(\rR\vbufferTotal\"{\n" + + "\x0eOtelExportData\x12\x1d\n" + + "\n" + + "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x1f\n" + + "\n" + + "trace_data\x18\x02 \x01(\fH\x00R\ttraceData\x12\x1f\n" + + "\vocsf_events\x18\x03 \x03(\fR\n" + + "ocsfEventsB\b\n" + + "\x06signal\"\xe3\x02\n" + "\x11SupervisorMessage\x125\n" + "\x05hello\x18\x01 \x01(\v2\x1d.openshell.v1.SupervisorHelloH\x00R\x05hello\x12A\n" + "\theartbeat\x18\x02 \x01(\v2!.openshell.v1.SupervisorHeartbeatH\x00R\theartbeat\x12K\n" + "\x11relay_open_result\x18\x03 \x01(\v2\x1d.openshell.v1.RelayOpenResultH\x00R\x0frelayOpenResult\x12;\n" + "\vrelay_close\x18\x04 \x01(\v2\x18.openshell.v1.RelayCloseH\x00R\n" + - "relayCloseB\t\n" + + "relayClose\x12?\n" + + "\votel_export\x18\x05 \x01(\v2\x1c.openshell.v1.OtelExportDataH\x00R\n" + + "otelExportB\t\n" + "\apayload\"\xea\x02\n" + "\x0eGatewayMessage\x12J\n" + "\x10session_accepted\x18\x01 \x01(\v2\x1d.openshell.v1.SessionAcceptedH\x00R\x0fsessionAccepted\x12J\n" + @@ -18821,17 +18956,19 @@ const file_openshell_proto_rawDesc = "" + "relay_open\x18\x04 \x01(\v2\x17.openshell.v1.RelayOpenH\x00R\trelayOpen\x12;\n" + "\vrelay_close\x18\x05 \x01(\v2\x18.openshell.v1.RelayCloseH\x00R\n" + "relayCloseB\t\n" + - "\apayload\"\xbc\x01\n" + + "\apayload\"\xe0\x01\n" + "\x0fSupervisorHello\x12\x1d\n" + "\n" + "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x1f\n" + "\vinstance_id\x18\x02 \x01(\tR\n" + "instanceId\x12)\n" + "\x10connection_epoch\x18\x03 \x01(\x04R\x0fconnectionEpoch\x12>\n" + - "\x1bsupports_provider_readiness\x18\x04 \x01(\bR\x19supportsProviderReadiness\"\x99\x01\n" + + "\x1bsupports_provider_readiness\x18\x04 \x01(\bR\x19supportsProviderReadiness\x12\"\n" + + "\fcapabilities\x18\x05 \x03(\tR\fcapabilities\"\xbd\x01\n" + "\x0fSessionAccepted\x12\x1d\n" + "\n" + - "session_id\x18\x01 \x01(\tR\tsessionId\x12H\n" + + "session_id\x18\x01 \x01(\tR\tsessionId\x12\"\n" + + "\fcapabilities\x18\x03 \x03(\tR\fcapabilities\x12H\n" + "\x12heartbeat_interval\x18f \x01(\v2\x19.google.protobuf.DurationR\x11heartbeatIntervalJ\x04\b\x02\x10\x03R\x17heartbeat_interval_secs\")\n" + "\x0fSessionRejected\x12\x16\n" + "\x06reason\x18\x01 \x01(\tR\x06reason\"\x15\n" + @@ -19491,7 +19628,7 @@ func file_openshell_proto_rawDescGZIP() []byte { } var file_openshell_proto_enumTypes = make([]protoimpl.EnumInfo, 20) -var file_openshell_proto_msgTypes = make([]protoimpl.MessageInfo, 255) +var file_openshell_proto_msgTypes = make([]protoimpl.MessageInfo, 256) var file_openshell_proto_goTypes = []any{ (ExtensionKind)(0), // 0: openshell.v1.ExtensionKind (SandboxPhase)(0), // 1: openshell.v1.SandboxPhase @@ -19677,122 +19814,123 @@ var file_openshell_proto_goTypes = []any{ (*PushSandboxLogsRequest)(nil), // 181: openshell.v1.PushSandboxLogsRequest (*PushSandboxLogsResponse)(nil), // 182: openshell.v1.PushSandboxLogsResponse (*GetSandboxLogsResponse)(nil), // 183: openshell.v1.GetSandboxLogsResponse - (*SupervisorMessage)(nil), // 184: openshell.v1.SupervisorMessage - (*GatewayMessage)(nil), // 185: openshell.v1.GatewayMessage - (*SupervisorHello)(nil), // 186: openshell.v1.SupervisorHello - (*SessionAccepted)(nil), // 187: openshell.v1.SessionAccepted - (*SessionRejected)(nil), // 188: openshell.v1.SessionRejected - (*SupervisorHeartbeat)(nil), // 189: openshell.v1.SupervisorHeartbeat - (*GatewayHeartbeat)(nil), // 190: openshell.v1.GatewayHeartbeat - (*ReportMainProcessExitRequest)(nil), // 191: openshell.v1.ReportMainProcessExitRequest - (*ReportMainProcessExitResponse)(nil), // 192: openshell.v1.ReportMainProcessExitResponse - (*FinalizeMainProcessExitRequest)(nil), // 193: openshell.v1.FinalizeMainProcessExitRequest - (*FinalizeMainProcessExitResponse)(nil), // 194: openshell.v1.FinalizeMainProcessExitResponse - (*RelayOpen)(nil), // 195: openshell.v1.RelayOpen - (*SshRelayTarget)(nil), // 196: openshell.v1.SshRelayTarget - (*TcpRelayTarget)(nil), // 197: openshell.v1.TcpRelayTarget - (*RelayInit)(nil), // 198: openshell.v1.RelayInit - (*RelayFrame)(nil), // 199: openshell.v1.RelayFrame - (*PeerRelayInit)(nil), // 200: openshell.v1.PeerRelayInit - (*PeerRelayFrame)(nil), // 201: openshell.v1.PeerRelayFrame - (*RelayOpenResult)(nil), // 202: openshell.v1.RelayOpenResult - (*RelayClose)(nil), // 203: openshell.v1.RelayClose - (*L7RequestSample)(nil), // 204: openshell.v1.L7RequestSample - (*DenialSummary)(nil), // 205: openshell.v1.DenialSummary - (*DenialGroupCount)(nil), // 206: openshell.v1.DenialGroupCount - (*NetworkActivitySummary)(nil), // 207: openshell.v1.NetworkActivitySummary - (*PolicyChunk)(nil), // 208: openshell.v1.PolicyChunk - (*DraftPolicyUpdate)(nil), // 209: openshell.v1.DraftPolicyUpdate - (*SubmitPolicyAnalysisRequest)(nil), // 210: openshell.v1.SubmitPolicyAnalysisRequest - (*SubmitPolicyAnalysisResponse)(nil), // 211: openshell.v1.SubmitPolicyAnalysisResponse - (*GetDraftPolicyRequest)(nil), // 212: openshell.v1.GetDraftPolicyRequest - (*GetDraftPolicyResponse)(nil), // 213: openshell.v1.GetDraftPolicyResponse - (*ApproveDraftChunkRequest)(nil), // 214: openshell.v1.ApproveDraftChunkRequest - (*ApproveDraftChunkResponse)(nil), // 215: openshell.v1.ApproveDraftChunkResponse - (*RejectDraftChunkRequest)(nil), // 216: openshell.v1.RejectDraftChunkRequest - (*RejectDraftChunkResponse)(nil), // 217: openshell.v1.RejectDraftChunkResponse - (*DraftChunkApproval)(nil), // 218: openshell.v1.DraftChunkApproval - (*ApproveAllDraftChunksRequest)(nil), // 219: openshell.v1.ApproveAllDraftChunksRequest - (*ApproveAllDraftChunksResponse)(nil), // 220: openshell.v1.ApproveAllDraftChunksResponse - (*EditDraftChunkRequest)(nil), // 221: openshell.v1.EditDraftChunkRequest - (*EditDraftChunkResponse)(nil), // 222: openshell.v1.EditDraftChunkResponse - (*UndoDraftChunkRequest)(nil), // 223: openshell.v1.UndoDraftChunkRequest - (*UndoDraftChunkResponse)(nil), // 224: openshell.v1.UndoDraftChunkResponse - (*ClearDraftChunksRequest)(nil), // 225: openshell.v1.ClearDraftChunksRequest - (*ClearDraftChunksResponse)(nil), // 226: openshell.v1.ClearDraftChunksResponse - (*GetDraftHistoryRequest)(nil), // 227: openshell.v1.GetDraftHistoryRequest - (*DraftHistoryEntry)(nil), // 228: openshell.v1.DraftHistoryEntry - (*GetDraftHistoryResponse)(nil), // 229: openshell.v1.GetDraftHistoryResponse - (*CreateWorkspaceRequest)(nil), // 230: openshell.v1.CreateWorkspaceRequest - (*CreateWorkspaceResponse)(nil), // 231: openshell.v1.CreateWorkspaceResponse - (*GetWorkspaceRequest)(nil), // 232: openshell.v1.GetWorkspaceRequest - (*GetWorkspaceResponse)(nil), // 233: openshell.v1.GetWorkspaceResponse - (*ListWorkspacesRequest)(nil), // 234: openshell.v1.ListWorkspacesRequest - (*ListWorkspacesResponse)(nil), // 235: openshell.v1.ListWorkspacesResponse - (*DeleteWorkspaceRequest)(nil), // 236: openshell.v1.DeleteWorkspaceRequest - (*DeleteWorkspaceResponse)(nil), // 237: openshell.v1.DeleteWorkspaceResponse - (*WorkspaceMember)(nil), // 238: openshell.v1.WorkspaceMember - (*AddWorkspaceMemberRequest)(nil), // 239: openshell.v1.AddWorkspaceMemberRequest - (*AddWorkspaceMemberResponse)(nil), // 240: openshell.v1.AddWorkspaceMemberResponse - (*RemoveWorkspaceMemberRequest)(nil), // 241: openshell.v1.RemoveWorkspaceMemberRequest - (*RemoveWorkspaceMemberResponse)(nil), // 242: openshell.v1.RemoveWorkspaceMemberResponse - (*ListWorkspaceMembersRequest)(nil), // 243: openshell.v1.ListWorkspaceMembersRequest - (*ListWorkspaceMembersResponse)(nil), // 244: openshell.v1.ListWorkspaceMembersResponse - (*ExtensionServiceCredential)(nil), // 245: openshell.v1.ExtensionServiceCredential - (*EndpointObservation)(nil), // 246: openshell.v1.EndpointObservation - (*ReportEndpointStatusRequest)(nil), // 247: openshell.v1.ReportEndpointStatusRequest - (*ReportEndpointStatusResponse)(nil), // 248: openshell.v1.ReportEndpointStatusResponse - (*EndpointStatus)(nil), // 249: openshell.v1.EndpointStatus - (*SandboxProvisioning)(nil), // 250: openshell.v1.SandboxProvisioning - (*SandboxServiceExposure)(nil), // 251: openshell.v1.SandboxServiceExposure - nil, // 252: openshell.v1.SandboxSpec.EnvironmentEntry - nil, // 253: openshell.v1.SandboxTemplate.LabelsEntry - nil, // 254: openshell.v1.SandboxTemplate.AnnotationsEntry - nil, // 255: openshell.v1.SandboxTemplate.EnvironmentEntry - nil, // 256: openshell.v1.SandboxWorkloadConfig.EnvironmentEntry - nil, // 257: openshell.v1.PlatformEvent.MetadataEntry - nil, // 258: openshell.v1.CreateSandboxRequest.LabelsEntry - nil, // 259: openshell.v1.CreateSandboxRequest.AnnotationsEntry - nil, // 260: openshell.v1.SandboxResponse.ServiceUrlsEntry - nil, // 261: openshell.v1.ExecSandboxRequest.EnvironmentEntry - nil, // 262: openshell.v1.SandboxLogLine.FieldsEntry - nil, // 263: openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry - nil, // 264: openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry - nil, // 265: openshell.v1.ProviderProfile.AnnotationsEntry - nil, // 266: openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry - nil, // 267: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry - nil, // 268: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry - nil, // 269: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry - nil, // 270: openshell.v1.GetSandboxProviderEnvironmentResponse.FilesEntry - nil, // 271: openshell.v1.UpdateConfigRequest.AnnotationsEntry - nil, // 272: openshell.v1.UpdateConfigResponse.AnnotationsEntry - nil, // 273: openshell.v1.SandboxPolicyRevision.ProvenanceEntry - nil, // 274: openshell.v1.CreateWorkspaceRequest.LabelsEntry - (*timestamppb.Timestamp)(nil), // 275: google.protobuf.Timestamp - (*datamodelv1.ObjectMeta)(nil), // 276: openshell.datamodel.v1.ObjectMeta - (*sandboxv1.SandboxPolicy)(nil), // 277: openshell.sandbox.v1.SandboxPolicy - (*structpb.Struct)(nil), // 278: google.protobuf.Struct - (*durationpb.Duration)(nil), // 279: google.protobuf.Duration - (*datamodelv1.WorkspaceSelector)(nil), // 280: openshell.datamodel.v1.WorkspaceSelector - (*datamodelv1.Provider)(nil), // 281: openshell.datamodel.v1.Provider - (sandboxv1.PolicySource)(0), // 282: openshell.sandbox.v1.PolicySource - (*sandboxv1.NetworkEndpoint)(nil), // 283: openshell.sandbox.v1.NetworkEndpoint - (*sandboxv1.NetworkBinary)(nil), // 284: openshell.sandbox.v1.NetworkBinary - (*sandboxv1.SettingValue)(nil), // 285: openshell.sandbox.v1.SettingValue - (*sandboxv1.NetworkPolicyRule)(nil), // 286: openshell.sandbox.v1.NetworkPolicyRule - (*sandboxv1.L7DenyRule)(nil), // 287: openshell.sandbox.v1.L7DenyRule - (*sandboxv1.L7Rule)(nil), // 288: openshell.sandbox.v1.L7Rule - (*datamodelv1.Workspace)(nil), // 289: openshell.datamodel.v1.Workspace - (*sandboxv1.GetSandboxConfigRequest)(nil), // 290: openshell.sandbox.v1.GetSandboxConfigRequest - (*sandboxv1.GetGatewayConfigRequest)(nil), // 291: openshell.sandbox.v1.GetGatewayConfigRequest - (*sandboxv1.GetSandboxConfigResponse)(nil), // 292: openshell.sandbox.v1.GetSandboxConfigResponse - (*sandboxv1.GetGatewayConfigResponse)(nil), // 293: openshell.sandbox.v1.GetGatewayConfigResponse + (*OtelExportData)(nil), // 184: openshell.v1.OtelExportData + (*SupervisorMessage)(nil), // 185: openshell.v1.SupervisorMessage + (*GatewayMessage)(nil), // 186: openshell.v1.GatewayMessage + (*SupervisorHello)(nil), // 187: openshell.v1.SupervisorHello + (*SessionAccepted)(nil), // 188: openshell.v1.SessionAccepted + (*SessionRejected)(nil), // 189: openshell.v1.SessionRejected + (*SupervisorHeartbeat)(nil), // 190: openshell.v1.SupervisorHeartbeat + (*GatewayHeartbeat)(nil), // 191: openshell.v1.GatewayHeartbeat + (*ReportMainProcessExitRequest)(nil), // 192: openshell.v1.ReportMainProcessExitRequest + (*ReportMainProcessExitResponse)(nil), // 193: openshell.v1.ReportMainProcessExitResponse + (*FinalizeMainProcessExitRequest)(nil), // 194: openshell.v1.FinalizeMainProcessExitRequest + (*FinalizeMainProcessExitResponse)(nil), // 195: openshell.v1.FinalizeMainProcessExitResponse + (*RelayOpen)(nil), // 196: openshell.v1.RelayOpen + (*SshRelayTarget)(nil), // 197: openshell.v1.SshRelayTarget + (*TcpRelayTarget)(nil), // 198: openshell.v1.TcpRelayTarget + (*RelayInit)(nil), // 199: openshell.v1.RelayInit + (*RelayFrame)(nil), // 200: openshell.v1.RelayFrame + (*PeerRelayInit)(nil), // 201: openshell.v1.PeerRelayInit + (*PeerRelayFrame)(nil), // 202: openshell.v1.PeerRelayFrame + (*RelayOpenResult)(nil), // 203: openshell.v1.RelayOpenResult + (*RelayClose)(nil), // 204: openshell.v1.RelayClose + (*L7RequestSample)(nil), // 205: openshell.v1.L7RequestSample + (*DenialSummary)(nil), // 206: openshell.v1.DenialSummary + (*DenialGroupCount)(nil), // 207: openshell.v1.DenialGroupCount + (*NetworkActivitySummary)(nil), // 208: openshell.v1.NetworkActivitySummary + (*PolicyChunk)(nil), // 209: openshell.v1.PolicyChunk + (*DraftPolicyUpdate)(nil), // 210: openshell.v1.DraftPolicyUpdate + (*SubmitPolicyAnalysisRequest)(nil), // 211: openshell.v1.SubmitPolicyAnalysisRequest + (*SubmitPolicyAnalysisResponse)(nil), // 212: openshell.v1.SubmitPolicyAnalysisResponse + (*GetDraftPolicyRequest)(nil), // 213: openshell.v1.GetDraftPolicyRequest + (*GetDraftPolicyResponse)(nil), // 214: openshell.v1.GetDraftPolicyResponse + (*ApproveDraftChunkRequest)(nil), // 215: openshell.v1.ApproveDraftChunkRequest + (*ApproveDraftChunkResponse)(nil), // 216: openshell.v1.ApproveDraftChunkResponse + (*RejectDraftChunkRequest)(nil), // 217: openshell.v1.RejectDraftChunkRequest + (*RejectDraftChunkResponse)(nil), // 218: openshell.v1.RejectDraftChunkResponse + (*DraftChunkApproval)(nil), // 219: openshell.v1.DraftChunkApproval + (*ApproveAllDraftChunksRequest)(nil), // 220: openshell.v1.ApproveAllDraftChunksRequest + (*ApproveAllDraftChunksResponse)(nil), // 221: openshell.v1.ApproveAllDraftChunksResponse + (*EditDraftChunkRequest)(nil), // 222: openshell.v1.EditDraftChunkRequest + (*EditDraftChunkResponse)(nil), // 223: openshell.v1.EditDraftChunkResponse + (*UndoDraftChunkRequest)(nil), // 224: openshell.v1.UndoDraftChunkRequest + (*UndoDraftChunkResponse)(nil), // 225: openshell.v1.UndoDraftChunkResponse + (*ClearDraftChunksRequest)(nil), // 226: openshell.v1.ClearDraftChunksRequest + (*ClearDraftChunksResponse)(nil), // 227: openshell.v1.ClearDraftChunksResponse + (*GetDraftHistoryRequest)(nil), // 228: openshell.v1.GetDraftHistoryRequest + (*DraftHistoryEntry)(nil), // 229: openshell.v1.DraftHistoryEntry + (*GetDraftHistoryResponse)(nil), // 230: openshell.v1.GetDraftHistoryResponse + (*CreateWorkspaceRequest)(nil), // 231: openshell.v1.CreateWorkspaceRequest + (*CreateWorkspaceResponse)(nil), // 232: openshell.v1.CreateWorkspaceResponse + (*GetWorkspaceRequest)(nil), // 233: openshell.v1.GetWorkspaceRequest + (*GetWorkspaceResponse)(nil), // 234: openshell.v1.GetWorkspaceResponse + (*ListWorkspacesRequest)(nil), // 235: openshell.v1.ListWorkspacesRequest + (*ListWorkspacesResponse)(nil), // 236: openshell.v1.ListWorkspacesResponse + (*DeleteWorkspaceRequest)(nil), // 237: openshell.v1.DeleteWorkspaceRequest + (*DeleteWorkspaceResponse)(nil), // 238: openshell.v1.DeleteWorkspaceResponse + (*WorkspaceMember)(nil), // 239: openshell.v1.WorkspaceMember + (*AddWorkspaceMemberRequest)(nil), // 240: openshell.v1.AddWorkspaceMemberRequest + (*AddWorkspaceMemberResponse)(nil), // 241: openshell.v1.AddWorkspaceMemberResponse + (*RemoveWorkspaceMemberRequest)(nil), // 242: openshell.v1.RemoveWorkspaceMemberRequest + (*RemoveWorkspaceMemberResponse)(nil), // 243: openshell.v1.RemoveWorkspaceMemberResponse + (*ListWorkspaceMembersRequest)(nil), // 244: openshell.v1.ListWorkspaceMembersRequest + (*ListWorkspaceMembersResponse)(nil), // 245: openshell.v1.ListWorkspaceMembersResponse + (*ExtensionServiceCredential)(nil), // 246: openshell.v1.ExtensionServiceCredential + (*EndpointObservation)(nil), // 247: openshell.v1.EndpointObservation + (*ReportEndpointStatusRequest)(nil), // 248: openshell.v1.ReportEndpointStatusRequest + (*ReportEndpointStatusResponse)(nil), // 249: openshell.v1.ReportEndpointStatusResponse + (*EndpointStatus)(nil), // 250: openshell.v1.EndpointStatus + (*SandboxProvisioning)(nil), // 251: openshell.v1.SandboxProvisioning + (*SandboxServiceExposure)(nil), // 252: openshell.v1.SandboxServiceExposure + nil, // 253: openshell.v1.SandboxSpec.EnvironmentEntry + nil, // 254: openshell.v1.SandboxTemplate.LabelsEntry + nil, // 255: openshell.v1.SandboxTemplate.AnnotationsEntry + nil, // 256: openshell.v1.SandboxTemplate.EnvironmentEntry + nil, // 257: openshell.v1.SandboxWorkloadConfig.EnvironmentEntry + nil, // 258: openshell.v1.PlatformEvent.MetadataEntry + nil, // 259: openshell.v1.CreateSandboxRequest.LabelsEntry + nil, // 260: openshell.v1.CreateSandboxRequest.AnnotationsEntry + nil, // 261: openshell.v1.SandboxResponse.ServiceUrlsEntry + nil, // 262: openshell.v1.ExecSandboxRequest.EnvironmentEntry + nil, // 263: openshell.v1.SandboxLogLine.FieldsEntry + nil, // 264: openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry + nil, // 265: openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry + nil, // 266: openshell.v1.ProviderProfile.AnnotationsEntry + nil, // 267: openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry + nil, // 268: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry + nil, // 269: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry + nil, // 270: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry + nil, // 271: openshell.v1.GetSandboxProviderEnvironmentResponse.FilesEntry + nil, // 272: openshell.v1.UpdateConfigRequest.AnnotationsEntry + nil, // 273: openshell.v1.UpdateConfigResponse.AnnotationsEntry + nil, // 274: openshell.v1.SandboxPolicyRevision.ProvenanceEntry + nil, // 275: openshell.v1.CreateWorkspaceRequest.LabelsEntry + (*timestamppb.Timestamp)(nil), // 276: google.protobuf.Timestamp + (*datamodelv1.ObjectMeta)(nil), // 277: openshell.datamodel.v1.ObjectMeta + (*sandboxv1.SandboxPolicy)(nil), // 278: openshell.sandbox.v1.SandboxPolicy + (*structpb.Struct)(nil), // 279: google.protobuf.Struct + (*durationpb.Duration)(nil), // 280: google.protobuf.Duration + (*datamodelv1.WorkspaceSelector)(nil), // 281: openshell.datamodel.v1.WorkspaceSelector + (*datamodelv1.Provider)(nil), // 282: openshell.datamodel.v1.Provider + (sandboxv1.PolicySource)(0), // 283: openshell.sandbox.v1.PolicySource + (*sandboxv1.NetworkEndpoint)(nil), // 284: openshell.sandbox.v1.NetworkEndpoint + (*sandboxv1.NetworkBinary)(nil), // 285: openshell.sandbox.v1.NetworkBinary + (*sandboxv1.SettingValue)(nil), // 286: openshell.sandbox.v1.SettingValue + (*sandboxv1.NetworkPolicyRule)(nil), // 287: openshell.sandbox.v1.NetworkPolicyRule + (*sandboxv1.L7DenyRule)(nil), // 288: openshell.sandbox.v1.L7DenyRule + (*sandboxv1.L7Rule)(nil), // 289: openshell.sandbox.v1.L7Rule + (*datamodelv1.Workspace)(nil), // 290: openshell.datamodel.v1.Workspace + (*sandboxv1.GetSandboxConfigRequest)(nil), // 291: openshell.sandbox.v1.GetSandboxConfigRequest + (*sandboxv1.GetGatewayConfigRequest)(nil), // 292: openshell.sandbox.v1.GetGatewayConfigRequest + (*sandboxv1.GetSandboxConfigResponse)(nil), // 293: openshell.sandbox.v1.GetSandboxConfigResponse + (*sandboxv1.GetGatewayConfigResponse)(nil), // 294: openshell.sandbox.v1.GetGatewayConfigResponse } var file_openshell_proto_depIdxs = []int32{ - 275, // 0: openshell.v1.IssueSandboxTokenResponse.expiration_time:type_name -> google.protobuf.Timestamp - 275, // 1: openshell.v1.RefreshSandboxTokenResponse.expiration_time:type_name -> google.protobuf.Timestamp - 245, // 2: openshell.v1.RefreshSandboxTokenResponse.extension_credentials:type_name -> openshell.v1.ExtensionServiceCredential - 275, // 3: openshell.v1.RefreshSandboxTokenResponse.sandbox_expiration_time:type_name -> google.protobuf.Timestamp + 276, // 0: openshell.v1.IssueSandboxTokenResponse.expiration_time:type_name -> google.protobuf.Timestamp + 276, // 1: openshell.v1.RefreshSandboxTokenResponse.expiration_time:type_name -> google.protobuf.Timestamp + 246, // 2: openshell.v1.RefreshSandboxTokenResponse.extension_credentials:type_name -> openshell.v1.ExtensionServiceCredential + 276, // 3: openshell.v1.RefreshSandboxTokenResponse.sandbox_expiration_time:type_name -> google.protobuf.Timestamp 13, // 4: openshell.v1.HealthResponse.status:type_name -> openshell.v1.ServiceStatus 13, // 5: openshell.v1.GetGatewayInfoResponse.status:type_name -> openshell.v1.ServiceStatus 31, // 6: openshell.v1.GetGatewayInfoResponse.compute_drivers:type_name -> openshell.v1.ComputeDriverInfo @@ -19803,485 +19941,486 @@ var file_openshell_proto_depIdxs = []int32{ 34, // 11: openshell.v1.ResourceCapabilities.cpu:type_name -> openshell.v1.CpuResourceCapabilities 35, // 12: openshell.v1.ResourceCapabilities.memory:type_name -> openshell.v1.MemoryResourceCapabilities 36, // 13: openshell.v1.ResourceCapabilities.gpu:type_name -> openshell.v1.GpuResourceCapabilities - 276, // 14: openshell.v1.Sandbox.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 277, // 14: openshell.v1.Sandbox.metadata:type_name -> openshell.datamodel.v1.ObjectMeta 38, // 15: openshell.v1.Sandbox.spec:type_name -> openshell.v1.SandboxSpec 49, // 16: openshell.v1.Sandbox.status:type_name -> openshell.v1.SandboxStatus 48, // 17: openshell.v1.Sandbox.created_from_workload_template:type_name -> openshell.v1.SandboxWorkloadTemplateProvenance - 252, // 18: openshell.v1.SandboxSpec.environment:type_name -> openshell.v1.SandboxSpec.EnvironmentEntry + 253, // 18: openshell.v1.SandboxSpec.environment:type_name -> openshell.v1.SandboxSpec.EnvironmentEntry 41, // 19: openshell.v1.SandboxSpec.template:type_name -> openshell.v1.SandboxTemplate - 277, // 20: openshell.v1.SandboxSpec.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 278, // 20: openshell.v1.SandboxSpec.policy:type_name -> openshell.sandbox.v1.SandboxPolicy 39, // 21: openshell.v1.SandboxSpec.resource_requirements:type_name -> openshell.v1.ResourceRequirements 16, // 22: openshell.v1.SandboxSpec.restart_policy:type_name -> openshell.v1.SandboxRestartPolicy 40, // 23: openshell.v1.ResourceRequirements.gpu:type_name -> openshell.v1.GpuResourceRequirements - 253, // 24: openshell.v1.SandboxTemplate.labels:type_name -> openshell.v1.SandboxTemplate.LabelsEntry - 254, // 25: openshell.v1.SandboxTemplate.annotations:type_name -> openshell.v1.SandboxTemplate.AnnotationsEntry - 255, // 26: openshell.v1.SandboxTemplate.environment:type_name -> openshell.v1.SandboxTemplate.EnvironmentEntry - 278, // 27: openshell.v1.SandboxTemplate.resources:type_name -> google.protobuf.Struct - 278, // 28: openshell.v1.SandboxTemplate.driver_config:type_name -> google.protobuf.Struct - 276, // 29: openshell.v1.SandboxWorkloadTemplate.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 254, // 24: openshell.v1.SandboxTemplate.labels:type_name -> openshell.v1.SandboxTemplate.LabelsEntry + 255, // 25: openshell.v1.SandboxTemplate.annotations:type_name -> openshell.v1.SandboxTemplate.AnnotationsEntry + 256, // 26: openshell.v1.SandboxTemplate.environment:type_name -> openshell.v1.SandboxTemplate.EnvironmentEntry + 279, // 27: openshell.v1.SandboxTemplate.resources:type_name -> google.protobuf.Struct + 279, // 28: openshell.v1.SandboxTemplate.driver_config:type_name -> google.protobuf.Struct + 277, // 29: openshell.v1.SandboxWorkloadTemplate.metadata:type_name -> openshell.datamodel.v1.ObjectMeta 43, // 30: openshell.v1.SandboxWorkloadTemplate.spec:type_name -> openshell.v1.SandboxWorkloadTemplateSpec 44, // 31: openshell.v1.SandboxWorkloadTemplateSpec.workload:type_name -> openshell.v1.SandboxWorkloadConfig - 278, // 32: openshell.v1.SandboxWorkloadTemplateSpec.driver_config:type_name -> google.protobuf.Struct + 279, // 32: openshell.v1.SandboxWorkloadTemplateSpec.driver_config:type_name -> google.protobuf.Struct 46, // 33: openshell.v1.SandboxWorkloadTemplateSpec.desired_service_level:type_name -> openshell.v1.SandboxServiceLevel - 256, // 34: openshell.v1.SandboxWorkloadConfig.environment:type_name -> openshell.v1.SandboxWorkloadConfig.EnvironmentEntry + 257, // 34: openshell.v1.SandboxWorkloadConfig.environment:type_name -> openshell.v1.SandboxWorkloadConfig.EnvironmentEntry 45, // 35: openshell.v1.SandboxWorkloadConfig.resources:type_name -> openshell.v1.SandboxResources 40, // 36: openshell.v1.SandboxResources.gpu:type_name -> openshell.v1.GpuResourceRequirements 47, // 37: openshell.v1.SandboxServiceLevel.startup:type_name -> openshell.v1.SandboxStartup - 279, // 38: openshell.v1.SandboxStartup.ready_within:type_name -> google.protobuf.Duration + 280, // 38: openshell.v1.SandboxStartup.ready_within:type_name -> google.protobuf.Duration 50, // 39: openshell.v1.SandboxStatus.conditions:type_name -> openshell.v1.SandboxCondition 1, // 40: openshell.v1.SandboxStatus.phase:type_name -> openshell.v1.SandboxPhase - 249, // 41: openshell.v1.SandboxStatus.endpoint_statuses:type_name -> openshell.v1.EndpointStatus + 250, // 41: openshell.v1.SandboxStatus.endpoint_statuses:type_name -> openshell.v1.EndpointStatus 176, // 42: openshell.v1.SandboxStatus.configuration_admission:type_name -> openshell.v1.SandboxConfigurationAdmission - 250, // 43: openshell.v1.SandboxStatus.provisioning:type_name -> openshell.v1.SandboxProvisioning - 275, // 44: openshell.v1.SandboxStatus.next_restart_time:type_name -> google.protobuf.Timestamp - 275, // 45: openshell.v1.SandboxStatus.main_process_started_time:type_name -> google.protobuf.Timestamp - 275, // 46: openshell.v1.SandboxCondition.transition_time:type_name -> google.protobuf.Timestamp - 275, // 47: openshell.v1.PlatformEvent.event_time:type_name -> google.protobuf.Timestamp - 257, // 48: openshell.v1.PlatformEvent.metadata:type_name -> openshell.v1.PlatformEvent.MetadataEntry - 280, // 49: openshell.v1.CreateSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 251, // 43: openshell.v1.SandboxStatus.provisioning:type_name -> openshell.v1.SandboxProvisioning + 276, // 44: openshell.v1.SandboxStatus.next_restart_time:type_name -> google.protobuf.Timestamp + 276, // 45: openshell.v1.SandboxStatus.main_process_started_time:type_name -> google.protobuf.Timestamp + 276, // 46: openshell.v1.SandboxCondition.transition_time:type_name -> google.protobuf.Timestamp + 276, // 47: openshell.v1.PlatformEvent.event_time:type_name -> google.protobuf.Timestamp + 258, // 48: openshell.v1.PlatformEvent.metadata:type_name -> openshell.v1.PlatformEvent.MetadataEntry + 281, // 49: openshell.v1.CreateSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 38, // 50: openshell.v1.CreateSandboxRequest.spec:type_name -> openshell.v1.SandboxSpec - 258, // 51: openshell.v1.CreateSandboxRequest.labels:type_name -> openshell.v1.CreateSandboxRequest.LabelsEntry - 259, // 52: openshell.v1.CreateSandboxRequest.annotations:type_name -> openshell.v1.CreateSandboxRequest.AnnotationsEntry - 251, // 53: openshell.v1.CreateSandboxRequest.service_exposures:type_name -> openshell.v1.SandboxServiceExposure - 280, // 54: openshell.v1.CreateSandboxTemplateRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 259, // 51: openshell.v1.CreateSandboxRequest.labels:type_name -> openshell.v1.CreateSandboxRequest.LabelsEntry + 260, // 52: openshell.v1.CreateSandboxRequest.annotations:type_name -> openshell.v1.CreateSandboxRequest.AnnotationsEntry + 252, // 53: openshell.v1.CreateSandboxRequest.service_exposures:type_name -> openshell.v1.SandboxServiceExposure + 281, // 54: openshell.v1.CreateSandboxTemplateRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 42, // 55: openshell.v1.CreateSandboxTemplateRequest.template:type_name -> openshell.v1.SandboxWorkloadTemplate - 280, // 56: openshell.v1.GetSandboxTemplateRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 57: openshell.v1.ListSandboxTemplatesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 58: openshell.v1.DeleteSandboxTemplateRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 56: openshell.v1.GetSandboxTemplateRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 57: openshell.v1.ListSandboxTemplatesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 58: openshell.v1.DeleteSandboxTemplateRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 42, // 59: openshell.v1.SandboxTemplateResponse.template:type_name -> openshell.v1.SandboxWorkloadTemplate 42, // 60: openshell.v1.ListSandboxTemplatesResponse.templates:type_name -> openshell.v1.SandboxWorkloadTemplate 17, // 61: openshell.v1.DeleteSandboxTemplateResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 280, // 62: openshell.v1.BeginRootfsTarStagingRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 275, // 63: openshell.v1.BeginRootfsTarStagingResponse.expiration_time:type_name -> google.protobuf.Timestamp - 280, // 64: openshell.v1.GetSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 65: openshell.v1.ListSandboxesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 66: openshell.v1.ListSandboxProvidersRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 67: openshell.v1.AttachSandboxProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 68: openshell.v1.DetachSandboxProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 69: openshell.v1.DeleteSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 70: openshell.v1.StopSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 71: openshell.v1.StartSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 62: openshell.v1.BeginRootfsTarStagingRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 276, // 63: openshell.v1.BeginRootfsTarStagingResponse.expiration_time:type_name -> google.protobuf.Timestamp + 281, // 64: openshell.v1.GetSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 65: openshell.v1.ListSandboxesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 66: openshell.v1.ListSandboxProvidersRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 67: openshell.v1.AttachSandboxProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 68: openshell.v1.DetachSandboxProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 69: openshell.v1.DeleteSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 70: openshell.v1.StopSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 71: openshell.v1.StartSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 37, // 72: openshell.v1.SandboxResponse.sandbox:type_name -> openshell.v1.Sandbox - 260, // 73: openshell.v1.SandboxResponse.service_urls:type_name -> openshell.v1.SandboxResponse.ServiceUrlsEntry + 261, // 73: openshell.v1.SandboxResponse.service_urls:type_name -> openshell.v1.SandboxResponse.ServiceUrlsEntry 37, // 74: openshell.v1.ListSandboxesResponse.sandboxes:type_name -> openshell.v1.Sandbox - 281, // 75: openshell.v1.ListSandboxProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider + 282, // 75: openshell.v1.ListSandboxProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider 37, // 76: openshell.v1.AttachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox 79, // 77: openshell.v1.AttachSandboxProviderResponse.receipt:type_name -> openshell.v1.ProviderMutationReceipt 37, // 78: openshell.v1.DetachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox 79, // 79: openshell.v1.DetachSandboxProviderResponse.receipt:type_name -> openshell.v1.ProviderMutationReceipt 77, // 80: openshell.v1.ConfigSnapshotRevision.sandbox_config:type_name -> openshell.v1.SandboxConfigRevision 75, // 81: openshell.v1.ConfigSnapshotRevision.provider_target:type_name -> openshell.v1.ProviderDesiredIdentity - 282, // 82: openshell.v1.SandboxConfigRevision.policy_source:type_name -> openshell.sandbox.v1.PolicySource + 283, // 82: openshell.v1.SandboxConfigRevision.policy_source:type_name -> openshell.sandbox.v1.PolicySource 5, // 83: openshell.v1.ConfigUpdateOperation.component:type_name -> openshell.v1.ConfigComponent 76, // 84: openshell.v1.ConfigUpdateOperation.target_revision:type_name -> openshell.v1.ConfigSnapshotRevision 7, // 85: openshell.v1.ConfigUpdateOperation.state:type_name -> openshell.v1.ConfigUpdateOperationState 6, // 86: openshell.v1.ConfigUpdateOperation.outcome:type_name -> openshell.v1.ConfigApplyOutcome - 275, // 87: openshell.v1.ConfigUpdateOperation.created_time:type_name -> google.protobuf.Timestamp - 275, // 88: openshell.v1.ConfigUpdateOperation.updated_time:type_name -> google.protobuf.Timestamp - 275, // 89: openshell.v1.ConfigUpdateOperation.completed_time:type_name -> google.protobuf.Timestamp + 276, // 87: openshell.v1.ConfigUpdateOperation.created_time:type_name -> google.protobuf.Timestamp + 276, // 88: openshell.v1.ConfigUpdateOperation.updated_time:type_name -> google.protobuf.Timestamp + 276, // 89: openshell.v1.ConfigUpdateOperation.completed_time:type_name -> google.protobuf.Timestamp 2, // 90: openshell.v1.ProviderMutationReceipt.kind:type_name -> openshell.v1.ProviderMutationKind 75, // 91: openshell.v1.ProviderMutationReceipt.desired:type_name -> openshell.v1.ProviderDesiredIdentity - 275, // 92: openshell.v1.ProviderMutationReceipt.persisted_time:type_name -> google.protobuf.Timestamp + 276, // 92: openshell.v1.ProviderMutationReceipt.persisted_time:type_name -> google.protobuf.Timestamp 4, // 93: openshell.v1.ProviderReadinessObservation.reason:type_name -> openshell.v1.ProviderReadinessReason 79, // 94: openshell.v1.ProviderReadinessStatus.receipt:type_name -> openshell.v1.ProviderMutationReceipt 3, // 95: openshell.v1.ProviderReadinessStatus.state:type_name -> openshell.v1.ProviderReadinessState 4, // 96: openshell.v1.ProviderReadinessStatus.reason:type_name -> openshell.v1.ProviderReadinessReason 80, // 97: openshell.v1.ProviderReadinessStatus.observed:type_name -> openshell.v1.ProviderReadinessObservation - 275, // 98: openshell.v1.ProviderReadinessStatus.observed_time:type_name -> google.protobuf.Timestamp - 275, // 99: openshell.v1.ProviderReadinessStatus.evaluated_time:type_name -> google.protobuf.Timestamp + 276, // 98: openshell.v1.ProviderReadinessStatus.observed_time:type_name -> google.protobuf.Timestamp + 276, // 99: openshell.v1.ProviderReadinessStatus.evaluated_time:type_name -> google.protobuf.Timestamp 78, // 100: openshell.v1.ProviderReadinessStatus.operation:type_name -> openshell.v1.ConfigUpdateOperation - 280, // 101: openshell.v1.GetSandboxProviderStatusRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 101: openshell.v1.GetSandboxProviderStatusRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 81, // 102: openshell.v1.GetSandboxProviderStatusResponse.status:type_name -> openshell.v1.ProviderReadinessStatus 80, // 103: openshell.v1.ReportProviderReadinessRequest.observation:type_name -> openshell.v1.ProviderReadinessObservation - 279, // 104: openshell.v1.ReportProviderReadinessResponse.report_interval:type_name -> google.protobuf.Duration - 279, // 105: openshell.v1.ReportProviderReadinessResponse.observation_ttl:type_name -> google.protobuf.Duration + 280, // 104: openshell.v1.ReportProviderReadinessResponse.report_interval:type_name -> google.protobuf.Duration + 280, // 105: openshell.v1.ReportProviderReadinessResponse.observation_ttl:type_name -> google.protobuf.Duration 17, // 106: openshell.v1.DeleteSandboxResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 280, // 107: openshell.v1.CreateSshSessionRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 275, // 108: openshell.v1.CreateSshSessionResponse.expiration_time:type_name -> google.protobuf.Timestamp - 280, // 109: openshell.v1.ExposeServiceRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 107: openshell.v1.CreateSshSessionRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 276, // 108: openshell.v1.CreateSshSessionResponse.expiration_time:type_name -> google.protobuf.Timestamp + 281, // 109: openshell.v1.ExposeServiceRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 19, // 110: openshell.v1.ExposeServiceRequest.authorization_mode:type_name -> openshell.v1.ServiceAuthorizationMode - 280, // 111: openshell.v1.GetServiceRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 112: openshell.v1.ListServicesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 111: openshell.v1.GetServiceRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 112: openshell.v1.ListServicesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 96, // 113: openshell.v1.ListServicesResponse.services:type_name -> openshell.v1.ServiceEndpointResponse - 280, // 114: openshell.v1.DeleteServiceRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 114: openshell.v1.DeleteServiceRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 17, // 115: openshell.v1.DeleteServiceResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 276, // 116: openshell.v1.ServiceEndpoint.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 277, // 116: openshell.v1.ServiceEndpoint.metadata:type_name -> openshell.datamodel.v1.ObjectMeta 19, // 117: openshell.v1.ServiceEndpoint.authorization_mode:type_name -> openshell.v1.ServiceAuthorizationMode 95, // 118: openshell.v1.ServiceEndpointResponse.endpoint:type_name -> openshell.v1.ServiceEndpoint 17, // 119: openshell.v1.RevokeSshSessionResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 280, // 120: openshell.v1.ExecSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 261, // 121: openshell.v1.ExecSandboxRequest.environment:type_name -> openshell.v1.ExecSandboxRequest.EnvironmentEntry - 279, // 122: openshell.v1.ExecSandboxRequest.execution_timeout:type_name -> google.protobuf.Duration + 281, // 120: openshell.v1.ExecSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 262, // 121: openshell.v1.ExecSandboxRequest.environment:type_name -> openshell.v1.ExecSandboxRequest.EnvironmentEntry + 280, // 122: openshell.v1.ExecSandboxRequest.execution_timeout:type_name -> google.protobuf.Duration 100, // 123: openshell.v1.ExecSandboxEvent.stdout:type_name -> openshell.v1.ExecSandboxStdout 101, // 124: openshell.v1.ExecSandboxEvent.stderr:type_name -> openshell.v1.ExecSandboxStderr 102, // 125: openshell.v1.ExecSandboxEvent.exit:type_name -> openshell.v1.ExecSandboxExit - 196, // 126: openshell.v1.TcpForwardInit.ssh:type_name -> openshell.v1.SshRelayTarget - 197, // 127: openshell.v1.TcpForwardInit.tcp:type_name -> openshell.v1.TcpRelayTarget + 197, // 126: openshell.v1.TcpForwardInit.ssh:type_name -> openshell.v1.SshRelayTarget + 198, // 127: openshell.v1.TcpForwardInit.tcp:type_name -> openshell.v1.TcpRelayTarget 104, // 128: openshell.v1.TcpForwardFrame.init:type_name -> openshell.v1.TcpForwardInit 99, // 129: openshell.v1.ExecSandboxInput.start:type_name -> openshell.v1.ExecSandboxRequest 107, // 130: openshell.v1.ExecSandboxInput.resize:type_name -> openshell.v1.ExecSandboxWindowResize - 276, // 131: openshell.v1.SshSession.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 275, // 132: openshell.v1.SshSession.expiration_time:type_name -> google.protobuf.Timestamp - 280, // 133: openshell.v1.WatchSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 275, // 134: openshell.v1.WatchSandboxRequest.since_time:type_name -> google.protobuf.Timestamp + 277, // 131: openshell.v1.SshSession.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 276, // 132: openshell.v1.SshSession.expiration_time:type_name -> google.protobuf.Timestamp + 281, // 133: openshell.v1.WatchSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 276, // 134: openshell.v1.WatchSandboxRequest.since_time:type_name -> google.protobuf.Timestamp 37, // 135: openshell.v1.SandboxStreamEvent.sandbox:type_name -> openshell.v1.Sandbox 111, // 136: openshell.v1.SandboxStreamEvent.log:type_name -> openshell.v1.SandboxLogLine 51, // 137: openshell.v1.SandboxStreamEvent.event:type_name -> openshell.v1.PlatformEvent 112, // 138: openshell.v1.SandboxStreamEvent.warning:type_name -> openshell.v1.SandboxStreamWarning - 209, // 139: openshell.v1.SandboxStreamEvent.draft_policy_update:type_name -> openshell.v1.DraftPolicyUpdate - 275, // 140: openshell.v1.SandboxLogLine.event_time:type_name -> google.protobuf.Timestamp - 262, // 141: openshell.v1.SandboxLogLine.fields:type_name -> openshell.v1.SandboxLogLine.FieldsEntry - 280, // 142: openshell.v1.CreateProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 281, // 143: openshell.v1.CreateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider - 280, // 144: openshell.v1.GetProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 145: openshell.v1.ListProvidersRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 146: openshell.v1.UpdateProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 281, // 147: openshell.v1.UpdateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider - 263, // 148: openshell.v1.UpdateProviderRequest.credential_expiration_times:type_name -> openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry - 280, // 149: openshell.v1.DeleteProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 281, // 150: openshell.v1.ProviderResponse.provider:type_name -> openshell.datamodel.v1.Provider + 210, // 139: openshell.v1.SandboxStreamEvent.draft_policy_update:type_name -> openshell.v1.DraftPolicyUpdate + 276, // 140: openshell.v1.SandboxLogLine.event_time:type_name -> google.protobuf.Timestamp + 263, // 141: openshell.v1.SandboxLogLine.fields:type_name -> openshell.v1.SandboxLogLine.FieldsEntry + 281, // 142: openshell.v1.CreateProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 282, // 143: openshell.v1.CreateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider + 281, // 144: openshell.v1.GetProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 145: openshell.v1.ListProvidersRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 146: openshell.v1.UpdateProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 282, // 147: openshell.v1.UpdateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider + 264, // 148: openshell.v1.UpdateProviderRequest.credential_expiration_times:type_name -> openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry + 281, // 149: openshell.v1.DeleteProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 282, // 150: openshell.v1.ProviderResponse.provider:type_name -> openshell.datamodel.v1.Provider 79, // 151: openshell.v1.ProviderResponse.target_receipts:type_name -> openshell.v1.ProviderMutationReceipt - 281, // 152: openshell.v1.ListProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider - 280, // 153: openshell.v1.ListProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 154: openshell.v1.GetProviderProfileRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 282, // 152: openshell.v1.ListProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider + 281, // 153: openshell.v1.ListProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 154: openshell.v1.GetProviderProfileRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 141, // 155: openshell.v1.ProviderProfileImportItem.profile:type_name -> openshell.v1.ProviderProfile - 279, // 156: openshell.v1.ProviderCredentialTokenGrant.cache_ttl:type_name -> google.protobuf.Duration + 280, // 156: openshell.v1.ProviderCredentialTokenGrant.cache_ttl:type_name -> google.protobuf.Duration 124, // 157: openshell.v1.ProviderCredentialTokenGrant.audience_overrides:type_name -> openshell.v1.ProviderCredentialTokenGrantAudienceOverride 8, // 158: openshell.v1.ProviderCredentialTokenGrant.grant_type:type_name -> openshell.v1.ProviderCredentialTokenGrantType 125, // 159: openshell.v1.ProviderCredentialTokenGrant.subject_token:type_name -> openshell.v1.ProviderCredentialTokenGrantSubjectToken 130, // 160: openshell.v1.ProviderProfileCredential.refresh:type_name -> openshell.v1.ProviderCredentialRefresh 126, // 161: openshell.v1.ProviderProfileCredential.token_grant:type_name -> openshell.v1.ProviderCredentialTokenGrant 9, // 162: openshell.v1.ProviderCredentialRefresh.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 279, // 163: openshell.v1.ProviderCredentialRefresh.refresh_before:type_name -> google.protobuf.Duration - 279, // 164: openshell.v1.ProviderCredentialRefresh.max_lifetime:type_name -> google.protobuf.Duration + 280, // 163: openshell.v1.ProviderCredentialRefresh.refresh_before:type_name -> google.protobuf.Duration + 280, // 164: openshell.v1.ProviderCredentialRefresh.max_lifetime:type_name -> google.protobuf.Duration 128, // 165: openshell.v1.ProviderCredentialRefresh.material:type_name -> openshell.v1.ProviderCredentialRefreshMaterial 129, // 166: openshell.v1.ProviderCredentialRefresh.additional_outputs:type_name -> openshell.v1.ProviderCredentialRefreshOutput 9, // 167: openshell.v1.ProviderCredentialRefreshStatus.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 275, // 168: openshell.v1.ProviderCredentialRefreshStatus.expiration_time:type_name -> google.protobuf.Timestamp - 275, // 169: openshell.v1.ProviderCredentialRefreshStatus.next_refresh_time:type_name -> google.protobuf.Timestamp - 275, // 170: openshell.v1.ProviderCredentialRefreshStatus.last_refresh_time:type_name -> google.protobuf.Timestamp + 276, // 168: openshell.v1.ProviderCredentialRefreshStatus.expiration_time:type_name -> google.protobuf.Timestamp + 276, // 169: openshell.v1.ProviderCredentialRefreshStatus.next_refresh_time:type_name -> google.protobuf.Timestamp + 276, // 170: openshell.v1.ProviderCredentialRefreshStatus.last_refresh_time:type_name -> google.protobuf.Timestamp 15, // 171: openshell.v1.ProviderCredentialRefreshStatus.recovery_action:type_name -> openshell.v1.ProviderCredentialRefreshRecoveryAction - 275, // 172: openshell.v1.ProviderCredentialRefreshStatus.last_error_time:type_name -> google.protobuf.Timestamp - 280, // 173: openshell.v1.GetProviderRefreshStatusRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 276, // 172: openshell.v1.ProviderCredentialRefreshStatus.last_error_time:type_name -> google.protobuf.Timestamp + 281, // 173: openshell.v1.GetProviderRefreshStatusRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 131, // 174: openshell.v1.GetProviderRefreshStatusResponse.credentials:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 280, // 175: openshell.v1.ConfigureProviderRefreshRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 175: openshell.v1.ConfigureProviderRefreshRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 9, // 176: openshell.v1.ConfigureProviderRefreshRequest.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 264, // 177: openshell.v1.ConfigureProviderRefreshRequest.material:type_name -> openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry - 275, // 178: openshell.v1.ConfigureProviderRefreshRequest.expiration_time:type_name -> google.protobuf.Timestamp + 265, // 177: openshell.v1.ConfigureProviderRefreshRequest.material:type_name -> openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry + 276, // 178: openshell.v1.ConfigureProviderRefreshRequest.expiration_time:type_name -> google.protobuf.Timestamp 131, // 179: openshell.v1.ConfigureProviderRefreshResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 280, // 180: openshell.v1.RotateProviderCredentialRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 180: openshell.v1.RotateProviderCredentialRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 131, // 181: openshell.v1.RotateProviderCredentialResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 280, // 182: openshell.v1.DeleteProviderRefreshRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 182: openshell.v1.DeleteProviderRefreshRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 17, // 183: openshell.v1.DeleteProviderRefreshResponse.outcome:type_name -> openshell.v1.DeletionOutcome 10, // 184: openshell.v1.ProviderProfile.category:type_name -> openshell.v1.ProviderProfileCategory 127, // 185: openshell.v1.ProviderProfile.credentials:type_name -> openshell.v1.ProviderProfileCredential - 283, // 186: openshell.v1.ProviderProfile.endpoints:type_name -> openshell.sandbox.v1.NetworkEndpoint - 284, // 187: openshell.v1.ProviderProfile.binaries:type_name -> openshell.sandbox.v1.NetworkBinary + 284, // 186: openshell.v1.ProviderProfile.endpoints:type_name -> openshell.sandbox.v1.NetworkEndpoint + 285, // 187: openshell.v1.ProviderProfile.binaries:type_name -> openshell.sandbox.v1.NetworkBinary 132, // 188: openshell.v1.ProviderProfile.discovery:type_name -> openshell.v1.ProviderProfileDiscovery - 265, // 189: openshell.v1.ProviderProfile.annotations:type_name -> openshell.v1.ProviderProfile.AnnotationsEntry + 266, // 189: openshell.v1.ProviderProfile.annotations:type_name -> openshell.v1.ProviderProfile.AnnotationsEntry 142, // 190: openshell.v1.ProviderProfile.files:type_name -> openshell.v1.ProviderProfileFile 141, // 191: openshell.v1.ProviderProfileResponse.profile:type_name -> openshell.v1.ProviderProfile 141, // 192: openshell.v1.ListProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile - 280, // 193: openshell.v1.ImportProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 193: openshell.v1.ImportProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 122, // 194: openshell.v1.ImportProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem 123, // 195: openshell.v1.ImportProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic 141, // 196: openshell.v1.ImportProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile - 280, // 197: openshell.v1.UpdateProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 197: openshell.v1.UpdateProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 122, // 198: openshell.v1.UpdateProviderProfilesRequest.profile:type_name -> openshell.v1.ProviderProfileImportItem 123, // 199: openshell.v1.UpdateProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic 141, // 200: openshell.v1.UpdateProviderProfilesResponse.profile:type_name -> openshell.v1.ProviderProfile - 280, // 201: openshell.v1.LintProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 201: openshell.v1.LintProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 122, // 202: openshell.v1.LintProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem 123, // 203: openshell.v1.LintProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic 17, // 204: openshell.v1.DeleteProviderResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 280, // 205: openshell.v1.DeleteProviderProfileRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 205: openshell.v1.DeleteProviderProfileRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 17, // 206: openshell.v1.DeleteProviderProfileResponse.outcome:type_name -> openshell.v1.DeletionOutcome 155, // 207: openshell.v1.StaticCredentialBinding.endpoints:type_name -> openshell.v1.StaticCredentialEndpointBinding - 266, // 208: openshell.v1.GetSandboxProviderEnvironmentResponse.environment:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry - 267, // 209: openshell.v1.GetSandboxProviderEnvironmentResponse.credential_expiration_times:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry - 268, // 210: openshell.v1.GetSandboxProviderEnvironmentResponse.dynamic_credentials:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry - 269, // 211: openshell.v1.GetSandboxProviderEnvironmentResponse.static_credential_bindings:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry + 267, // 208: openshell.v1.GetSandboxProviderEnvironmentResponse.environment:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry + 268, // 209: openshell.v1.GetSandboxProviderEnvironmentResponse.credential_expiration_times:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry + 269, // 210: openshell.v1.GetSandboxProviderEnvironmentResponse.dynamic_credentials:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry + 270, // 211: openshell.v1.GetSandboxProviderEnvironmentResponse.static_credential_bindings:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry 4, // 212: openshell.v1.GetSandboxProviderEnvironmentResponse.readiness_reason:type_name -> openshell.v1.ProviderReadinessReason - 270, // 213: openshell.v1.GetSandboxProviderEnvironmentResponse.files:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.FilesEntry - 279, // 214: openshell.v1.ExchangeProviderSubjectTokenResponse.expires_after:type_name -> google.protobuf.Duration - 280, // 215: openshell.v1.UpdateConfigRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 277, // 216: openshell.v1.UpdateConfigRequest.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 285, // 217: openshell.v1.UpdateConfigRequest.setting_value:type_name -> openshell.sandbox.v1.SettingValue + 271, // 213: openshell.v1.GetSandboxProviderEnvironmentResponse.files:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.FilesEntry + 280, // 214: openshell.v1.ExchangeProviderSubjectTokenResponse.expires_after:type_name -> google.protobuf.Duration + 281, // 215: openshell.v1.UpdateConfigRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 278, // 216: openshell.v1.UpdateConfigRequest.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 286, // 217: openshell.v1.UpdateConfigRequest.setting_value:type_name -> openshell.sandbox.v1.SettingValue 161, // 218: openshell.v1.UpdateConfigRequest.merge_operations:type_name -> openshell.v1.PolicyMergeOperation - 271, // 219: openshell.v1.UpdateConfigRequest.annotations:type_name -> openshell.v1.UpdateConfigRequest.AnnotationsEntry + 272, // 219: openshell.v1.UpdateConfigRequest.annotations:type_name -> openshell.v1.UpdateConfigRequest.AnnotationsEntry 162, // 220: openshell.v1.PolicyMergeOperation.add_rule:type_name -> openshell.v1.AddNetworkRule 163, // 221: openshell.v1.PolicyMergeOperation.remove_endpoint:type_name -> openshell.v1.RemoveNetworkEndpoint 164, // 222: openshell.v1.PolicyMergeOperation.remove_rule:type_name -> openshell.v1.RemoveNetworkRule 166, // 223: openshell.v1.PolicyMergeOperation.add_deny_rules:type_name -> openshell.v1.AddDenyRules 167, // 224: openshell.v1.PolicyMergeOperation.add_allow_rules:type_name -> openshell.v1.AddAllowRules 168, // 225: openshell.v1.PolicyMergeOperation.remove_binary:type_name -> openshell.v1.RemoveNetworkBinary - 286, // 226: openshell.v1.AddNetworkRule.rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 284, // 227: openshell.v1.L7RuleTarget.binaries:type_name -> openshell.sandbox.v1.NetworkBinary - 287, // 228: openshell.v1.AddDenyRules.deny_rules:type_name -> openshell.sandbox.v1.L7DenyRule + 287, // 226: openshell.v1.AddNetworkRule.rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 285, // 227: openshell.v1.L7RuleTarget.binaries:type_name -> openshell.sandbox.v1.NetworkBinary + 288, // 228: openshell.v1.AddDenyRules.deny_rules:type_name -> openshell.sandbox.v1.L7DenyRule 165, // 229: openshell.v1.AddDenyRules.target:type_name -> openshell.v1.L7RuleTarget - 288, // 230: openshell.v1.AddAllowRules.rules:type_name -> openshell.sandbox.v1.L7Rule + 289, // 230: openshell.v1.AddAllowRules.rules:type_name -> openshell.sandbox.v1.L7Rule 165, // 231: openshell.v1.AddAllowRules.target:type_name -> openshell.v1.L7RuleTarget - 272, // 232: openshell.v1.UpdateConfigResponse.annotations:type_name -> openshell.v1.UpdateConfigResponse.AnnotationsEntry - 280, // 233: openshell.v1.GetSandboxPolicyStatusRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 273, // 232: openshell.v1.UpdateConfigResponse.annotations:type_name -> openshell.v1.UpdateConfigResponse.AnnotationsEntry + 281, // 233: openshell.v1.GetSandboxPolicyStatusRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 179, // 234: openshell.v1.GetSandboxPolicyStatusResponse.revision:type_name -> openshell.v1.SandboxPolicyRevision - 280, // 235: openshell.v1.ListSandboxPoliciesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 235: openshell.v1.ListSandboxPoliciesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 179, // 236: openshell.v1.ListSandboxPoliciesResponse.revisions:type_name -> openshell.v1.SandboxPolicyRevision 12, // 237: openshell.v1.ReportPolicyStatusRequest.status:type_name -> openshell.v1.PolicyStatus 11, // 238: openshell.v1.SandboxConfigurationAdmission.state:type_name -> openshell.v1.ConfigurationAdmissionState 176, // 239: openshell.v1.ReportSandboxConfigurationRequest.admission:type_name -> openshell.v1.SandboxConfigurationAdmission 12, // 240: openshell.v1.SandboxPolicyRevision.status:type_name -> openshell.v1.PolicyStatus - 275, // 241: openshell.v1.SandboxPolicyRevision.created_time:type_name -> google.protobuf.Timestamp - 275, // 242: openshell.v1.SandboxPolicyRevision.loaded_time:type_name -> google.protobuf.Timestamp - 277, // 243: openshell.v1.SandboxPolicyRevision.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 273, // 244: openshell.v1.SandboxPolicyRevision.provenance:type_name -> openshell.v1.SandboxPolicyRevision.ProvenanceEntry - 280, // 245: openshell.v1.GetSandboxLogsRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 275, // 246: openshell.v1.GetSandboxLogsRequest.since_time:type_name -> google.protobuf.Timestamp + 276, // 241: openshell.v1.SandboxPolicyRevision.created_time:type_name -> google.protobuf.Timestamp + 276, // 242: openshell.v1.SandboxPolicyRevision.loaded_time:type_name -> google.protobuf.Timestamp + 278, // 243: openshell.v1.SandboxPolicyRevision.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 274, // 244: openshell.v1.SandboxPolicyRevision.provenance:type_name -> openshell.v1.SandboxPolicyRevision.ProvenanceEntry + 281, // 245: openshell.v1.GetSandboxLogsRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 276, // 246: openshell.v1.GetSandboxLogsRequest.since_time:type_name -> google.protobuf.Timestamp 111, // 247: openshell.v1.PushSandboxLogsRequest.logs:type_name -> openshell.v1.SandboxLogLine 111, // 248: openshell.v1.GetSandboxLogsResponse.logs:type_name -> openshell.v1.SandboxLogLine - 186, // 249: openshell.v1.SupervisorMessage.hello:type_name -> openshell.v1.SupervisorHello - 189, // 250: openshell.v1.SupervisorMessage.heartbeat:type_name -> openshell.v1.SupervisorHeartbeat - 202, // 251: openshell.v1.SupervisorMessage.relay_open_result:type_name -> openshell.v1.RelayOpenResult - 203, // 252: openshell.v1.SupervisorMessage.relay_close:type_name -> openshell.v1.RelayClose - 187, // 253: openshell.v1.GatewayMessage.session_accepted:type_name -> openshell.v1.SessionAccepted - 188, // 254: openshell.v1.GatewayMessage.session_rejected:type_name -> openshell.v1.SessionRejected - 190, // 255: openshell.v1.GatewayMessage.heartbeat:type_name -> openshell.v1.GatewayHeartbeat - 195, // 256: openshell.v1.GatewayMessage.relay_open:type_name -> openshell.v1.RelayOpen - 203, // 257: openshell.v1.GatewayMessage.relay_close:type_name -> openshell.v1.RelayClose - 279, // 258: openshell.v1.SessionAccepted.heartbeat_interval:type_name -> google.protobuf.Duration - 196, // 259: openshell.v1.RelayOpen.ssh:type_name -> openshell.v1.SshRelayTarget - 197, // 260: openshell.v1.RelayOpen.tcp:type_name -> openshell.v1.TcpRelayTarget - 198, // 261: openshell.v1.RelayFrame.init:type_name -> openshell.v1.RelayInit - 195, // 262: openshell.v1.PeerRelayInit.relay_open:type_name -> openshell.v1.RelayOpen - 200, // 263: openshell.v1.PeerRelayFrame.init:type_name -> openshell.v1.PeerRelayInit - 275, // 264: openshell.v1.DenialSummary.first_seen_time:type_name -> google.protobuf.Timestamp - 275, // 265: openshell.v1.DenialSummary.last_seen_time:type_name -> google.protobuf.Timestamp - 204, // 266: openshell.v1.DenialSummary.l7_request_samples:type_name -> openshell.v1.L7RequestSample - 206, // 267: openshell.v1.NetworkActivitySummary.denials_by_group:type_name -> openshell.v1.DenialGroupCount - 286, // 268: openshell.v1.PolicyChunk.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 275, // 269: openshell.v1.PolicyChunk.created_time:type_name -> google.protobuf.Timestamp - 275, // 270: openshell.v1.PolicyChunk.decided_time:type_name -> google.protobuf.Timestamp - 275, // 271: openshell.v1.PolicyChunk.first_seen_time:type_name -> google.protobuf.Timestamp - 275, // 272: openshell.v1.PolicyChunk.last_seen_time:type_name -> google.protobuf.Timestamp - 277, // 273: openshell.v1.PolicyChunk.current_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 277, // 274: openshell.v1.PolicyChunk.candidate_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 280, // 275: openshell.v1.SubmitPolicyAnalysisRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 205, // 276: openshell.v1.SubmitPolicyAnalysisRequest.summaries:type_name -> openshell.v1.DenialSummary - 208, // 277: openshell.v1.SubmitPolicyAnalysisRequest.proposed_chunks:type_name -> openshell.v1.PolicyChunk - 207, // 278: openshell.v1.SubmitPolicyAnalysisRequest.network_activity_summaries:type_name -> openshell.v1.NetworkActivitySummary - 280, // 279: openshell.v1.GetDraftPolicyRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 208, // 280: openshell.v1.GetDraftPolicyResponse.chunks:type_name -> openshell.v1.PolicyChunk - 275, // 281: openshell.v1.GetDraftPolicyResponse.last_analyzed_time:type_name -> google.protobuf.Timestamp - 280, // 282: openshell.v1.ApproveDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 283: openshell.v1.RejectDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 284: openshell.v1.ApproveAllDraftChunksRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 218, // 285: openshell.v1.ApproveAllDraftChunksRequest.approvals:type_name -> openshell.v1.DraftChunkApproval - 280, // 286: openshell.v1.EditDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 286, // 287: openshell.v1.EditDraftChunkRequest.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 280, // 288: openshell.v1.UndoDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 289: openshell.v1.ClearDraftChunksRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 280, // 290: openshell.v1.GetDraftHistoryRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 275, // 291: openshell.v1.DraftHistoryEntry.event_time:type_name -> google.protobuf.Timestamp - 228, // 292: openshell.v1.GetDraftHistoryResponse.entries:type_name -> openshell.v1.DraftHistoryEntry - 274, // 293: openshell.v1.CreateWorkspaceRequest.labels:type_name -> openshell.v1.CreateWorkspaceRequest.LabelsEntry - 289, // 294: openshell.v1.CreateWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace - 289, // 295: openshell.v1.GetWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace - 289, // 296: openshell.v1.ListWorkspacesResponse.workspaces:type_name -> openshell.datamodel.v1.Workspace - 17, // 297: openshell.v1.DeleteWorkspaceResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 276, // 298: openshell.v1.WorkspaceMember.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 14, // 299: openshell.v1.WorkspaceMember.role:type_name -> openshell.v1.WorkspaceRole - 280, // 300: openshell.v1.AddWorkspaceMemberRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 14, // 301: openshell.v1.AddWorkspaceMemberRequest.role:type_name -> openshell.v1.WorkspaceRole - 238, // 302: openshell.v1.AddWorkspaceMemberResponse.member:type_name -> openshell.v1.WorkspaceMember - 280, // 303: openshell.v1.RemoveWorkspaceMemberRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 17, // 304: openshell.v1.RemoveWorkspaceMemberResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 280, // 305: openshell.v1.ListWorkspaceMembersRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 238, // 306: openshell.v1.ListWorkspaceMembersResponse.members:type_name -> openshell.v1.WorkspaceMember - 275, // 307: openshell.v1.ExtensionServiceCredential.expiration_time:type_name -> google.protobuf.Timestamp - 18, // 308: openshell.v1.EndpointObservation.result:type_name -> openshell.v1.EndpointResult - 246, // 309: openshell.v1.ReportEndpointStatusRequest.observations:type_name -> openshell.v1.EndpointObservation - 18, // 310: openshell.v1.EndpointStatus.last_result:type_name -> openshell.v1.EndpointResult - 275, // 311: openshell.v1.EndpointStatus.last_reported_time:type_name -> google.protobuf.Timestamp - 275, // 312: openshell.v1.SandboxProvisioning.configuration_change_time:type_name -> google.protobuf.Timestamp - 275, // 313: openshell.v1.SandboxProvisioning.first_rejection_time:type_name -> google.protobuf.Timestamp - 275, // 314: openshell.v1.SandboxProvisioning.deadline:type_name -> google.protobuf.Timestamp - 275, // 315: openshell.v1.SandboxProvisioning.timeout_time:type_name -> google.protobuf.Timestamp - 275, // 316: openshell.v1.SandboxProvisioning.cleanup_completed_time:type_name -> google.protobuf.Timestamp - 275, // 317: openshell.v1.SandboxProvisioning.cleanup_retry_time:type_name -> google.protobuf.Timestamp - 275, // 318: openshell.v1.SandboxProvisioning.attachment_change_time:type_name -> google.protobuf.Timestamp - 19, // 319: openshell.v1.SandboxServiceExposure.authorization_mode:type_name -> openshell.v1.ServiceAuthorizationMode - 275, // 320: openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry.value:type_name -> google.protobuf.Timestamp - 275, // 321: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry.value:type_name -> google.protobuf.Timestamp - 127, // 322: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential - 156, // 323: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry.value:type_name -> openshell.v1.StaticCredentialBinding - 24, // 324: openshell.v1.OpenShell.Health:input_type -> openshell.v1.HealthRequest - 26, // 325: openshell.v1.OpenShell.GetCurrentUser:input_type -> openshell.v1.GetCurrentUserRequest - 28, // 326: openshell.v1.OpenShell.GetGatewayInfo:input_type -> openshell.v1.GetGatewayInfoRequest - 52, // 327: openshell.v1.OpenShell.CreateSandbox:input_type -> openshell.v1.CreateSandboxRequest - 60, // 328: openshell.v1.OpenShell.BeginRootfsTarStaging:input_type -> openshell.v1.BeginRootfsTarStagingRequest - 62, // 329: openshell.v1.OpenShell.GetSandbox:input_type -> openshell.v1.GetSandboxRequest - 63, // 330: openshell.v1.OpenShell.ListSandboxes:input_type -> openshell.v1.ListSandboxesRequest - 53, // 331: openshell.v1.OpenShell.CreateSandboxTemplate:input_type -> openshell.v1.CreateSandboxTemplateRequest - 54, // 332: openshell.v1.OpenShell.GetSandboxTemplate:input_type -> openshell.v1.GetSandboxTemplateRequest - 55, // 333: openshell.v1.OpenShell.ListSandboxTemplates:input_type -> openshell.v1.ListSandboxTemplatesRequest - 56, // 334: openshell.v1.OpenShell.DeleteSandboxTemplate:input_type -> openshell.v1.DeleteSandboxTemplateRequest - 64, // 335: openshell.v1.OpenShell.ListSandboxProviders:input_type -> openshell.v1.ListSandboxProvidersRequest - 65, // 336: openshell.v1.OpenShell.AttachSandboxProvider:input_type -> openshell.v1.AttachSandboxProviderRequest - 66, // 337: openshell.v1.OpenShell.DetachSandboxProvider:input_type -> openshell.v1.DetachSandboxProviderRequest - 82, // 338: openshell.v1.OpenShell.GetSandboxProviderStatus:input_type -> openshell.v1.GetSandboxProviderStatusRequest - 67, // 339: openshell.v1.OpenShell.DeleteSandbox:input_type -> openshell.v1.DeleteSandboxRequest - 68, // 340: openshell.v1.OpenShell.StopSandbox:input_type -> openshell.v1.StopSandboxRequest - 69, // 341: openshell.v1.OpenShell.StartSandbox:input_type -> openshell.v1.StartSandboxRequest - 87, // 342: openshell.v1.OpenShell.CreateSshSession:input_type -> openshell.v1.CreateSshSessionRequest - 89, // 343: openshell.v1.OpenShell.ExposeService:input_type -> openshell.v1.ExposeServiceRequest - 90, // 344: openshell.v1.OpenShell.GetService:input_type -> openshell.v1.GetServiceRequest - 91, // 345: openshell.v1.OpenShell.ListServices:input_type -> openshell.v1.ListServicesRequest - 93, // 346: openshell.v1.OpenShell.DeleteService:input_type -> openshell.v1.DeleteServiceRequest - 97, // 347: openshell.v1.OpenShell.RevokeSshSession:input_type -> openshell.v1.RevokeSshSessionRequest - 99, // 348: openshell.v1.OpenShell.ExecSandbox:input_type -> openshell.v1.ExecSandboxRequest - 105, // 349: openshell.v1.OpenShell.ForwardTcp:input_type -> openshell.v1.TcpForwardFrame - 106, // 350: openshell.v1.OpenShell.ExecSandboxInteractive:input_type -> openshell.v1.ExecSandboxInput - 113, // 351: openshell.v1.OpenShell.CreateProvider:input_type -> openshell.v1.CreateProviderRequest - 114, // 352: openshell.v1.OpenShell.GetProvider:input_type -> openshell.v1.GetProviderRequest - 115, // 353: openshell.v1.OpenShell.ListProviders:input_type -> openshell.v1.ListProvidersRequest - 120, // 354: openshell.v1.OpenShell.ListProviderProfiles:input_type -> openshell.v1.ListProviderProfilesRequest - 121, // 355: openshell.v1.OpenShell.GetProviderProfile:input_type -> openshell.v1.GetProviderProfileRequest - 145, // 356: openshell.v1.OpenShell.ImportProviderProfiles:input_type -> openshell.v1.ImportProviderProfilesRequest - 147, // 357: openshell.v1.OpenShell.UpdateProviderProfiles:input_type -> openshell.v1.UpdateProviderProfilesRequest - 149, // 358: openshell.v1.OpenShell.LintProviderProfiles:input_type -> openshell.v1.LintProviderProfilesRequest - 116, // 359: openshell.v1.OpenShell.UpdateProvider:input_type -> openshell.v1.UpdateProviderRequest - 133, // 360: openshell.v1.OpenShell.GetProviderRefreshStatus:input_type -> openshell.v1.GetProviderRefreshStatusRequest - 135, // 361: openshell.v1.OpenShell.ConfigureProviderRefresh:input_type -> openshell.v1.ConfigureProviderRefreshRequest - 137, // 362: openshell.v1.OpenShell.RotateProviderCredential:input_type -> openshell.v1.RotateProviderCredentialRequest - 139, // 363: openshell.v1.OpenShell.DeleteProviderRefresh:input_type -> openshell.v1.DeleteProviderRefreshRequest - 117, // 364: openshell.v1.OpenShell.DeleteProvider:input_type -> openshell.v1.DeleteProviderRequest - 152, // 365: openshell.v1.OpenShell.DeleteProviderProfile:input_type -> openshell.v1.DeleteProviderProfileRequest - 290, // 366: openshell.v1.OpenShell.GetSandboxConfig:input_type -> openshell.sandbox.v1.GetSandboxConfigRequest - 291, // 367: openshell.v1.OpenShell.GetGatewayConfig:input_type -> openshell.sandbox.v1.GetGatewayConfigRequest - 160, // 368: openshell.v1.OpenShell.UpdateConfig:input_type -> openshell.v1.UpdateConfigRequest - 170, // 369: openshell.v1.OpenShell.GetSandboxPolicyStatus:input_type -> openshell.v1.GetSandboxPolicyStatusRequest - 172, // 370: openshell.v1.OpenShell.ListSandboxPolicies:input_type -> openshell.v1.ListSandboxPoliciesRequest - 174, // 371: openshell.v1.OpenShell.ReportPolicyStatus:input_type -> openshell.v1.ReportPolicyStatusRequest - 247, // 372: openshell.v1.OpenShell.ReportEndpointStatus:input_type -> openshell.v1.ReportEndpointStatusRequest - 84, // 373: openshell.v1.OpenShell.ReportProviderReadiness:input_type -> openshell.v1.ReportProviderReadinessRequest - 177, // 374: openshell.v1.OpenShell.ReportSandboxConfiguration:input_type -> openshell.v1.ReportSandboxConfigurationRequest - 154, // 375: openshell.v1.OpenShell.GetSandboxProviderEnvironment:input_type -> openshell.v1.GetSandboxProviderEnvironmentRequest - 158, // 376: openshell.v1.OpenShell.ExchangeProviderSubjectToken:input_type -> openshell.v1.ExchangeProviderSubjectTokenRequest - 180, // 377: openshell.v1.OpenShell.GetSandboxLogs:input_type -> openshell.v1.GetSandboxLogsRequest - 181, // 378: openshell.v1.OpenShell.PushSandboxLogs:input_type -> openshell.v1.PushSandboxLogsRequest - 184, // 379: openshell.v1.OpenShell.ConnectSupervisor:input_type -> openshell.v1.SupervisorMessage - 191, // 380: openshell.v1.OpenShell.ReportMainProcessExit:input_type -> openshell.v1.ReportMainProcessExitRequest - 193, // 381: openshell.v1.OpenShell.FinalizeMainProcessExit:input_type -> openshell.v1.FinalizeMainProcessExitRequest - 199, // 382: openshell.v1.OpenShell.RelayStream:input_type -> openshell.v1.RelayFrame - 201, // 383: openshell.v1.OpenShell.PeerRelay:input_type -> openshell.v1.PeerRelayFrame - 84, // 384: openshell.v1.OpenShell.PeerReportProviderReadiness:input_type -> openshell.v1.ReportProviderReadinessRequest - 247, // 385: openshell.v1.OpenShell.PeerReportEndpointStatus:input_type -> openshell.v1.ReportEndpointStatusRequest - 82, // 386: openshell.v1.OpenShell.PeerGetSandboxProviderStatus:input_type -> openshell.v1.GetSandboxProviderStatusRequest - 109, // 387: openshell.v1.OpenShell.WatchSandbox:input_type -> openshell.v1.WatchSandboxRequest - 210, // 388: openshell.v1.OpenShell.SubmitPolicyAnalysis:input_type -> openshell.v1.SubmitPolicyAnalysisRequest - 212, // 389: openshell.v1.OpenShell.GetDraftPolicy:input_type -> openshell.v1.GetDraftPolicyRequest - 214, // 390: openshell.v1.OpenShell.ApproveDraftChunk:input_type -> openshell.v1.ApproveDraftChunkRequest - 216, // 391: openshell.v1.OpenShell.RejectDraftChunk:input_type -> openshell.v1.RejectDraftChunkRequest - 219, // 392: openshell.v1.OpenShell.ApproveAllDraftChunks:input_type -> openshell.v1.ApproveAllDraftChunksRequest - 221, // 393: openshell.v1.OpenShell.EditDraftChunk:input_type -> openshell.v1.EditDraftChunkRequest - 223, // 394: openshell.v1.OpenShell.UndoDraftChunk:input_type -> openshell.v1.UndoDraftChunkRequest - 225, // 395: openshell.v1.OpenShell.ClearDraftChunks:input_type -> openshell.v1.ClearDraftChunksRequest - 227, // 396: openshell.v1.OpenShell.GetDraftHistory:input_type -> openshell.v1.GetDraftHistoryRequest - 20, // 397: openshell.v1.OpenShell.IssueSandboxToken:input_type -> openshell.v1.IssueSandboxTokenRequest - 22, // 398: openshell.v1.OpenShell.RefreshSandboxToken:input_type -> openshell.v1.RefreshSandboxTokenRequest - 230, // 399: openshell.v1.OpenShell.CreateWorkspace:input_type -> openshell.v1.CreateWorkspaceRequest - 232, // 400: openshell.v1.OpenShell.GetWorkspace:input_type -> openshell.v1.GetWorkspaceRequest - 234, // 401: openshell.v1.OpenShell.ListWorkspaces:input_type -> openshell.v1.ListWorkspacesRequest - 236, // 402: openshell.v1.OpenShell.DeleteWorkspace:input_type -> openshell.v1.DeleteWorkspaceRequest - 239, // 403: openshell.v1.OpenShell.AddWorkspaceMember:input_type -> openshell.v1.AddWorkspaceMemberRequest - 241, // 404: openshell.v1.OpenShell.RemoveWorkspaceMember:input_type -> openshell.v1.RemoveWorkspaceMemberRequest - 243, // 405: openshell.v1.OpenShell.ListWorkspaceMembers:input_type -> openshell.v1.ListWorkspaceMembersRequest - 25, // 406: openshell.v1.OpenShell.Health:output_type -> openshell.v1.HealthResponse - 27, // 407: openshell.v1.OpenShell.GetCurrentUser:output_type -> openshell.v1.GetCurrentUserResponse - 29, // 408: openshell.v1.OpenShell.GetGatewayInfo:output_type -> openshell.v1.GetGatewayInfoResponse - 70, // 409: openshell.v1.OpenShell.CreateSandbox:output_type -> openshell.v1.SandboxResponse - 61, // 410: openshell.v1.OpenShell.BeginRootfsTarStaging:output_type -> openshell.v1.BeginRootfsTarStagingResponse - 70, // 411: openshell.v1.OpenShell.GetSandbox:output_type -> openshell.v1.SandboxResponse - 71, // 412: openshell.v1.OpenShell.ListSandboxes:output_type -> openshell.v1.ListSandboxesResponse - 57, // 413: openshell.v1.OpenShell.CreateSandboxTemplate:output_type -> openshell.v1.SandboxTemplateResponse - 57, // 414: openshell.v1.OpenShell.GetSandboxTemplate:output_type -> openshell.v1.SandboxTemplateResponse - 58, // 415: openshell.v1.OpenShell.ListSandboxTemplates:output_type -> openshell.v1.ListSandboxTemplatesResponse - 59, // 416: openshell.v1.OpenShell.DeleteSandboxTemplate:output_type -> openshell.v1.DeleteSandboxTemplateResponse - 72, // 417: openshell.v1.OpenShell.ListSandboxProviders:output_type -> openshell.v1.ListSandboxProvidersResponse - 73, // 418: openshell.v1.OpenShell.AttachSandboxProvider:output_type -> openshell.v1.AttachSandboxProviderResponse - 74, // 419: openshell.v1.OpenShell.DetachSandboxProvider:output_type -> openshell.v1.DetachSandboxProviderResponse - 83, // 420: openshell.v1.OpenShell.GetSandboxProviderStatus:output_type -> openshell.v1.GetSandboxProviderStatusResponse - 86, // 421: openshell.v1.OpenShell.DeleteSandbox:output_type -> openshell.v1.DeleteSandboxResponse - 70, // 422: openshell.v1.OpenShell.StopSandbox:output_type -> openshell.v1.SandboxResponse - 70, // 423: openshell.v1.OpenShell.StartSandbox:output_type -> openshell.v1.SandboxResponse - 88, // 424: openshell.v1.OpenShell.CreateSshSession:output_type -> openshell.v1.CreateSshSessionResponse - 96, // 425: openshell.v1.OpenShell.ExposeService:output_type -> openshell.v1.ServiceEndpointResponse - 96, // 426: openshell.v1.OpenShell.GetService:output_type -> openshell.v1.ServiceEndpointResponse - 92, // 427: openshell.v1.OpenShell.ListServices:output_type -> openshell.v1.ListServicesResponse - 94, // 428: openshell.v1.OpenShell.DeleteService:output_type -> openshell.v1.DeleteServiceResponse - 98, // 429: openshell.v1.OpenShell.RevokeSshSession:output_type -> openshell.v1.RevokeSshSessionResponse - 103, // 430: openshell.v1.OpenShell.ExecSandbox:output_type -> openshell.v1.ExecSandboxEvent - 105, // 431: openshell.v1.OpenShell.ForwardTcp:output_type -> openshell.v1.TcpForwardFrame - 103, // 432: openshell.v1.OpenShell.ExecSandboxInteractive:output_type -> openshell.v1.ExecSandboxEvent - 118, // 433: openshell.v1.OpenShell.CreateProvider:output_type -> openshell.v1.ProviderResponse - 118, // 434: openshell.v1.OpenShell.GetProvider:output_type -> openshell.v1.ProviderResponse - 119, // 435: openshell.v1.OpenShell.ListProviders:output_type -> openshell.v1.ListProvidersResponse - 144, // 436: openshell.v1.OpenShell.ListProviderProfiles:output_type -> openshell.v1.ListProviderProfilesResponse - 143, // 437: openshell.v1.OpenShell.GetProviderProfile:output_type -> openshell.v1.ProviderProfileResponse - 146, // 438: openshell.v1.OpenShell.ImportProviderProfiles:output_type -> openshell.v1.ImportProviderProfilesResponse - 148, // 439: openshell.v1.OpenShell.UpdateProviderProfiles:output_type -> openshell.v1.UpdateProviderProfilesResponse - 150, // 440: openshell.v1.OpenShell.LintProviderProfiles:output_type -> openshell.v1.LintProviderProfilesResponse - 118, // 441: openshell.v1.OpenShell.UpdateProvider:output_type -> openshell.v1.ProviderResponse - 134, // 442: openshell.v1.OpenShell.GetProviderRefreshStatus:output_type -> openshell.v1.GetProviderRefreshStatusResponse - 136, // 443: openshell.v1.OpenShell.ConfigureProviderRefresh:output_type -> openshell.v1.ConfigureProviderRefreshResponse - 138, // 444: openshell.v1.OpenShell.RotateProviderCredential:output_type -> openshell.v1.RotateProviderCredentialResponse - 140, // 445: openshell.v1.OpenShell.DeleteProviderRefresh:output_type -> openshell.v1.DeleteProviderRefreshResponse - 151, // 446: openshell.v1.OpenShell.DeleteProvider:output_type -> openshell.v1.DeleteProviderResponse - 153, // 447: openshell.v1.OpenShell.DeleteProviderProfile:output_type -> openshell.v1.DeleteProviderProfileResponse - 292, // 448: openshell.v1.OpenShell.GetSandboxConfig:output_type -> openshell.sandbox.v1.GetSandboxConfigResponse - 293, // 449: openshell.v1.OpenShell.GetGatewayConfig:output_type -> openshell.sandbox.v1.GetGatewayConfigResponse - 169, // 450: openshell.v1.OpenShell.UpdateConfig:output_type -> openshell.v1.UpdateConfigResponse - 171, // 451: openshell.v1.OpenShell.GetSandboxPolicyStatus:output_type -> openshell.v1.GetSandboxPolicyStatusResponse - 173, // 452: openshell.v1.OpenShell.ListSandboxPolicies:output_type -> openshell.v1.ListSandboxPoliciesResponse - 175, // 453: openshell.v1.OpenShell.ReportPolicyStatus:output_type -> openshell.v1.ReportPolicyStatusResponse - 248, // 454: openshell.v1.OpenShell.ReportEndpointStatus:output_type -> openshell.v1.ReportEndpointStatusResponse - 85, // 455: openshell.v1.OpenShell.ReportProviderReadiness:output_type -> openshell.v1.ReportProviderReadinessResponse - 178, // 456: openshell.v1.OpenShell.ReportSandboxConfiguration:output_type -> openshell.v1.ReportSandboxConfigurationResponse - 157, // 457: openshell.v1.OpenShell.GetSandboxProviderEnvironment:output_type -> openshell.v1.GetSandboxProviderEnvironmentResponse - 159, // 458: openshell.v1.OpenShell.ExchangeProviderSubjectToken:output_type -> openshell.v1.ExchangeProviderSubjectTokenResponse - 183, // 459: openshell.v1.OpenShell.GetSandboxLogs:output_type -> openshell.v1.GetSandboxLogsResponse - 182, // 460: openshell.v1.OpenShell.PushSandboxLogs:output_type -> openshell.v1.PushSandboxLogsResponse - 185, // 461: openshell.v1.OpenShell.ConnectSupervisor:output_type -> openshell.v1.GatewayMessage - 192, // 462: openshell.v1.OpenShell.ReportMainProcessExit:output_type -> openshell.v1.ReportMainProcessExitResponse - 194, // 463: openshell.v1.OpenShell.FinalizeMainProcessExit:output_type -> openshell.v1.FinalizeMainProcessExitResponse - 199, // 464: openshell.v1.OpenShell.RelayStream:output_type -> openshell.v1.RelayFrame - 201, // 465: openshell.v1.OpenShell.PeerRelay:output_type -> openshell.v1.PeerRelayFrame - 85, // 466: openshell.v1.OpenShell.PeerReportProviderReadiness:output_type -> openshell.v1.ReportProviderReadinessResponse - 248, // 467: openshell.v1.OpenShell.PeerReportEndpointStatus:output_type -> openshell.v1.ReportEndpointStatusResponse - 83, // 468: openshell.v1.OpenShell.PeerGetSandboxProviderStatus:output_type -> openshell.v1.GetSandboxProviderStatusResponse - 110, // 469: openshell.v1.OpenShell.WatchSandbox:output_type -> openshell.v1.SandboxStreamEvent - 211, // 470: openshell.v1.OpenShell.SubmitPolicyAnalysis:output_type -> openshell.v1.SubmitPolicyAnalysisResponse - 213, // 471: openshell.v1.OpenShell.GetDraftPolicy:output_type -> openshell.v1.GetDraftPolicyResponse - 215, // 472: openshell.v1.OpenShell.ApproveDraftChunk:output_type -> openshell.v1.ApproveDraftChunkResponse - 217, // 473: openshell.v1.OpenShell.RejectDraftChunk:output_type -> openshell.v1.RejectDraftChunkResponse - 220, // 474: openshell.v1.OpenShell.ApproveAllDraftChunks:output_type -> openshell.v1.ApproveAllDraftChunksResponse - 222, // 475: openshell.v1.OpenShell.EditDraftChunk:output_type -> openshell.v1.EditDraftChunkResponse - 224, // 476: openshell.v1.OpenShell.UndoDraftChunk:output_type -> openshell.v1.UndoDraftChunkResponse - 226, // 477: openshell.v1.OpenShell.ClearDraftChunks:output_type -> openshell.v1.ClearDraftChunksResponse - 229, // 478: openshell.v1.OpenShell.GetDraftHistory:output_type -> openshell.v1.GetDraftHistoryResponse - 21, // 479: openshell.v1.OpenShell.IssueSandboxToken:output_type -> openshell.v1.IssueSandboxTokenResponse - 23, // 480: openshell.v1.OpenShell.RefreshSandboxToken:output_type -> openshell.v1.RefreshSandboxTokenResponse - 231, // 481: openshell.v1.OpenShell.CreateWorkspace:output_type -> openshell.v1.CreateWorkspaceResponse - 233, // 482: openshell.v1.OpenShell.GetWorkspace:output_type -> openshell.v1.GetWorkspaceResponse - 235, // 483: openshell.v1.OpenShell.ListWorkspaces:output_type -> openshell.v1.ListWorkspacesResponse - 237, // 484: openshell.v1.OpenShell.DeleteWorkspace:output_type -> openshell.v1.DeleteWorkspaceResponse - 240, // 485: openshell.v1.OpenShell.AddWorkspaceMember:output_type -> openshell.v1.AddWorkspaceMemberResponse - 242, // 486: openshell.v1.OpenShell.RemoveWorkspaceMember:output_type -> openshell.v1.RemoveWorkspaceMemberResponse - 244, // 487: openshell.v1.OpenShell.ListWorkspaceMembers:output_type -> openshell.v1.ListWorkspaceMembersResponse - 406, // [406:488] is the sub-list for method output_type - 324, // [324:406] is the sub-list for method input_type - 324, // [324:324] is the sub-list for extension type_name - 324, // [324:324] is the sub-list for extension extendee - 0, // [0:324] is the sub-list for field type_name + 187, // 249: openshell.v1.SupervisorMessage.hello:type_name -> openshell.v1.SupervisorHello + 190, // 250: openshell.v1.SupervisorMessage.heartbeat:type_name -> openshell.v1.SupervisorHeartbeat + 203, // 251: openshell.v1.SupervisorMessage.relay_open_result:type_name -> openshell.v1.RelayOpenResult + 204, // 252: openshell.v1.SupervisorMessage.relay_close:type_name -> openshell.v1.RelayClose + 184, // 253: openshell.v1.SupervisorMessage.otel_export:type_name -> openshell.v1.OtelExportData + 188, // 254: openshell.v1.GatewayMessage.session_accepted:type_name -> openshell.v1.SessionAccepted + 189, // 255: openshell.v1.GatewayMessage.session_rejected:type_name -> openshell.v1.SessionRejected + 191, // 256: openshell.v1.GatewayMessage.heartbeat:type_name -> openshell.v1.GatewayHeartbeat + 196, // 257: openshell.v1.GatewayMessage.relay_open:type_name -> openshell.v1.RelayOpen + 204, // 258: openshell.v1.GatewayMessage.relay_close:type_name -> openshell.v1.RelayClose + 280, // 259: openshell.v1.SessionAccepted.heartbeat_interval:type_name -> google.protobuf.Duration + 197, // 260: openshell.v1.RelayOpen.ssh:type_name -> openshell.v1.SshRelayTarget + 198, // 261: openshell.v1.RelayOpen.tcp:type_name -> openshell.v1.TcpRelayTarget + 199, // 262: openshell.v1.RelayFrame.init:type_name -> openshell.v1.RelayInit + 196, // 263: openshell.v1.PeerRelayInit.relay_open:type_name -> openshell.v1.RelayOpen + 201, // 264: openshell.v1.PeerRelayFrame.init:type_name -> openshell.v1.PeerRelayInit + 276, // 265: openshell.v1.DenialSummary.first_seen_time:type_name -> google.protobuf.Timestamp + 276, // 266: openshell.v1.DenialSummary.last_seen_time:type_name -> google.protobuf.Timestamp + 205, // 267: openshell.v1.DenialSummary.l7_request_samples:type_name -> openshell.v1.L7RequestSample + 207, // 268: openshell.v1.NetworkActivitySummary.denials_by_group:type_name -> openshell.v1.DenialGroupCount + 287, // 269: openshell.v1.PolicyChunk.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 276, // 270: openshell.v1.PolicyChunk.created_time:type_name -> google.protobuf.Timestamp + 276, // 271: openshell.v1.PolicyChunk.decided_time:type_name -> google.protobuf.Timestamp + 276, // 272: openshell.v1.PolicyChunk.first_seen_time:type_name -> google.protobuf.Timestamp + 276, // 273: openshell.v1.PolicyChunk.last_seen_time:type_name -> google.protobuf.Timestamp + 278, // 274: openshell.v1.PolicyChunk.current_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 278, // 275: openshell.v1.PolicyChunk.candidate_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 281, // 276: openshell.v1.SubmitPolicyAnalysisRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 206, // 277: openshell.v1.SubmitPolicyAnalysisRequest.summaries:type_name -> openshell.v1.DenialSummary + 209, // 278: openshell.v1.SubmitPolicyAnalysisRequest.proposed_chunks:type_name -> openshell.v1.PolicyChunk + 208, // 279: openshell.v1.SubmitPolicyAnalysisRequest.network_activity_summaries:type_name -> openshell.v1.NetworkActivitySummary + 281, // 280: openshell.v1.GetDraftPolicyRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 209, // 281: openshell.v1.GetDraftPolicyResponse.chunks:type_name -> openshell.v1.PolicyChunk + 276, // 282: openshell.v1.GetDraftPolicyResponse.last_analyzed_time:type_name -> google.protobuf.Timestamp + 281, // 283: openshell.v1.ApproveDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 284: openshell.v1.RejectDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 285: openshell.v1.ApproveAllDraftChunksRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 219, // 286: openshell.v1.ApproveAllDraftChunksRequest.approvals:type_name -> openshell.v1.DraftChunkApproval + 281, // 287: openshell.v1.EditDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 287, // 288: openshell.v1.EditDraftChunkRequest.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 281, // 289: openshell.v1.UndoDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 290: openshell.v1.ClearDraftChunksRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 281, // 291: openshell.v1.GetDraftHistoryRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 276, // 292: openshell.v1.DraftHistoryEntry.event_time:type_name -> google.protobuf.Timestamp + 229, // 293: openshell.v1.GetDraftHistoryResponse.entries:type_name -> openshell.v1.DraftHistoryEntry + 275, // 294: openshell.v1.CreateWorkspaceRequest.labels:type_name -> openshell.v1.CreateWorkspaceRequest.LabelsEntry + 290, // 295: openshell.v1.CreateWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace + 290, // 296: openshell.v1.GetWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace + 290, // 297: openshell.v1.ListWorkspacesResponse.workspaces:type_name -> openshell.datamodel.v1.Workspace + 17, // 298: openshell.v1.DeleteWorkspaceResponse.outcome:type_name -> openshell.v1.DeletionOutcome + 277, // 299: openshell.v1.WorkspaceMember.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 14, // 300: openshell.v1.WorkspaceMember.role:type_name -> openshell.v1.WorkspaceRole + 281, // 301: openshell.v1.AddWorkspaceMemberRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 14, // 302: openshell.v1.AddWorkspaceMemberRequest.role:type_name -> openshell.v1.WorkspaceRole + 239, // 303: openshell.v1.AddWorkspaceMemberResponse.member:type_name -> openshell.v1.WorkspaceMember + 281, // 304: openshell.v1.RemoveWorkspaceMemberRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 17, // 305: openshell.v1.RemoveWorkspaceMemberResponse.outcome:type_name -> openshell.v1.DeletionOutcome + 281, // 306: openshell.v1.ListWorkspaceMembersRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 239, // 307: openshell.v1.ListWorkspaceMembersResponse.members:type_name -> openshell.v1.WorkspaceMember + 276, // 308: openshell.v1.ExtensionServiceCredential.expiration_time:type_name -> google.protobuf.Timestamp + 18, // 309: openshell.v1.EndpointObservation.result:type_name -> openshell.v1.EndpointResult + 247, // 310: openshell.v1.ReportEndpointStatusRequest.observations:type_name -> openshell.v1.EndpointObservation + 18, // 311: openshell.v1.EndpointStatus.last_result:type_name -> openshell.v1.EndpointResult + 276, // 312: openshell.v1.EndpointStatus.last_reported_time:type_name -> google.protobuf.Timestamp + 276, // 313: openshell.v1.SandboxProvisioning.configuration_change_time:type_name -> google.protobuf.Timestamp + 276, // 314: openshell.v1.SandboxProvisioning.first_rejection_time:type_name -> google.protobuf.Timestamp + 276, // 315: openshell.v1.SandboxProvisioning.deadline:type_name -> google.protobuf.Timestamp + 276, // 316: openshell.v1.SandboxProvisioning.timeout_time:type_name -> google.protobuf.Timestamp + 276, // 317: openshell.v1.SandboxProvisioning.cleanup_completed_time:type_name -> google.protobuf.Timestamp + 276, // 318: openshell.v1.SandboxProvisioning.cleanup_retry_time:type_name -> google.protobuf.Timestamp + 276, // 319: openshell.v1.SandboxProvisioning.attachment_change_time:type_name -> google.protobuf.Timestamp + 19, // 320: openshell.v1.SandboxServiceExposure.authorization_mode:type_name -> openshell.v1.ServiceAuthorizationMode + 276, // 321: openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry.value:type_name -> google.protobuf.Timestamp + 276, // 322: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry.value:type_name -> google.protobuf.Timestamp + 127, // 323: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential + 156, // 324: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry.value:type_name -> openshell.v1.StaticCredentialBinding + 24, // 325: openshell.v1.OpenShell.Health:input_type -> openshell.v1.HealthRequest + 26, // 326: openshell.v1.OpenShell.GetCurrentUser:input_type -> openshell.v1.GetCurrentUserRequest + 28, // 327: openshell.v1.OpenShell.GetGatewayInfo:input_type -> openshell.v1.GetGatewayInfoRequest + 52, // 328: openshell.v1.OpenShell.CreateSandbox:input_type -> openshell.v1.CreateSandboxRequest + 60, // 329: openshell.v1.OpenShell.BeginRootfsTarStaging:input_type -> openshell.v1.BeginRootfsTarStagingRequest + 62, // 330: openshell.v1.OpenShell.GetSandbox:input_type -> openshell.v1.GetSandboxRequest + 63, // 331: openshell.v1.OpenShell.ListSandboxes:input_type -> openshell.v1.ListSandboxesRequest + 53, // 332: openshell.v1.OpenShell.CreateSandboxTemplate:input_type -> openshell.v1.CreateSandboxTemplateRequest + 54, // 333: openshell.v1.OpenShell.GetSandboxTemplate:input_type -> openshell.v1.GetSandboxTemplateRequest + 55, // 334: openshell.v1.OpenShell.ListSandboxTemplates:input_type -> openshell.v1.ListSandboxTemplatesRequest + 56, // 335: openshell.v1.OpenShell.DeleteSandboxTemplate:input_type -> openshell.v1.DeleteSandboxTemplateRequest + 64, // 336: openshell.v1.OpenShell.ListSandboxProviders:input_type -> openshell.v1.ListSandboxProvidersRequest + 65, // 337: openshell.v1.OpenShell.AttachSandboxProvider:input_type -> openshell.v1.AttachSandboxProviderRequest + 66, // 338: openshell.v1.OpenShell.DetachSandboxProvider:input_type -> openshell.v1.DetachSandboxProviderRequest + 82, // 339: openshell.v1.OpenShell.GetSandboxProviderStatus:input_type -> openshell.v1.GetSandboxProviderStatusRequest + 67, // 340: openshell.v1.OpenShell.DeleteSandbox:input_type -> openshell.v1.DeleteSandboxRequest + 68, // 341: openshell.v1.OpenShell.StopSandbox:input_type -> openshell.v1.StopSandboxRequest + 69, // 342: openshell.v1.OpenShell.StartSandbox:input_type -> openshell.v1.StartSandboxRequest + 87, // 343: openshell.v1.OpenShell.CreateSshSession:input_type -> openshell.v1.CreateSshSessionRequest + 89, // 344: openshell.v1.OpenShell.ExposeService:input_type -> openshell.v1.ExposeServiceRequest + 90, // 345: openshell.v1.OpenShell.GetService:input_type -> openshell.v1.GetServiceRequest + 91, // 346: openshell.v1.OpenShell.ListServices:input_type -> openshell.v1.ListServicesRequest + 93, // 347: openshell.v1.OpenShell.DeleteService:input_type -> openshell.v1.DeleteServiceRequest + 97, // 348: openshell.v1.OpenShell.RevokeSshSession:input_type -> openshell.v1.RevokeSshSessionRequest + 99, // 349: openshell.v1.OpenShell.ExecSandbox:input_type -> openshell.v1.ExecSandboxRequest + 105, // 350: openshell.v1.OpenShell.ForwardTcp:input_type -> openshell.v1.TcpForwardFrame + 106, // 351: openshell.v1.OpenShell.ExecSandboxInteractive:input_type -> openshell.v1.ExecSandboxInput + 113, // 352: openshell.v1.OpenShell.CreateProvider:input_type -> openshell.v1.CreateProviderRequest + 114, // 353: openshell.v1.OpenShell.GetProvider:input_type -> openshell.v1.GetProviderRequest + 115, // 354: openshell.v1.OpenShell.ListProviders:input_type -> openshell.v1.ListProvidersRequest + 120, // 355: openshell.v1.OpenShell.ListProviderProfiles:input_type -> openshell.v1.ListProviderProfilesRequest + 121, // 356: openshell.v1.OpenShell.GetProviderProfile:input_type -> openshell.v1.GetProviderProfileRequest + 145, // 357: openshell.v1.OpenShell.ImportProviderProfiles:input_type -> openshell.v1.ImportProviderProfilesRequest + 147, // 358: openshell.v1.OpenShell.UpdateProviderProfiles:input_type -> openshell.v1.UpdateProviderProfilesRequest + 149, // 359: openshell.v1.OpenShell.LintProviderProfiles:input_type -> openshell.v1.LintProviderProfilesRequest + 116, // 360: openshell.v1.OpenShell.UpdateProvider:input_type -> openshell.v1.UpdateProviderRequest + 133, // 361: openshell.v1.OpenShell.GetProviderRefreshStatus:input_type -> openshell.v1.GetProviderRefreshStatusRequest + 135, // 362: openshell.v1.OpenShell.ConfigureProviderRefresh:input_type -> openshell.v1.ConfigureProviderRefreshRequest + 137, // 363: openshell.v1.OpenShell.RotateProviderCredential:input_type -> openshell.v1.RotateProviderCredentialRequest + 139, // 364: openshell.v1.OpenShell.DeleteProviderRefresh:input_type -> openshell.v1.DeleteProviderRefreshRequest + 117, // 365: openshell.v1.OpenShell.DeleteProvider:input_type -> openshell.v1.DeleteProviderRequest + 152, // 366: openshell.v1.OpenShell.DeleteProviderProfile:input_type -> openshell.v1.DeleteProviderProfileRequest + 291, // 367: openshell.v1.OpenShell.GetSandboxConfig:input_type -> openshell.sandbox.v1.GetSandboxConfigRequest + 292, // 368: openshell.v1.OpenShell.GetGatewayConfig:input_type -> openshell.sandbox.v1.GetGatewayConfigRequest + 160, // 369: openshell.v1.OpenShell.UpdateConfig:input_type -> openshell.v1.UpdateConfigRequest + 170, // 370: openshell.v1.OpenShell.GetSandboxPolicyStatus:input_type -> openshell.v1.GetSandboxPolicyStatusRequest + 172, // 371: openshell.v1.OpenShell.ListSandboxPolicies:input_type -> openshell.v1.ListSandboxPoliciesRequest + 174, // 372: openshell.v1.OpenShell.ReportPolicyStatus:input_type -> openshell.v1.ReportPolicyStatusRequest + 248, // 373: openshell.v1.OpenShell.ReportEndpointStatus:input_type -> openshell.v1.ReportEndpointStatusRequest + 84, // 374: openshell.v1.OpenShell.ReportProviderReadiness:input_type -> openshell.v1.ReportProviderReadinessRequest + 177, // 375: openshell.v1.OpenShell.ReportSandboxConfiguration:input_type -> openshell.v1.ReportSandboxConfigurationRequest + 154, // 376: openshell.v1.OpenShell.GetSandboxProviderEnvironment:input_type -> openshell.v1.GetSandboxProviderEnvironmentRequest + 158, // 377: openshell.v1.OpenShell.ExchangeProviderSubjectToken:input_type -> openshell.v1.ExchangeProviderSubjectTokenRequest + 180, // 378: openshell.v1.OpenShell.GetSandboxLogs:input_type -> openshell.v1.GetSandboxLogsRequest + 181, // 379: openshell.v1.OpenShell.PushSandboxLogs:input_type -> openshell.v1.PushSandboxLogsRequest + 185, // 380: openshell.v1.OpenShell.ConnectSupervisor:input_type -> openshell.v1.SupervisorMessage + 192, // 381: openshell.v1.OpenShell.ReportMainProcessExit:input_type -> openshell.v1.ReportMainProcessExitRequest + 194, // 382: openshell.v1.OpenShell.FinalizeMainProcessExit:input_type -> openshell.v1.FinalizeMainProcessExitRequest + 200, // 383: openshell.v1.OpenShell.RelayStream:input_type -> openshell.v1.RelayFrame + 202, // 384: openshell.v1.OpenShell.PeerRelay:input_type -> openshell.v1.PeerRelayFrame + 84, // 385: openshell.v1.OpenShell.PeerReportProviderReadiness:input_type -> openshell.v1.ReportProviderReadinessRequest + 248, // 386: openshell.v1.OpenShell.PeerReportEndpointStatus:input_type -> openshell.v1.ReportEndpointStatusRequest + 82, // 387: openshell.v1.OpenShell.PeerGetSandboxProviderStatus:input_type -> openshell.v1.GetSandboxProviderStatusRequest + 109, // 388: openshell.v1.OpenShell.WatchSandbox:input_type -> openshell.v1.WatchSandboxRequest + 211, // 389: openshell.v1.OpenShell.SubmitPolicyAnalysis:input_type -> openshell.v1.SubmitPolicyAnalysisRequest + 213, // 390: openshell.v1.OpenShell.GetDraftPolicy:input_type -> openshell.v1.GetDraftPolicyRequest + 215, // 391: openshell.v1.OpenShell.ApproveDraftChunk:input_type -> openshell.v1.ApproveDraftChunkRequest + 217, // 392: openshell.v1.OpenShell.RejectDraftChunk:input_type -> openshell.v1.RejectDraftChunkRequest + 220, // 393: openshell.v1.OpenShell.ApproveAllDraftChunks:input_type -> openshell.v1.ApproveAllDraftChunksRequest + 222, // 394: openshell.v1.OpenShell.EditDraftChunk:input_type -> openshell.v1.EditDraftChunkRequest + 224, // 395: openshell.v1.OpenShell.UndoDraftChunk:input_type -> openshell.v1.UndoDraftChunkRequest + 226, // 396: openshell.v1.OpenShell.ClearDraftChunks:input_type -> openshell.v1.ClearDraftChunksRequest + 228, // 397: openshell.v1.OpenShell.GetDraftHistory:input_type -> openshell.v1.GetDraftHistoryRequest + 20, // 398: openshell.v1.OpenShell.IssueSandboxToken:input_type -> openshell.v1.IssueSandboxTokenRequest + 22, // 399: openshell.v1.OpenShell.RefreshSandboxToken:input_type -> openshell.v1.RefreshSandboxTokenRequest + 231, // 400: openshell.v1.OpenShell.CreateWorkspace:input_type -> openshell.v1.CreateWorkspaceRequest + 233, // 401: openshell.v1.OpenShell.GetWorkspace:input_type -> openshell.v1.GetWorkspaceRequest + 235, // 402: openshell.v1.OpenShell.ListWorkspaces:input_type -> openshell.v1.ListWorkspacesRequest + 237, // 403: openshell.v1.OpenShell.DeleteWorkspace:input_type -> openshell.v1.DeleteWorkspaceRequest + 240, // 404: openshell.v1.OpenShell.AddWorkspaceMember:input_type -> openshell.v1.AddWorkspaceMemberRequest + 242, // 405: openshell.v1.OpenShell.RemoveWorkspaceMember:input_type -> openshell.v1.RemoveWorkspaceMemberRequest + 244, // 406: openshell.v1.OpenShell.ListWorkspaceMembers:input_type -> openshell.v1.ListWorkspaceMembersRequest + 25, // 407: openshell.v1.OpenShell.Health:output_type -> openshell.v1.HealthResponse + 27, // 408: openshell.v1.OpenShell.GetCurrentUser:output_type -> openshell.v1.GetCurrentUserResponse + 29, // 409: openshell.v1.OpenShell.GetGatewayInfo:output_type -> openshell.v1.GetGatewayInfoResponse + 70, // 410: openshell.v1.OpenShell.CreateSandbox:output_type -> openshell.v1.SandboxResponse + 61, // 411: openshell.v1.OpenShell.BeginRootfsTarStaging:output_type -> openshell.v1.BeginRootfsTarStagingResponse + 70, // 412: openshell.v1.OpenShell.GetSandbox:output_type -> openshell.v1.SandboxResponse + 71, // 413: openshell.v1.OpenShell.ListSandboxes:output_type -> openshell.v1.ListSandboxesResponse + 57, // 414: openshell.v1.OpenShell.CreateSandboxTemplate:output_type -> openshell.v1.SandboxTemplateResponse + 57, // 415: openshell.v1.OpenShell.GetSandboxTemplate:output_type -> openshell.v1.SandboxTemplateResponse + 58, // 416: openshell.v1.OpenShell.ListSandboxTemplates:output_type -> openshell.v1.ListSandboxTemplatesResponse + 59, // 417: openshell.v1.OpenShell.DeleteSandboxTemplate:output_type -> openshell.v1.DeleteSandboxTemplateResponse + 72, // 418: openshell.v1.OpenShell.ListSandboxProviders:output_type -> openshell.v1.ListSandboxProvidersResponse + 73, // 419: openshell.v1.OpenShell.AttachSandboxProvider:output_type -> openshell.v1.AttachSandboxProviderResponse + 74, // 420: openshell.v1.OpenShell.DetachSandboxProvider:output_type -> openshell.v1.DetachSandboxProviderResponse + 83, // 421: openshell.v1.OpenShell.GetSandboxProviderStatus:output_type -> openshell.v1.GetSandboxProviderStatusResponse + 86, // 422: openshell.v1.OpenShell.DeleteSandbox:output_type -> openshell.v1.DeleteSandboxResponse + 70, // 423: openshell.v1.OpenShell.StopSandbox:output_type -> openshell.v1.SandboxResponse + 70, // 424: openshell.v1.OpenShell.StartSandbox:output_type -> openshell.v1.SandboxResponse + 88, // 425: openshell.v1.OpenShell.CreateSshSession:output_type -> openshell.v1.CreateSshSessionResponse + 96, // 426: openshell.v1.OpenShell.ExposeService:output_type -> openshell.v1.ServiceEndpointResponse + 96, // 427: openshell.v1.OpenShell.GetService:output_type -> openshell.v1.ServiceEndpointResponse + 92, // 428: openshell.v1.OpenShell.ListServices:output_type -> openshell.v1.ListServicesResponse + 94, // 429: openshell.v1.OpenShell.DeleteService:output_type -> openshell.v1.DeleteServiceResponse + 98, // 430: openshell.v1.OpenShell.RevokeSshSession:output_type -> openshell.v1.RevokeSshSessionResponse + 103, // 431: openshell.v1.OpenShell.ExecSandbox:output_type -> openshell.v1.ExecSandboxEvent + 105, // 432: openshell.v1.OpenShell.ForwardTcp:output_type -> openshell.v1.TcpForwardFrame + 103, // 433: openshell.v1.OpenShell.ExecSandboxInteractive:output_type -> openshell.v1.ExecSandboxEvent + 118, // 434: openshell.v1.OpenShell.CreateProvider:output_type -> openshell.v1.ProviderResponse + 118, // 435: openshell.v1.OpenShell.GetProvider:output_type -> openshell.v1.ProviderResponse + 119, // 436: openshell.v1.OpenShell.ListProviders:output_type -> openshell.v1.ListProvidersResponse + 144, // 437: openshell.v1.OpenShell.ListProviderProfiles:output_type -> openshell.v1.ListProviderProfilesResponse + 143, // 438: openshell.v1.OpenShell.GetProviderProfile:output_type -> openshell.v1.ProviderProfileResponse + 146, // 439: openshell.v1.OpenShell.ImportProviderProfiles:output_type -> openshell.v1.ImportProviderProfilesResponse + 148, // 440: openshell.v1.OpenShell.UpdateProviderProfiles:output_type -> openshell.v1.UpdateProviderProfilesResponse + 150, // 441: openshell.v1.OpenShell.LintProviderProfiles:output_type -> openshell.v1.LintProviderProfilesResponse + 118, // 442: openshell.v1.OpenShell.UpdateProvider:output_type -> openshell.v1.ProviderResponse + 134, // 443: openshell.v1.OpenShell.GetProviderRefreshStatus:output_type -> openshell.v1.GetProviderRefreshStatusResponse + 136, // 444: openshell.v1.OpenShell.ConfigureProviderRefresh:output_type -> openshell.v1.ConfigureProviderRefreshResponse + 138, // 445: openshell.v1.OpenShell.RotateProviderCredential:output_type -> openshell.v1.RotateProviderCredentialResponse + 140, // 446: openshell.v1.OpenShell.DeleteProviderRefresh:output_type -> openshell.v1.DeleteProviderRefreshResponse + 151, // 447: openshell.v1.OpenShell.DeleteProvider:output_type -> openshell.v1.DeleteProviderResponse + 153, // 448: openshell.v1.OpenShell.DeleteProviderProfile:output_type -> openshell.v1.DeleteProviderProfileResponse + 293, // 449: openshell.v1.OpenShell.GetSandboxConfig:output_type -> openshell.sandbox.v1.GetSandboxConfigResponse + 294, // 450: openshell.v1.OpenShell.GetGatewayConfig:output_type -> openshell.sandbox.v1.GetGatewayConfigResponse + 169, // 451: openshell.v1.OpenShell.UpdateConfig:output_type -> openshell.v1.UpdateConfigResponse + 171, // 452: openshell.v1.OpenShell.GetSandboxPolicyStatus:output_type -> openshell.v1.GetSandboxPolicyStatusResponse + 173, // 453: openshell.v1.OpenShell.ListSandboxPolicies:output_type -> openshell.v1.ListSandboxPoliciesResponse + 175, // 454: openshell.v1.OpenShell.ReportPolicyStatus:output_type -> openshell.v1.ReportPolicyStatusResponse + 249, // 455: openshell.v1.OpenShell.ReportEndpointStatus:output_type -> openshell.v1.ReportEndpointStatusResponse + 85, // 456: openshell.v1.OpenShell.ReportProviderReadiness:output_type -> openshell.v1.ReportProviderReadinessResponse + 178, // 457: openshell.v1.OpenShell.ReportSandboxConfiguration:output_type -> openshell.v1.ReportSandboxConfigurationResponse + 157, // 458: openshell.v1.OpenShell.GetSandboxProviderEnvironment:output_type -> openshell.v1.GetSandboxProviderEnvironmentResponse + 159, // 459: openshell.v1.OpenShell.ExchangeProviderSubjectToken:output_type -> openshell.v1.ExchangeProviderSubjectTokenResponse + 183, // 460: openshell.v1.OpenShell.GetSandboxLogs:output_type -> openshell.v1.GetSandboxLogsResponse + 182, // 461: openshell.v1.OpenShell.PushSandboxLogs:output_type -> openshell.v1.PushSandboxLogsResponse + 186, // 462: openshell.v1.OpenShell.ConnectSupervisor:output_type -> openshell.v1.GatewayMessage + 193, // 463: openshell.v1.OpenShell.ReportMainProcessExit:output_type -> openshell.v1.ReportMainProcessExitResponse + 195, // 464: openshell.v1.OpenShell.FinalizeMainProcessExit:output_type -> openshell.v1.FinalizeMainProcessExitResponse + 200, // 465: openshell.v1.OpenShell.RelayStream:output_type -> openshell.v1.RelayFrame + 202, // 466: openshell.v1.OpenShell.PeerRelay:output_type -> openshell.v1.PeerRelayFrame + 85, // 467: openshell.v1.OpenShell.PeerReportProviderReadiness:output_type -> openshell.v1.ReportProviderReadinessResponse + 249, // 468: openshell.v1.OpenShell.PeerReportEndpointStatus:output_type -> openshell.v1.ReportEndpointStatusResponse + 83, // 469: openshell.v1.OpenShell.PeerGetSandboxProviderStatus:output_type -> openshell.v1.GetSandboxProviderStatusResponse + 110, // 470: openshell.v1.OpenShell.WatchSandbox:output_type -> openshell.v1.SandboxStreamEvent + 212, // 471: openshell.v1.OpenShell.SubmitPolicyAnalysis:output_type -> openshell.v1.SubmitPolicyAnalysisResponse + 214, // 472: openshell.v1.OpenShell.GetDraftPolicy:output_type -> openshell.v1.GetDraftPolicyResponse + 216, // 473: openshell.v1.OpenShell.ApproveDraftChunk:output_type -> openshell.v1.ApproveDraftChunkResponse + 218, // 474: openshell.v1.OpenShell.RejectDraftChunk:output_type -> openshell.v1.RejectDraftChunkResponse + 221, // 475: openshell.v1.OpenShell.ApproveAllDraftChunks:output_type -> openshell.v1.ApproveAllDraftChunksResponse + 223, // 476: openshell.v1.OpenShell.EditDraftChunk:output_type -> openshell.v1.EditDraftChunkResponse + 225, // 477: openshell.v1.OpenShell.UndoDraftChunk:output_type -> openshell.v1.UndoDraftChunkResponse + 227, // 478: openshell.v1.OpenShell.ClearDraftChunks:output_type -> openshell.v1.ClearDraftChunksResponse + 230, // 479: openshell.v1.OpenShell.GetDraftHistory:output_type -> openshell.v1.GetDraftHistoryResponse + 21, // 480: openshell.v1.OpenShell.IssueSandboxToken:output_type -> openshell.v1.IssueSandboxTokenResponse + 23, // 481: openshell.v1.OpenShell.RefreshSandboxToken:output_type -> openshell.v1.RefreshSandboxTokenResponse + 232, // 482: openshell.v1.OpenShell.CreateWorkspace:output_type -> openshell.v1.CreateWorkspaceResponse + 234, // 483: openshell.v1.OpenShell.GetWorkspace:output_type -> openshell.v1.GetWorkspaceResponse + 236, // 484: openshell.v1.OpenShell.ListWorkspaces:output_type -> openshell.v1.ListWorkspacesResponse + 238, // 485: openshell.v1.OpenShell.DeleteWorkspace:output_type -> openshell.v1.DeleteWorkspaceResponse + 241, // 486: openshell.v1.OpenShell.AddWorkspaceMember:output_type -> openshell.v1.AddWorkspaceMemberResponse + 243, // 487: openshell.v1.OpenShell.RemoveWorkspaceMember:output_type -> openshell.v1.RemoveWorkspaceMemberResponse + 245, // 488: openshell.v1.OpenShell.ListWorkspaceMembers:output_type -> openshell.v1.ListWorkspaceMembersResponse + 407, // [407:489] is the sub-list for method output_type + 325, // [325:407] is the sub-list for method input_type + 325, // [325:325] is the sub-list for extension type_name + 325, // [325:325] is the sub-list for extension extendee + 0, // [0:325] is the sub-list for field type_name } func init() { file_openshell_proto_init() } @@ -20332,27 +20471,31 @@ func file_openshell_proto_init() { } file_openshell_proto_msgTypes[145].OneofWrappers = []any{} file_openshell_proto_msgTypes[164].OneofWrappers = []any{ + (*OtelExportData_TraceData)(nil), + } + file_openshell_proto_msgTypes[165].OneofWrappers = []any{ (*SupervisorMessage_Hello)(nil), (*SupervisorMessage_Heartbeat)(nil), (*SupervisorMessage_RelayOpenResult)(nil), (*SupervisorMessage_RelayClose)(nil), + (*SupervisorMessage_OtelExport)(nil), } - file_openshell_proto_msgTypes[165].OneofWrappers = []any{ + file_openshell_proto_msgTypes[166].OneofWrappers = []any{ (*GatewayMessage_SessionAccepted)(nil), (*GatewayMessage_SessionRejected)(nil), (*GatewayMessage_Heartbeat)(nil), (*GatewayMessage_RelayOpen)(nil), (*GatewayMessage_RelayClose)(nil), } - file_openshell_proto_msgTypes[175].OneofWrappers = []any{ + file_openshell_proto_msgTypes[176].OneofWrappers = []any{ (*RelayOpen_Ssh)(nil), (*RelayOpen_Tcp)(nil), } - file_openshell_proto_msgTypes[179].OneofWrappers = []any{ + file_openshell_proto_msgTypes[180].OneofWrappers = []any{ (*RelayFrame_Init)(nil), (*RelayFrame_Data)(nil), } - file_openshell_proto_msgTypes[181].OneofWrappers = []any{ + file_openshell_proto_msgTypes[182].OneofWrappers = []any{ (*PeerRelayFrame_Init)(nil), (*PeerRelayFrame_Data)(nil), } @@ -20362,7 +20505,7 @@ func file_openshell_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_openshell_proto_rawDesc), len(file_openshell_proto_rawDesc)), NumEnums: 20, - NumMessages: 255, + NumMessages: 256, NumExtensions: 0, NumServices: 1, },