From cdc545548ae29e501a03a755cffd31e99ea83c38 Mon Sep 17 00:00:00 2001 From: Taylor Mutch Date: Mon, 26 Oct 2026 16:13:14 -0700 Subject: [PATCH 1/2] feat(services): publish authoritative application origins Signed-off-by: Taylor Mutch --- crates/openshell-cli/src/run.rs | 57 +- .../tests/provider_commands_integration.rs | 2 + .../sandbox_create_lifecycle_integration.rs | 8 + .../sandbox_exec_streaming_integration.rs | 2 + .../sandbox_name_fallback_integration.rs | 2 + crates/openshell-core/src/app_listener.rs | 199 +++ crates/openshell-crypto/README.md | 1 + .../openshell-driver-kubernetes/src/config.rs | 1 + crates/openshell-sdk/src/client.rs | 2 + crates/openshell-sdk/src/types.rs | 6 + crates/openshell-sdk/tests/client_mock.rs | 22 +- crates/openshell-server/README.md | 17 + .../src/grpc/mutation_replay.rs | 5 +- .../src/grpc/mutation_replay/ordinary.rs | 28 +- .../src/grpc/mutation_replay/tests.rs | 59 + crates/openshell-server/src/grpc/sandbox.rs | 57 +- crates/openshell-server/src/grpc/service.rs | 157 ++- .../src/grpc/service_advertisement.rs | 18 +- .../src/grpc/service_discovery.rs | 95 +- .../openshell-server/src/service_routing.rs | 110 +- crates/openshell-server/src/storage_proto.rs | 4 +- crates/openshell-supervisor/README.md | 6 + .../openshell-supervisor/src/app_listener.rs | 324 ++++- docs/how-it-works/gateways/configuration.mdx | 43 +- proto/openshell.proto | 12 +- proto/sandbox.proto | 4 + python/openshell/sandbox.py | 32 +- python/openshell/sandbox_test.py | 29 + .../v1/internal/converter/service.go | 8 +- .../v1/internal/converter/service_test.go | 7 +- sdk/go/openshell/v1/sandbox_client.go | 4 + sdk/go/openshell/v1/types/sandbox.go | 4 +- sdk/go/openshell/v1/types/service.go | 2 + sdk/go/proto/openshellv1/openshell.pb.go | 1177 +++++++++-------- sdk/go/proto/sandboxv1/sandbox.pb.go | 20 +- sdk/typescript/src/client.test.ts | 4 + sdk/typescript/src/client.ts | 15 +- skills/debug-openshell-cluster/SKILL.md | 20 +- 38 files changed, 1802 insertions(+), 761 deletions(-) diff --git a/crates/openshell-cli/src/run.rs b/crates/openshell-cli/src/run.rs index eb224685ed..fdbf04ee57 100644 --- a/crates/openshell-cli/src/run.rs +++ b/crates/openshell-cli/src/run.rs @@ -723,10 +723,17 @@ pub async fn sandbox_create( Err(status) => return Err(miette::miette!(status.to_string())), }; let response = response.into_inner(); + let application_service_urls = response.application_service_urls; let service_urls = response .service_urls .into_iter() - .map(|(service, url)| (service, service_url_for_gateway(&url, &effective_server))) + .map(|(service, url)| { + let url = application_service_urls + .get(&service) + .cloned() + .unwrap_or_else(|| service_url_for_gateway(&url, &effective_server)); + (service, url) + }) .collect::>(); let sandbox = response .sandbox @@ -1115,10 +1122,7 @@ pub async fn sandbox_create( "\u{2713}".green().bold(), ); if let Some(url) = service_urls.get("").filter(|url| !url.is_empty()) { - eprintln!( - " Access at: {}", - service_url_for_gateway(url, &effective_server) - ); + eprintln!(" Access at: {url}"); } } @@ -4369,7 +4373,7 @@ pub async fn service_expose( ); } if !response.url.is_empty() { - let url = service_url_for_gateway(&response.url, server); + let url = preferred_service_url(&response, server); println!(" URL: {}", url.cyan()); } Ok(()) @@ -4582,7 +4586,7 @@ fn print_service_endpoint_table( let url = if response.url.is_empty() { String::new() } else { - service_url_for_gateway(&response.url, gateway_endpoint) + preferred_service_url(response, gateway_endpoint) }; Some(( workspace.to_string(), @@ -4673,7 +4677,7 @@ fn service_endpoint_to_json( let url = if response.url.is_empty() { String::new() } else { - service_url_for_gateway(&response.url, gateway_endpoint) + preferred_service_url(response, gateway_endpoint) }; Some(serde_json::json!({ @@ -4683,6 +4687,8 @@ fn service_endpoint_to_json( "target_port": endpoint.target_port, "authorization_mode": service_authorization_mode_name(endpoint.authorization_mode), "url": url, + "application_url": response.application_url, + "gateway_url": service_url_for_gateway(&response.gateway_url, gateway_endpoint), })) } @@ -4697,13 +4703,16 @@ fn service_authorization_mode_name(mode: i32) -> &'static str { } } -/// Read gcloud Application Default Credentials from disk. -/// -/// Returns `(client_id, client_secret, refresh_token)`. -/// -/// Checks `GOOGLE_APPLICATION_CREDENTIALS` first; falls back to -/// `$CLOUDSDK_CONFIG/application_default_credentials.json` when set, then to -/// `~/.config/gcloud/application_default_credentials.json`. +/// Application origins are independent of the gateway transport. Only legacy +/// gateway URLs inherit a caller's external gateway port. +fn preferred_service_url(response: &ServiceEndpointResponse, gateway: &str) -> String { + if response.application_url.is_empty() { + service_url_for_gateway(&response.url, gateway) + } else { + response.application_url.clone() + } +} + fn service_url_for_gateway(service_url: &str, gateway_endpoint: &str) -> String { let (Ok(mut service_url), Ok(gateway_endpoint)) = ( url::Url::parse(service_url), @@ -7332,6 +7341,8 @@ mod tests { #[test] fn service_endpoint_json_has_raw_fields_and_normalized_url() { let response = ServiceEndpointResponse { + application_url: String::new(), + gateway_url: String::new(), endpoint: Some(ServiceEndpoint { metadata: Some(ObjectMeta { workspace: "team-a".to_string(), @@ -7357,6 +7368,8 @@ mod tests { "target_port": 8080, "authorization_mode": "bearer_passthrough", "url": "https://api.openshell.localhost:17670/", + "application_url": "", + "gateway_url": "", }) ); assert!(service_endpoint_to_json(&ServiceEndpointResponse::default(), "unused").is_none()); @@ -8116,6 +8129,20 @@ mod tests { assert!(rootfs_tar_sources_supported_for_gateway(None)); } + #[test] + fn application_origin_is_preserved_when_gateway_uses_a_port_forward() { + let response = ServiceEndpointResponse { + application_url: "https://default--demo--web.services.example.com:10443/".into(), + gateway_url: "https://default--demo--web.api.example.com:8080/".into(), + url: "https://default--demo--web.services.example.com:10443/".into(), + ..Default::default() + }; + assert_eq!( + super::preferred_service_url(&response, "https://localhost:29092"), + response.application_url + ); + } + #[test] fn service_url_for_gateway_uses_external_gateway_port() { assert_eq!( diff --git a/crates/openshell-cli/tests/provider_commands_integration.rs b/crates/openshell-cli/tests/provider_commands_integration.rs index f14805f17d..35e86d3124 100644 --- a/crates/openshell-cli/tests/provider_commands_integration.rs +++ b/crates/openshell-cli/tests/provider_commands_integration.rs @@ -378,6 +378,8 @@ impl OpenShell for TestOpenShell { let name = request.name; // Return a minimal sandbox with metadata for CAS operations Ok(Response::new(SandboxResponse { + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), sandbox: Some(Sandbox { metadata: Some(openshell_core::proto::datamodel::v1::ObjectMeta { id: format!("sb-{name}"), diff --git a/crates/openshell-cli/tests/sandbox_create_lifecycle_integration.rs b/crates/openshell-cli/tests/sandbox_create_lifecycle_integration.rs index bc8197872d..262e4aa8b0 100644 --- a/crates/openshell-cli/tests/sandbox_create_lifecycle_integration.rs +++ b/crates/openshell-cli/tests/sandbox_create_lifecycle_integration.rs @@ -238,6 +238,8 @@ impl OpenShell for TestOpenShell { }, ); Ok(Response::new(SandboxResponse { + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), sandbox: Some(sandbox), service_urls, })) @@ -302,6 +304,8 @@ impl OpenShell for TestOpenShell { }; sandbox.set_phase(SandboxPhase::Ready as i32); Ok(Response::new(SandboxResponse { + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), sandbox: Some(sandbox), service_urls: HashMap::new(), })) @@ -538,6 +542,8 @@ impl OpenShell for TestOpenShell { .push(request.into_inner()); Ok(Response::new( openshell_core::proto::ServiceEndpointResponse { + application_url: String::new(), + gateway_url: String::new(), url: "https://default--sandbox.openshell.localhost:17670/".to_string(), ..Default::default() }, @@ -550,6 +556,8 @@ impl OpenShell for TestOpenShell { ) -> Result, Status> { Ok(Response::new( openshell_core::proto::ServiceEndpointResponse { + application_url: String::new(), + gateway_url: String::new(), url: "https://default--sandbox.openshell.localhost:17670/".to_string(), ..Default::default() }, diff --git a/crates/openshell-cli/tests/sandbox_exec_streaming_integration.rs b/crates/openshell-cli/tests/sandbox_exec_streaming_integration.rs index 4238254ed4..6c27aa325d 100644 --- a/crates/openshell-cli/tests/sandbox_exec_streaming_integration.rs +++ b/crates/openshell-cli/tests/sandbox_exec_streaming_integration.rs @@ -188,6 +188,8 @@ macro_rules! mock_gateway { async fn get_sandbox(&self, _: Request) -> Result, Status> { self.calls.lock().unwrap().lookups += 1; Ok(Response::new(proto::SandboxResponse { +application_service_urls: std::collections::HashMap::new(), +gateway_service_urls: std::collections::HashMap::new(), sandbox: Some(proto::Sandbox { metadata: Some(proto::datamodel::v1::ObjectMeta { id: "test-id".to_string(), diff --git a/crates/openshell-cli/tests/sandbox_name_fallback_integration.rs b/crates/openshell-cli/tests/sandbox_name_fallback_integration.rs index c218a7fcb7..238750d04f 100644 --- a/crates/openshell-cli/tests/sandbox_name_fallback_integration.rs +++ b/crates/openshell-cli/tests/sandbox_name_fallback_integration.rs @@ -187,6 +187,8 @@ impl OpenShell for TestOpenShell { let name = request.name.clone(); *self.state.last_get_name.lock().await = Some(name.clone()); Ok(Response::new(SandboxResponse { + application_service_urls: std::collections::HashMap::new(), + gateway_service_urls: std::collections::HashMap::new(), sandbox: Some(Sandbox { metadata: Some(openshell_core::proto::datamodel::v1::ObjectMeta { id: "test-id".to_string(), diff --git a/crates/openshell-core/src/app_listener.rs b/crates/openshell-core/src/app_listener.rs index bece387213..26bc4a0a69 100644 --- a/crates/openshell-core/src/app_listener.rs +++ b/crates/openshell-core/src/app_listener.rs @@ -4,6 +4,7 @@ //! Driver-independent application listener configuration. use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; /// Explicitly enabled application ingress. Deployment details such as TLS /// Secret names belong to the driver, not to this common configuration. @@ -13,6 +14,8 @@ pub struct AppListenerConfig { pub enabled: bool, pub port: u16, pub domain: String, + /// Public HTTPS origin, independently of the supervisor's listener port. + pub public_url: Option, } impl Default for AppListenerConfig { @@ -21,6 +24,7 @@ impl Default for AppListenerConfig { enabled: false, port: 8443, domain: String::new(), + public_url: None, } } } @@ -28,6 +32,9 @@ impl Default for AppListenerConfig { impl AppListenerConfig { pub fn validate(&self) -> Result<(), String> { if !self.enabled { + if self.public_url.is_some() { + return Err("supervisor.app_listener.public_url requires enabled = true".into()); + } return Ok(()); } if self.port < 1024 { @@ -46,8 +53,124 @@ impl AppListenerConfig { { return Err("supervisor.app_listener.domain must be a lowercase DNS name".into()); } + if let Some(origin) = &self.public_url { + let parsed = url::Url::parse(origin) + .map_err(|_| "supervisor.app_listener.public_url must be an HTTPS origin")?; + if parsed.scheme() != "https" + || parsed.host_str() != Some(self.domain.as_str()) + || !parsed.username().is_empty() + || parsed.password().is_some() + || parsed.path() != "/" + || parsed.query().is_some() + || parsed.fragment().is_some() + || parsed.port() == Some(0) + { + return Err("supervisor.app_listener.public_url must be an HTTPS origin matching domain, without credentials, path, query, or fragment".into()); + } + } Ok(()) } + + /// Canonical SNI/Host shared by URL publication, configuration and discovery. + /// A discriminator selects the gateway's hashed mode; otherwise use readable names. + pub fn service_hostname( + &self, + workspace: &str, + sandbox: &str, + service: &str, + discriminator: Option<&str>, + ) -> String { + let label = service_route_label(discriminator, workspace, sandbox, service); + format!("{label}.{}", self.domain) + } + + /// Configured address only; this does not imply installation or readiness. + pub fn service_url( + &self, + workspace: &str, + sandbox: &str, + service: &str, + discriminator: Option<&str>, + ) -> Option { + if !self.enabled { + return None; + } + let mut origin = url::Url::parse(self.public_url.as_deref()?).ok()?; + origin + .set_host(Some(&self.service_hostname( + workspace, + sandbox, + service, + discriminator, + ))) + .ok()?; + Some(origin.into()) + } +} + +/// Shared naming policy for gateway-mediated and direct application URLs. +/// The ingress implementation does not participate in the public service identity. +pub fn service_route_label( + discriminator: Option<&str>, + workspace: &str, + sandbox: &str, + service: &str, +) -> String { + discriminator.map_or_else( + || { + if service.is_empty() { + format!("{workspace}--{sandbox}") + } else { + format!("{workspace}--{sandbox}--{service}") + } + }, + |gateway| service_route_hash_label(gateway, workspace, sandbox, Some(service)), + ) +} + +/// Returns the stable, one-label DNS name for a service route. +/// +/// This is a non-security naming digest, not authentication or proof of authority. +/// Preserve the existing SHA-256 input encoding and 80-bit base32 label from #4842. +pub fn service_route_hash_label( + gateway_name: &str, + workspace: &str, + sandbox: &str, + service: Option<&str>, +) -> String { + let mut hasher = Sha256::new(); + hasher.update(gateway_name.as_bytes()); + hasher.update([0]); + hasher.update(workspace.as_bytes()); + hasher.update([0]); + hasher.update(sandbox.as_bytes()); + hasher.update([0]); + hasher.update(service.unwrap_or("").as_bytes()); + let digest = hasher.finalize(); + base32_lower_no_padding(&digest[..10]) +} + +fn base32_lower_no_padding(bytes: &[u8]) -> String { + const ALPHABET: &[u8; 32] = b"abcdefghijklmnopqrstuvwxyz234567"; + let mut output = String::with_capacity(16); + let mut accumulator = 0_u32; + let mut bits = 0_u8; + for byte in bytes { + accumulator = (accumulator << 8) | u32::from(*byte); + bits += 8; + while bits >= 5 { + bits -= 5; + output.push(char::from( + ALPHABET[((accumulator >> bits) & 0x1f) as usize], + )); + } + } + if bits > 0 { + output.push(char::from( + ALPHABET[((accumulator << (5 - bits)) & 0x1f) as usize], + )); + } + output } /// A synchronous view of installed ingress, evaluated on the sandbox instance @@ -60,6 +183,82 @@ pub type InstallationSource = std::sync::Arc< mod tests { use super::*; + #[test] + fn legacy_service_route_wire_payload_keeps_readable_routing_default() { + use prost::Message as _; + let legacy = b"\x0a\x08endpoint\x12\x03web\x18\x90\x3f\x20\x01\x28\x02"; + let route = crate::proto::SandboxServiceRoute::decode(legacy.as_slice()).unwrap(); + assert_eq!(route.endpoint_id, "endpoint"); + assert_eq!(route.name, "web"); + assert_eq!(route.target_port, 8080); + assert!(route.application_hostname.is_empty()); + } + + #[test] + fn application_urls_share_gateway_hash_labels_and_preserve_public_origin() { + let config = AppListenerConfig { + enabled: true, + domain: "apps.example.com".into(), + public_url: Some("https://apps.example.com:10443".into()), + ..Default::default() + }; + assert_eq!( + config + .service_url("default", "my-sandbox", "web", Some("gateway-a")) + .unwrap(), + "https://enh47uqyj7bpyttz.apps.example.com:10443/" + ); + assert_eq!( + config + .service_url("default", "my-sandbox", "", Some("gateway-a")) + .unwrap(), + "https://eiud2z5apv3yeucg.apps.example.com:10443/" + ); + assert_ne!( + config.service_hostname("default", "my-sandbox", "web", Some("gateway-a")), + config.service_hostname("default", "my-sandbox", "web", Some("gateway-b")) + ); + } + + #[test] + fn public_origin_is_explicit_https_and_matches_listener_domain() { + let mut config = AppListenerConfig { + enabled: true, + domain: "services.example.com".into(), + public_url: Some("https://services.example.com:10443".into()), + ..Default::default() + }; + config.validate().unwrap(); + assert_eq!( + config + .service_url("default", "demo", "web", None) + .as_deref(), + Some("https://default--demo--web.services.example.com:10443/") + ); + config.public_url = Some("https://services.example.com:443/".into()); + assert_eq!( + config.service_url("default", "demo", "", None).as_deref(), + Some("https://default--demo.services.example.com/") + ); + for bad in [ + "http://services.example.com", + "https://other.example.com", + "https://user:password@services.example.com", + "https://services.example.com/path", + "https://services.example.com/?query", + "https://services.example.com/#fragment", + "https://services.example.com:0", + "not-a-url", + ] { + config.public_url = Some(bad.into()); + assert!(config.validate().is_err(), "{bad}"); + } + config.public_url = Some("https://services.example.com".into()); + config.enabled = false; + assert!(config.validate().is_err()); + assert!(config.service_url("default", "demo", "web", None).is_none()); + } + #[test] fn only_explicit_enablement_requires_a_valid_endpoint() { assert!(!AppListenerConfig::default().enabled); diff --git a/crates/openshell-crypto/README.md b/crates/openshell-crypto/README.md index 7c040c1c31..8797f0a754 100644 --- a/crates/openshell-crypto/README.md +++ b/crates/openshell-crypto/README.md @@ -249,6 +249,7 @@ not a whole-application crypto coverage claim: | DNS policy contract fingerprints (`openshell-supervisor-network::policy_dns`) | These bind cached DNS state to policy and are security-relevant. They remain outside this migration; migrating the cache identity API and its failure handling is required for exclusive backend ownership. | | Credential-storage names (`credentials`, Kubernetes Secrets, Vault) | Deterministic namespace derivation, not encryption or proof of authorization. These retain their current hashing implementation and stored names; backend coverage is not asserted for them. | | Provider-refresh advisory-lock keys | Deterministic database lock selection for coordinating refreshes, not authentication or encryption. These remain outside the selected-backend contract. | +| Service-routing hostname labels (`openshell-core::app_listener`) | Deterministic public DNS naming, not authentication or proof of authorization. The existing truncated SHA-256/base32 encoding remains unchanged and uses `sha2`; gateway and application paths share the implementation. | | Endpoint IDs and pagination fingerprints | Deterministic identification/request matching, not authentication. These remain outside the selected-backend contract. | | Debug token fingerprints | Diagnostic display only; direct hashing remains outside the contract even though the input is a credential. | | Destination-trust generation labels (`openshell-server` network trust, Podman operator staging, Kubernetes ConfigMap naming, VM staged-snapshot checks) | Deterministic identity derivation for naming and generation equality, not encryption or proof of authorization. Trust decisions use the gateway's normalization, immutable staging, exact-byte validation, and TLS verification rather than these digests. These retain `sha2` directly. | diff --git a/crates/openshell-driver-kubernetes/src/config.rs b/crates/openshell-driver-kubernetes/src/config.rs index 0a22fada9e..427d56ffd7 100644 --- a/crates/openshell-driver-kubernetes/src/config.rs +++ b/crates/openshell-driver-kubernetes/src/config.rs @@ -1151,6 +1151,7 @@ mod tests { assert!(!config.app_listener.common.enabled); assert!(config.validate().is_ok()); config.app_listener.common = openshell_core::app_listener::AppListenerConfig { + public_url: None, enabled: true, port: 9443, domain: "services.example.com".into(), diff --git a/crates/openshell-sdk/src/client.rs b/crates/openshell-sdk/src/client.rs index 1f03916ef2..eec40b67f2 100644 --- a/crates/openshell-sdk/src/client.rs +++ b/crates/openshell-sdk/src/client.rs @@ -1561,6 +1561,8 @@ fn sandbox_from_response(sandbox: Option) -> Result fn sandbox_from_create_response(response: proto::SandboxResponse) -> Result { let mut sandbox = sandbox_from_response(response.sandbox)?; sandbox.service_urls = response.service_urls; + sandbox.application_service_urls = response.application_service_urls; + sandbox.gateway_service_urls = response.gateway_service_urls; Ok(sandbox) } diff --git a/crates/openshell-sdk/src/types.rs b/crates/openshell-sdk/src/types.rs index 571ee42b68..97c1fc7574 100644 --- a/crates/openshell-sdk/src/types.rs +++ b/crates/openshell-sdk/src/types.rs @@ -402,6 +402,10 @@ pub struct SandboxRef { /// Service URLs returned by sandbox creation, keyed by service name. The /// empty key identifies the unnamed service. Non-create reads leave this empty. pub service_urls: HashMap, + /// Configured application origins; these do not establish installation readiness. + pub application_service_urls: HashMap, + /// Gateway-routed alternatives. + pub gateway_service_urls: HashMap, pub restart_count: u32, pub next_restart_at_ms: Option, pub main_process_started_at_ms: Option, @@ -455,6 +459,8 @@ impl SandboxRef { .then_some(sandbox.host_key_fingerprint), created_from_workload_template, service_urls: HashMap::new(), + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), restart_count, next_restart_at_ms, main_process_started_at_ms, diff --git a/crates/openshell-sdk/tests/client_mock.rs b/crates/openshell-sdk/tests/client_mock.rs index 3e8cee1579..f5dfb7fbed 100644 --- a/crates/openshell-sdk/tests/client_mock.rs +++ b/crates/openshell-sdk/tests/client_mock.rs @@ -373,7 +373,7 @@ impl OpenShell for TestOpenShell { } else { req.name.clone() }; - let service_urls = req + let service_urls: HashMap = req .service_exposures .iter() .map(|exposure| { @@ -385,6 +385,11 @@ impl OpenShell for TestOpenShell { .collect(); *self.state.last_create.lock().await = Some(req); Ok(Response::new(proto::SandboxResponse { + application_service_urls: service_urls.clone(), + gateway_service_urls: HashMap::from([( + "web".into(), + "https://web.gateway.test:8080/".into(), + )]), sandbox: Some(sandbox_with_phase(&name, proto::SandboxPhase::Provisioning)), service_urls, })) @@ -455,6 +460,8 @@ impl OpenShell for TestOpenShell { ); *self.state.last_stop.lock().await = Some(request); Ok(Response::new(proto::SandboxResponse { + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), sandbox: Some(sandbox), service_urls: HashMap::new(), })) @@ -472,6 +479,8 @@ impl OpenShell for TestOpenShell { ); *self.state.last_start.lock().await = Some(request); Ok(Response::new(proto::SandboxResponse { + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), sandbox: Some(sandbox), service_urls: HashMap::new(), })) @@ -489,6 +498,8 @@ impl OpenShell for TestOpenShell { ); *self.state.last_suspend.lock().await = Some(request); Ok(Response::new(proto::SandboxResponse { + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), sandbox: Some(sandbox), service_urls: HashMap::new(), })) @@ -506,6 +517,8 @@ impl OpenShell for TestOpenShell { ); *self.state.last_resume.lock().await = Some(request); Ok(Response::new(proto::SandboxResponse { + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), sandbox: Some(sandbox), service_urls: HashMap::new(), })) @@ -553,6 +566,8 @@ impl OpenShell for TestOpenShell { sandbox.metadata.as_mut().unwrap().id.clone_from(id); } let mut response = Response::new(proto::SandboxResponse { + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), sandbox: Some(sandbox), service_urls: HashMap::new(), }); @@ -1275,6 +1290,11 @@ async fn create_sandbox_passes_spec_through() { result.service_urls.get("web").map(String::as_str), Some("https://web.example.test/") ); + assert_eq!(result.application_service_urls, result.service_urls); + assert_eq!( + result.gateway_service_urls["web"], + "https://web.gateway.test:8080/" + ); let observed = state.last_create.lock().await.clone().unwrap(); assert_eq!(observed.name, "my-box"); diff --git a/crates/openshell-server/README.md b/crates/openshell-server/README.md index 4c12359872..216f167c51 100644 --- a/crates/openshell-server/README.md +++ b/crates/openshell-server/README.md @@ -277,3 +277,20 @@ Watches refresh and reauthorize each second, use a one-message buffer, and end after one minute or a five-second blocked send. Each gateway process accepts at most 32 watches. Reconnect and relist on stream termination; revisions are opaque equality tokens, not resumable cursors or an ordering across gateway replicas. + +## Application URL publication + +`supervisor.app_listener.public_url` is an optional HTTPS origin matching the +listener domain. It requires explicit listener enablement. Service responses +return preferred `url`, configured `application_url`, and a gateway alternative +`gateway_url`. Sandbox creation supplies equivalent service-name maps. Current +deployment origins are recomputed for durable replay without repeating the +mutation or changing resource identities. These are configured addresses, not +installation, ingress acceptance, or application health signals. + +Application hostname mode follows `service_hostname_format` for both access +paths. The gateway sends each canonical app hostname in `SandboxServiceRoute`; +URL responses and discovery agree with that installed name. A mode or gateway +name change changes configuration identity, withdrawing old discovery evidence +until the supervisor acknowledges the replacement. Hash inputs and encoding are +shared with gateway-mediated routing in `openshell-core::app_listener`. diff --git a/crates/openshell-server/src/grpc/mutation_replay.rs b/crates/openshell-server/src/grpc/mutation_replay.rs index 837d26466c..6d302bf342 100644 --- a/crates/openshell-server/src/grpc/mutation_replay.rs +++ b/crates/openshell-server/src/grpc/mutation_replay.rs @@ -127,6 +127,7 @@ pub(super) trait Mutation: Message + Default + Send + Sync + 'static { request: Request, ) -> impl Future, Status>> + Send; fn capture(response: &Response) -> Result; + fn refresh_replay(_state: &ServerState, _response: &mut Self::Output) {} fn restore( store: &Store, success: Success, @@ -314,7 +315,9 @@ async fn execute_owned( return Err(replay_unavailable()); } let success = previous.success.ok_or_else(uncertain)?; - let mut response = Response::new(M::restore(&state.store, success).await?); + let mut output = M::restore(&state.store, success).await?; + M::refresh_replay(state, &mut output); + let mut response = Response::new(output); response.metadata_mut().insert( "openshell-replayed", "true".parse().expect("static metadata"), diff --git a/crates/openshell-server/src/grpc/mutation_replay/ordinary.rs b/crates/openshell-server/src/grpc/mutation_replay/ordinary.rs index a88c6abb9d..beaa178d49 100644 --- a/crates/openshell-server/src/grpc/mutation_replay/ordinary.rs +++ b/crates/openshell-server/src/grpc/mutation_replay/ordinary.rs @@ -335,7 +335,7 @@ async fn profile_scope( } macro_rules! mutation { - ($req:ty, $resp:ty, $method:literal, $handler:path, $auth:expr, $capture:expr, $restore:expr $(, $replay_method:literal)?) => { + ($req:ty, $resp:ty, $method:literal, $handler:path, $auth:expr, $capture:expr, $restore:expr $(, refresh = $refresh:expr)? $(, replay_method = $replay_method:literal)?) => { impl Mutation for $req { type Output = $resp; const METHOD: &'static str = $method; @@ -363,12 +363,15 @@ macro_rules! mutation { async fn restore(store: &Store, success: Success) -> Result { ($restore)(store, outcome(success)?).await } + fn refresh_replay(_state: &ServerState, _response: &mut Self::Output) { + $(($refresh)(_state, _response);)? + } } }; } macro_rules! scoped_mutation { - ($req:ty, $resp:ty, $method:literal, $handler:path, $role:ident, $capture:expr, $restore:expr) => { + ($req:ty, $resp:ty, $method:literal, $handler:path, $role:ident, $capture:expr, $restore:expr $(, refresh = $refresh:expr)?) => { mutation!( $req, $resp, @@ -384,13 +387,13 @@ macro_rules! scoped_mutation { .await }, $capture, - $restore + $restore $(, refresh = $refresh)? ); }; } macro_rules! sandbox_scoped_mutation { - ($req:ty, $resp:ty, $method:literal, $handler:path, $role:ident, $capture:expr, $restore:expr $(, $replay_method:literal)?) => { + ($req:ty, $resp:ty, $method:literal, $handler:path, $role:ident, $capture:expr, $restore:expr $(, refresh = $refresh:expr)? $(, replay_method = $replay_method:literal)?) => { mutation!( $req, $resp, @@ -407,7 +410,8 @@ macro_rules! sandbox_scoped_mutation { }, $capture, $restore - $(, $replay_method)? + $(, refresh = $refresh)? + $(, replay_method = $replay_method)? ); }; } @@ -442,11 +446,13 @@ macro_rules! sandbox_mutation { return Err(replay_unavailable()); }; Ok(SandboxResponse { + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), sandbox: Some(live(store, &id).await?), service_urls: HashMap::new(), }) } - $(, $replay_method)? + $(, replay_method = $replay_method)? ); }; } @@ -469,10 +475,13 @@ scoped_mutation!( return Err(replay_unavailable()); }; Ok(SandboxResponse { + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), sandbox: Some(live(store, &id).await?), service_urls, }) - } + }, + refresh = sandbox::refresh_response_urls ); sandbox_mutation!( openshell_core::proto::UpdateSandboxRequest, @@ -594,10 +603,13 @@ sandbox_scoped_mutation!( }; let _: Sandbox = live(store, &sandbox_id).await?; Ok(ServiceEndpointResponse { + application_url: String::new(), + gateway_url: String::new(), endpoint: Some(reference.restore(store).await?), url, }) - } + }, + refresh = service::refresh_response_urls ); macro_rules! provider_mutation { diff --git a/crates/openshell-server/src/grpc/mutation_replay/tests.rs b/crates/openshell-server/src/grpc/mutation_replay/tests.rs index fe9e2c20d0..39f9f398a2 100644 --- a/crates/openshell-server/src/grpc/mutation_replay/tests.rs +++ b/crates/openshell-server/src/grpc/mutation_replay/tests.rs @@ -93,6 +93,65 @@ fn canonical_payload_ignores_map_order_and_id_but_preserves_presence() { assert_ne!(absent, fingerprint(&template).unwrap()); } +#[tokio::test] +async fn service_url_replay_uses_current_origin_without_repeating_mutation() { + let directory = tempfile::tempdir().unwrap(); + let key = directory.path().join("private-key"); + std::fs::write(&key, b"test-only stable private fingerprint material").unwrap(); + let mut state = test_server_state().await; + let config = &mut Arc::get_mut(&mut state).unwrap().config; + config.gateway_jwt = Some(openshell_core::config::GatewayJwtConfig { + signing_key_path: key, + public_key_path: directory.path().join("public"), + kid_path: directory.path().join("kid"), + gateway_id: "test".into(), + ttl_secs: None, + }); + let request = CreateSandboxRequest { + name: "origin-replay".into(), + spec: Some(SandboxSpec::default()), + workspace_scope: Some(openshell_core::proto::workspace_selector("default")), + service_exposures: vec![SandboxServiceExposure { + service: "web".into(), + target_port: 8080, + ..Default::default() + }], + request_id: uuid::Uuid::new_v4().to_string(), + ..Default::default() + }; + let original = run(&state, authed_request(request.clone())) + .await + .unwrap() + .into_inner(); + Arc::get_mut(&mut state) + .unwrap() + .config + .supervisor_app_listener = openshell_core::app_listener::AppListenerConfig { + enabled: true, + port: 9443, + domain: "services.example.com".into(), + public_url: Some("https://services.example.com:10443".into()), + }; + let replay = run(&state, authed_request(request)).await.unwrap(); + assert_eq!(replay.metadata().get("openshell-replayed").unwrap(), "true"); + assert_eq!( + original.sandbox.as_ref().unwrap().metadata, + replay.get_ref().sandbox.as_ref().unwrap().metadata + ); + assert_eq!( + replay.get_ref().service_urls["web"], + "https://default--origin-replay--web.services.example.com:10443/" + ); + assert_eq!( + replay.get_ref().application_service_urls, + replay.get_ref().service_urls + ); + assert_eq!( + replay.get_ref().gateway_service_urls, + original.gateway_service_urls + ); +} + #[tokio::test] async fn create_sandbox_replay_preserves_service_urls() { let directory = tempfile::tempdir().unwrap(); diff --git a/crates/openshell-server/src/grpc/sandbox.rs b/crates/openshell-server/src/grpc/sandbox.rs index a797da2589..c94f963550 100644 --- a/crates/openshell-server/src/grpc/sandbox.rs +++ b/crates/openshell-server/src/grpc/sandbox.rs @@ -363,6 +363,8 @@ pub(super) async fn handle_update_sandbox( ) .await?; Ok(Response::new(SandboxResponse { + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), sandbox: Some(sandbox), service_urls: HashMap::new(), })) @@ -822,6 +824,8 @@ async fn handle_create_sandbox_inner( .await?; let mut service_urls = HashMap::with_capacity(request.service_exposures.len()); + let mut application_service_urls = HashMap::new(); + let mut gateway_service_urls = HashMap::new(); for exposure in &request.service_exposures { let endpoint = match super::service::expose_service_endpoint( state, @@ -863,7 +867,12 @@ async fn handle_create_sandbox_inner( return Err(exposure_error); } }; - service_urls.insert(exposure.service.clone(), endpoint.into_inner().url); + let endpoint = endpoint.into_inner(); + if !endpoint.application_url.is_empty() { + application_service_urls.insert(exposure.service.clone(), endpoint.application_url); + } + gateway_service_urls.insert(exposure.service.clone(), endpoint.gateway_url); + service_urls.insert(exposure.service.clone(), endpoint.url); } info!( @@ -872,6 +881,8 @@ async fn handle_create_sandbox_inner( "CreateSandbox request completed successfully" ); Ok(Response::new(SandboxResponse { + application_service_urls, + gateway_service_urls, sandbox: Some(sandbox), service_urls, })) @@ -1050,6 +1061,8 @@ pub(super) async fn handle_get_sandbox( None }; let mut response = Response::new(SandboxResponse { + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), sandbox: Some(sandbox), service_urls: HashMap::new(), }); @@ -1873,6 +1886,8 @@ async fn handle_stop_sandbox_inner( let sandbox = state.compute.stop_sandbox(workspace, name).await?; info!(sandbox_name = %name, "StopSandbox request completed successfully"); Ok(Response::new(SandboxResponse { + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), sandbox: Some(sandbox), service_urls: HashMap::new(), })) @@ -1928,6 +1943,8 @@ async fn handle_start_sandbox_inner( .project_endpoint_status(&mut sandbox, remote_authority); info!(sandbox_name = %name, "StartSandbox request completed successfully"); Ok(Response::new(SandboxResponse { + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), sandbox: Some(sandbox), service_urls: HashMap::new(), })) @@ -1969,6 +1986,8 @@ async fn handle_suspend_sandbox_inner( let sandbox = state.compute.suspend_sandbox(workspace, name).await?; info!(sandbox_name = %name, "SuspendSandbox request completed successfully"); Ok(Response::new(SandboxResponse { + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), sandbox: Some(sandbox), service_urls: HashMap::new(), })) @@ -2025,6 +2044,8 @@ async fn handle_resume_sandbox_inner( .project_endpoint_status(&mut sandbox, remote_authority); info!(sandbox_name = %name, "ResumeSandbox request completed successfully"); Ok(Response::new(SandboxResponse { + application_service_urls: HashMap::new(), + gateway_service_urls: HashMap::new(), sandbox: Some(sandbox), service_urls: HashMap::new(), })) @@ -4278,6 +4299,40 @@ async fn run_exec_with_russh( // Tests // --------------------------------------------------------------------------- +/// Replays preserve original service names and sandbox identity while using +/// current deployment origins. No service mutation is performed here. +pub(super) fn refresh_response_urls(state: &ServerState, response: &mut SandboxResponse) { + let Some(sandbox) = response.sandbox.as_ref() else { + return; + }; + response.application_service_urls.clear(); + response.gateway_service_urls.clear(); + for (service, url) in &mut response.service_urls { + let gateway = crate::service_routing::endpoint_url( + &state.config, + sandbox.object_workspace(), + sandbox.object_name(), + service, + ) + .unwrap_or_default(); + let application = state.config.supervisor_app_listener.service_url( + sandbox.object_workspace(), + sandbox.object_name(), + service, + state.config.service_routing_discriminator(), + ); + response + .gateway_service_urls + .insert(service.clone(), gateway.clone()); + *url = application.clone().unwrap_or(gateway); + if let Some(application) = application { + response + .application_service_urls + .insert(service.clone(), application); + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/openshell-server/src/grpc/service.rs b/crates/openshell-server/src/grpc/service.rs index d063c315ad..5fa6df67e1 100644 --- a/crates/openshell-server/src/grpc/service.rs +++ b/crates/openshell-server/src/grpc/service.rs @@ -227,14 +227,13 @@ pub(super) async fn expose_service_endpoint( meta.resource_version = result.resource_version; } - let url = service_routing::endpoint_url(&state.config, workspace, sandbox_name, service) - .unwrap_or_default(); - service_routing::emit_service_endpoint_config_event(&endpoint, &url, created); - - Ok(Response::new(ServiceEndpointResponse { - endpoint: Some(endpoint), - url, - })) + let response = service_endpoint_response(state, endpoint); + service_routing::emit_service_endpoint_config_event( + response.endpoint.as_ref().expect("endpoint populated"), + &response.url, + created, + ); + Ok(Response::new(response)) } pub(super) async fn handle_get_service( @@ -438,6 +437,12 @@ pub(super) async fn configured_app_routes( target_port: ep.target_port, authorization_mode: ep.authorization_mode, resource_version: ep.metadata.as_ref().map_or(0, |m| m.resource_version), + application_hostname: state.config.supervisor_app_listener.service_hostname( + sandbox.object_workspace(), + sandbox.object_name(), + &ep.name, + state.config.service_routing_discriminator(), + ), }) .collect(); service_routes.sort_by(|a, b| a.name.cmp(&b.name)); @@ -495,14 +500,41 @@ fn service_endpoint_response( Ok(ServiceAuthorizationMode::Unspecified | ServiceAuthorizationMode::Strip) | Err(_) => ServiceAuthorizationMode::Strip as i32, }; - let workspace = endpoint.object_workspace(); - let url = - service_routing::endpoint_url(&state.config, workspace, &endpoint.sandbox, &endpoint.name) - .unwrap_or_default(); - ServiceEndpointResponse { + let mut response = ServiceEndpointResponse { endpoint: Some(endpoint), - url, - } + ..Default::default() + }; + refresh_response_urls(state, &mut response); + response +} + +/// Recompute presentation from current configuration, including durable replays. +pub(super) fn refresh_response_urls(state: &ServerState, response: &mut ServiceEndpointResponse) { + let Some(endpoint) = response.endpoint.as_ref() else { + return; + }; + response.gateway_url = service_routing::endpoint_url( + &state.config, + endpoint.object_workspace(), + &endpoint.sandbox, + &endpoint.name, + ) + .unwrap_or_default(); + response.application_url = state + .config + .supervisor_app_listener + .service_url( + endpoint.object_workspace(), + &endpoint.sandbox, + &endpoint.name, + state.config.service_routing_discriminator(), + ) + .unwrap_or_default(); + response.url = if response.application_url.is_empty() { + response.gateway_url.clone() + } else { + response.application_url.clone() + }; } #[allow(clippy::result_large_err)] @@ -590,6 +622,7 @@ mod tests { enabled: true, port: 9443, domain: "services.example.com".into(), + public_url: None, }; seed_sandbox(&state, "limited").await; state @@ -705,6 +738,99 @@ mod tests { ); } + #[tokio::test] + async fn configured_application_origin_is_published_before_installation() { + let mut state = test_server_state().await; + Arc::get_mut(&mut state) + .unwrap() + .config + .supervisor_app_listener = openshell_core::app_listener::AppListenerConfig { + enabled: true, + port: 9443, + domain: "services.example.com".into(), + public_url: Some("https://services.example.com:10443".into()), + }; + seed_sandbox(&state, "demo").await; + for name in ["", "web"] { + let created = handle_expose_service( + &state, + authed_request(ExposeServiceRequest { + workspace_scope: Some(openshell_core::proto::workspace_selector("default")), + sandbox: "demo".into(), + name: name.into(), + target_port: 8080, + ..Default::default() + }), + ) + .await + .unwrap() + .into_inner(); + let label = if name.is_empty() { + "default--demo" + } else { + "default--demo--web" + }; + assert_eq!( + created.url, + format!("https://{label}.services.example.com:10443/") + ); + assert_eq!(created.application_url, created.url); + assert!(created.gateway_url.contains("openshell.localhost")); + let fetched = handle_get_service( + &state, + authed_request(GetServiceRequest { + workspace_scope: Some(openshell_core::proto::workspace_selector("default")), + sandbox: "demo".into(), + name: name.into(), + }), + ) + .await + .unwrap() + .into_inner(); + assert_eq!(created, fetched); + } + } + + #[tokio::test] + async fn hashed_gateway_url_keeps_the_independent_application_origin() { + let mut state = test_server_state().await; + let config = &mut Arc::get_mut(&mut state).unwrap().config; + config.name = "gateway-a".into(); + config.service_routing.service_hostname_format = ServiceHostnameFormat::Hashed; + config.service_routing.base_domains = vec!["gateway.example.com".into()]; + config.supervisor_app_listener = openshell_core::app_listener::AppListenerConfig { + enabled: true, + port: 9443, + domain: "services.example.com".into(), + public_url: Some("https://services.example.com:10443".into()), + }; + let mut response = ServiceEndpointResponse { + endpoint: Some(ServiceEndpoint { + metadata: Some(ObjectMeta { + workspace: "default".into(), + ..Default::default() + }), + sandbox: "demo".into(), + name: "web".into(), + ..Default::default() + }), + ..Default::default() + }; + refresh_response_urls(&state, &mut response); + let gateway = url::Url::parse(&response.gateway_url).unwrap(); + let label = + service_routing::service_route_hash_label("gateway-a", "default", "demo", Some("web")); + assert_eq!( + gateway.host_str(), + Some(format!("{label}.gateway.example.com").as_str()) + ); + assert_eq!( + response.application_url, + format!("https://{label}.services.example.com:10443/") + ); + assert_eq!(response.url, response.application_url); + } + #[test] fn validates_good_endpoint_name() { validate_endpoint_name("service", "web-api", 28).unwrap(); @@ -717,6 +843,7 @@ mod tests { .expect("unshared test state") .config .supervisor_app_listener = openshell_core::app_listener::AppListenerConfig { + public_url: None, enabled: true, port: 9443, domain: "services.example.com".into(), diff --git a/crates/openshell-server/src/grpc/service_advertisement.rs b/crates/openshell-server/src/grpc/service_advertisement.rs index daefe14c7a..66f70d59a6 100644 --- a/crates/openshell-server/src/grpc/service_advertisement.rs +++ b/crates/openshell-server/src/grpc/service_advertisement.rs @@ -505,6 +505,7 @@ pub mod tests { target_port: 8080, authorization_mode: 1, resource_version: 2, + application_hostname: String::new(), } } @@ -616,6 +617,17 @@ pub mod tests { pub async fn installed_fixture() -> (Arc, OwnerGuard, SupervisorServiceAdvertisement) { + installed_fixture_for_gateway( + "gateway-a", + openshell_core::config::ServiceHostnameFormat::Readable, + ) + .await + } + + pub async fn installed_fixture_for_gateway( + gateway: &str, + format: openshell_core::config::ServiceHostnameFormat, + ) -> (Arc, OwnerGuard, SupervisorServiceAdvertisement) { use openshell_core::proto::datamodel::v1::ObjectMeta; use openshell_core::proto::{SandboxPolicy, SandboxSpec, ServiceEndpoint}; let endpoint = openshell_core::proto::compute::v1::SupervisorAppEndpoint { @@ -641,11 +653,15 @@ pub mod tests { .unwrap() .config .supervisor_app_listener = openshell_core::app_listener::AppListenerConfig { + public_url: None, enabled: true, port: 9443, domain: "services.example.com".into(), }; - Arc::get_mut(&mut state).unwrap().admin_role = "openshell-admin".into(); + let inner = Arc::get_mut(&mut state).unwrap(); + inner.config.name = gateway.into(); + inner.config.service_routing.service_hostname_format = format; + inner.admin_role = "openshell-admin".into(); let sandbox = Sandbox { metadata: Some(ObjectMeta { id: "sandbox".into(), diff --git a/crates/openshell-server/src/grpc/service_discovery.rs b/crates/openshell-server/src/grpc/service_discovery.rs index d552833d51..8209481bfb 100644 --- a/crates/openshell-server/src/grpc/service_discovery.rs +++ b/crates/openshell-server/src/grpc/service_discovery.rs @@ -196,17 +196,25 @@ async fn collect(state: &ServerState, workspace: &str) -> Result 63 { + let label = hostname + .strip_suffix(&format!(".{}", installation.service_domain)) + .ok_or_else(|| Status::internal("installed hostname domain mismatch"))?; + if label.is_empty() || label.len() > 63 || label.contains('.') { return Err(Status::failed_precondition( "service hostname exceeds DNS label limit", )); } - let hostname = format!("{label}.{}", installation.service_domain); let identity = serde_json::to_vec(&( "openshell-installation-v1", &installation.session_id, @@ -300,6 +308,83 @@ mod tests { } } + #[tokio::test] + async fn hashed_urls_discovery_and_installed_routes_use_the_same_gateway_identity() { + use super::super::service_advertisement::tests::installed_fixture_for_gateway; + use openshell_core::config::ServiceHostnameFormat; + let (mut state, owner, report) = + installed_fixture_for_gateway("gateway-a", ServiceHostnameFormat::Hashed).await; + Arc::get_mut(&mut state) + .unwrap() + .config + .supervisor_app_listener + .public_url = Some("https://services.example.com:10443".into()); + accept(&state, &owner, &report, current_time_ms() + 10_000) + .await + .unwrap(); + let first = snapshot(&state, "default").await.unwrap(); + let hostname = &first.routes[0].hostname; + let label = crate::service_routing::service_route_hash_label( + "gateway-a", + "default", + "demo", + Some("web"), + ); + assert_eq!(*hostname, format!("{label}.services.example.com")); + assert_eq!(*hostname, report.routes[0].application_hostname); + let endpoint = state + .store + .get_message::("endpoint") + .await + .unwrap() + .unwrap(); + let mut response = openshell_core::proto::ServiceEndpointResponse { + endpoint: Some(endpoint), + ..Default::default() + }; + super::super::service::refresh_response_urls(&state, &mut response); + assert_eq!( + response.application_url, + format!("https://{hostname}:10443/") + ); + assert_eq!(response.url, response.application_url); + + // A renamed installation cannot advertise a hostname it has not installed. + Arc::get_mut(&mut state).unwrap().config.name = "gateway-b".into(); + assert!(snapshot(&state, "default").await.unwrap().routes.is_empty()); + assert_eq!( + accept(&state, &owner, &report, current_time_ms() + 10_000) + .await + .unwrap_err() + .code(), + tonic::Code::FailedPrecondition + ); + let sandbox = state + .store + .get_message::("sandbox") + .await + .unwrap() + .unwrap(); + let config = super::super::policy::build_sandbox_config_snapshot(&state, &sandbox) + .await + .unwrap(); + let replacement = openshell_core::proto::SupervisorServiceAdvertisement { + config_revision: config.config_revision, + routes: config.service_routes, + lease_sequence: report.lease_sequence + 1, + ..report + }; + accept(&state, &owner, &replacement, current_time_ms() + 10_000) + .await + .unwrap(); + let second = snapshot(&state, "default").await.unwrap(); + assert_ne!(first.routes[0].hostname, second.routes[0].hostname); + assert_ne!( + first.routes[0].installation_version, + second.routes[0].installation_version + ); + } + #[tokio::test] async fn snapshot_is_shared_between_gateway_states_and_rejects_expired_or_replaced_owners() { let (state, owner, report) = installed_fixture().await; diff --git a/crates/openshell-server/src/service_routing.rs b/crates/openshell-server/src/service_routing.rs index ece2add1e0..d5d1ac6880 100644 --- a/crates/openshell-server/src/service_routing.rs +++ b/crates/openshell-server/src/service_routing.rs @@ -9,6 +9,8 @@ use axum::{ }; use http::{HeaderMap, HeaderValue, Method, Request, Response, StatusCode, header}; use hyper_util::rt::TokioIo; +pub use openshell_core::app_listener::service_route_hash_label; +use openshell_core::app_listener::service_route_label; use openshell_core::config::ServiceRoutingConfig; use openshell_core::proto::{ Sandbox, SandboxPhase, ServiceAuthorizationMode, ServiceEndpoint, TcpRelayTarget, relay_open, @@ -19,7 +21,6 @@ use openshell_ocsf::{ HttpActivityBuilder, HttpRequest, HttpResponse as OcsfHttpResponse, NetworkActivityBuilder, OcsfEvent, SeverityId, StateId, StatusId, Url as OcsfUrl, }; -use sha2::{Digest, Sha256}; use std::collections::HashMap; use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; @@ -198,71 +199,12 @@ fn endpoint_host( service: &str, ) -> Option { let base_domain = config.base_domains.first()?; - Some(discriminator.map_or_else( - || { - if service.is_empty() { - format!("{workspace}--{sandbox}.{base_domain}") - } else { - format!("{workspace}--{sandbox}--{service}.{base_domain}") - } - }, - |gateway_name| { - format!( - "{}.{}", - service_route_hash_label( - gateway_name, - workspace, - sandbox, - (!service.is_empty()).then_some(service), - ), - base_domain - ) - }, + Some(format!( + "{}.{base_domain}", + service_route_label(discriminator, workspace, sandbox, service) )) } -/// Returns the stable, one-label DNS name for a service route. -pub fn service_route_hash_label( - gateway_name: &str, - workspace: &str, - sandbox: &str, - service: Option<&str>, -) -> String { - let mut hasher = Sha256::new(); - hasher.update(gateway_name.as_bytes()); - hasher.update([0]); - hasher.update(workspace.as_bytes()); - hasher.update([0]); - hasher.update(sandbox.as_bytes()); - hasher.update([0]); - hasher.update(service.unwrap_or("").as_bytes()); - let digest = hasher.finalize(); - base32_lower_no_padding(&digest[..10]) -} - -fn base32_lower_no_padding(bytes: &[u8]) -> String { - const ALPHABET: &[u8; 32] = b"abcdefghijklmnopqrstuvwxyz234567"; - let mut output = String::with_capacity(16); - let mut accumulator = 0_u32; - let mut bits = 0_u8; - for byte in bytes { - accumulator = (accumulator << 8) | u32::from(*byte); - bits += 8; - while bits >= 5 { - bits -= 5; - output.push(char::from( - ALPHABET[((accumulator >> bits) & 0x1f) as usize], - )); - } - } - if bits > 0 { - output.push(char::from( - ALPHABET[((accumulator << (5 - bits)) & 0x1f) as usize], - )); - } - output -} - /// A resolved service-route destination. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ServiceRouteTarget { @@ -1528,6 +1470,48 @@ mod tests { ); } + #[test] + fn switching_ingress_preserves_public_urls_in_both_hostname_modes() { + for format in [ + ServiceHostnameFormat::Readable, + ServiceHostnameFormat::Hashed, + ] { + let mut cfg = openshell_core::Config::new(Some(tls_config())) + .with_bind_address("0.0.0.0:443".parse().unwrap()) + .with_server_sans(["*.apps.example.com"]) + .with_name("gateway-a") + .with_service_hostname_format(format); + cfg.supervisor_app_listener = openshell_core::app_listener::AppListenerConfig { + enabled: true, + port: 9443, + domain: "apps.example.com".into(), + public_url: Some("https://apps.example.com".into()), + }; + for workspace in ["default", "team-a"] { + for service in ["", "web"] { + let application_url = cfg + .supervisor_app_listener + .service_url( + workspace, + "my-sandbox", + service, + cfg.service_routing_discriminator(), + ) + .unwrap(); + // Switching to gateway ingress removes listener configuration, + // but preserves the installation identity and public origin. + let listener = std::mem::take(&mut cfg.supervisor_app_listener); + let gateway_url = endpoint_url(&cfg, workspace, "my-sandbox", service).unwrap(); + assert_eq!( + application_url, gateway_url, + "{format:?}: {workspace}/{service}" + ); + cfg.supervisor_app_listener = listener; + } + } + } + } + #[test] fn service_route_hash_label_uses_stable_fixed_vectors() { assert_eq!( diff --git a/crates/openshell-server/src/storage_proto.rs b/crates/openshell-server/src/storage_proto.rs index 8613a60f91..9e13497137 100644 --- a/crates/openshell-server/src/storage_proto.rs +++ b/crates/openshell-server/src/storage_proto.rs @@ -178,7 +178,7 @@ mod tests { // image wire identity, now with explicit presence. Compute configuration // updates use a separate capability/service and add one classified RPC. const PUBLIC_RPC_SCHEMA_SHA256: &str = - "1fce709ed9a1c91e307c46c5fe68be470cbe88ca70ea0087e771c973a9052308"; + "724ac81b9c3071ecb67cb3ca1530f3c49820aa69acf3878778ecc06fa40f69a7"; const DURABLE_SCHEMA_SHA256: &str = "cb15f8f79a855064a43fe8c6cd908bf07eca6604e2bf8ab7d2de0ef33e97da4e"; const PUBLIC_DURABLE_OVERLAP_SHA256: &str = @@ -723,7 +723,7 @@ mod tests { overlap_hash.as_str(), ), ( - (344, 31), + (346, 31), (100, 22), (86, 22), PUBLIC_RPC_SCHEMA_SHA256, diff --git a/crates/openshell-supervisor/README.md b/crates/openshell-supervisor/README.md index 1acb665472..52803aeec9 100644 --- a/crates/openshell-supervisor/README.md +++ b/crates/openshell-supervisor/README.md @@ -313,3 +313,9 @@ seconds from issuance, independently of configuration freshness. Expiry closes streams; later renewal cannot revive them. Takeover preserves the workload and public hostname while replacing the app endpoint. Clients reconnect after an interruption; sockets are not migrated. + +The gateway supplies a canonical `application_hostname` with each installed +service route. The listener dispatches exact hashed or readable SNI names to +the correct sandbox instance, validates HTTP Host against SNI, and fences old +streams when a hostname changes. Readable aliases remain accepted during +migration; an older gateway omitting the field retains readable routing. diff --git a/crates/openshell-supervisor/src/app_listener.rs b/crates/openshell-supervisor/src/app_listener.rs index c486f55b3b..fc10c8f82f 100644 --- a/crates/openshell-supervisor/src/app_listener.rs +++ b/crates/openshell-supervisor/src/app_listener.rs @@ -69,6 +69,7 @@ impl Config { }; let address: SocketAddr = address.parse().into_diagnostic()?; openshell_core::app_listener::AppListenerConfig { + public_url: None, enabled: true, port: address.port(), domain: domain.clone(), @@ -280,7 +281,7 @@ impl Routes { return; } if let Some(registration) = &self.registration - && !registration.bind(&result.workspace, &self.stopped) + && !registration.bind(&result.workspace, &routes, &self.stopped) { self.suspend(); return; @@ -366,15 +367,19 @@ impl Routes { domain: &str, ) -> Option<(SandboxServiceRoute, RouteToken)> { let encoded = sni.strip_suffix(&format!(".{domain}"))?; - let service = if encoded == prefix { - "" - } else { - encoded.strip_prefix(&format!("{prefix}--"))? - }; let snapshot = self.sender.borrow(); if self.serving_expiry(&snapshot)? <= Instant::now() { return None; } + let service = if let Some(route) = snapshot.routes.values().find(|route| { + !route.application_hostname.is_empty() && route.application_hostname == sni + }) { + route.name.as_str() + } else if encoded == prefix { + "" + } else { + encoded.strip_prefix(&format!("{prefix}--"))? + }; Some(( snapshot.routes.get(service)?.clone(), RouteToken { @@ -432,12 +437,38 @@ struct Registration { router: Router, sandbox: String, prefix: Mutex, + hostnames: Mutex>, sender: mpsc::Sender, } impl Registration { - fn bind(&self, workspace: &str, stopped: &AtomicBool) -> bool { + fn bind( + &self, + workspace: &str, + routes: &BTreeMap, + stopped: &AtomicBool, + ) -> bool { let next = format!("{workspace}--{}", self.sandbox); + let mut names = vec![next.clone()]; + for route in routes.values() { + if route.application_hostname.is_empty() { + continue; // Older gateways only supplied the readable route identity. + } + let Some(label) = route + .application_hostname + .strip_suffix(&format!(".{}", self.router.domain)) + else { + return false; + }; + if !valid_label(label) || route.application_hostname.len() > 253 { + return false; + } + names.push(route.application_hostname.clone()); + } + names.sort(); + if names.windows(2).any(|pair| pair[0] == pair[1]) { + return false; + } let mut prefix = self .prefix .lock() @@ -448,45 +479,62 @@ impl Registration { if stopped.load(Ordering::Acquire) { return false; } + let mut hostnames = self + .hostnames + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut entries = self .router .registrations .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - if entries - .get(&next) - .is_some_and(|sender| !sender.same_channel(&self.sender)) - { + // Check the whole replacement before changing any registration. An exact + // hashed label must never select an arbitrary sandbox on a shared host. + if names.iter().any(|name| { + entries + .get(name) + .is_some_and(|sender| !sender.same_channel(&self.sender)) + }) { return false; } - if *prefix != next { + for name in hostnames.iter() { if entries - .get(&*prefix) - .is_some_and(|s| s.same_channel(&self.sender)) + .get(name) + .is_some_and(|sender| sender.same_channel(&self.sender)) { - entries.remove(&*prefix); + entries.remove(name); } - prefix.clone_from(&next); } - entries.insert(next, self.sender.clone()); + for name in &names { + entries.insert(name.clone(), self.sender.clone()); + } + *prefix = next; + *hostnames = names; true } fn remove(&self) { - let prefix = self + // Use the same lock as bind's stopped check, including exact aliases. + let _prefix = self .prefix .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); + let hostnames = self + .hostnames + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut entries = self .router .registrations .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - if entries - .get(&*prefix) - .is_some_and(|s| s.same_channel(&self.sender)) - { - entries.remove(&*prefix); + for name in hostnames.iter() { + if entries + .get(name) + .is_some_and(|sender| sender.same_channel(&self.sender)) + { + entries.remove(name); + } } } } @@ -553,6 +601,7 @@ impl Router { router: self.clone(), sandbox: sandbox.to_string(), prefix: Mutex::new(String::new()), + hostnames: Mutex::new(Vec::new()), sender, }); Ok(InstanceListener { @@ -592,10 +641,15 @@ impl BoundListener { let Ok(Ok(tls)) = tokio::time::timeout(HANDSHAKE_TIMEOUT, acceptor.accept(stream)).await else { return; }; let Some(sni) = tls.get_ref().1.server_name() else { return; }; let Some(label) = sni.strip_suffix(&format!(".{domain}")) else { return; }; - let mut segments = label.split("--"); - let (Some(workspace), Some(sandbox)) = (segments.next(), segments.next()) else { return; }; - let prefix = format!("{workspace}--{sandbox}"); - let sender = dispatch.registrations.lock().unwrap_or_else(std::sync::PoisonError::into_inner).get(&prefix).cloned(); + let sender = { + let entries = dispatch.registrations.lock().unwrap_or_else(std::sync::PoisonError::into_inner); + entries.get(sni).cloned().or_else(|| { + // Keep readable aliases during migration, as gateway routing does. + let mut segments = label.split("--"); + let (Some(workspace), Some(sandbox)) = (segments.next(), segments.next()) else { return None; }; + entries.get(&format!("{workspace}--{sandbox}")).cloned() + }) + }; if let Some(sender) = sender { let _ = sender.try_send(Accepted { tls, permit }); } }); } @@ -945,6 +999,7 @@ mod tests { target_port: 8080, authorization_mode: 1, resource_version: 1, + application_hostname: String::new(), }; let mut config = installed(vec![route.clone()]); config.config_revision = 42; @@ -983,6 +1038,7 @@ mod tests { target_port: 8080, authorization_mode: 1, resource_version: 1, + application_hostname: String::new(), }; routes.install(&installed(vec![route.clone()])); assert!( @@ -1209,6 +1265,7 @@ mod tests { target_port: 8080, authorization_mode: 1, resource_version: 1, + application_hostname: String::new(), }; routes.install(&installed(vec![route.clone()])); let connector = Arc::new(Connector { @@ -1282,6 +1339,96 @@ mod tests { ); } + #[tokio::test] + async fn hashed_sni_routes_to_the_correct_instance_and_replacement_withdraws_old_names() { + let f = fixture(); + let hostname = |gateway, sandbox, service| { + format!( + "{}.svc.openshell.internal", + openshell_core::app_listener::service_route_hash_label( + gateway, + "default", + sandbox, + Some(service) + ) + ) + }; + let first_host = hostname("gateway-a", "sandbox-a", "web"); + let mut first = f.route.clone(); + first.application_hostname.clone_from(&first_host); + f.routes.install(&installed(vec![first.clone()])); + assert!( + request(&f, &first_host, &first_host, "") + .await + .starts_with("HTTP/1.1 200") + ); + assert!( + request(&f, &first_host, HOST, "") + .await + .starts_with("HTTP/1.1 421") + ); + // Readable migration aliases remain usable, as with gateway-mediated hosts. + assert!( + request(&f, HOST, HOST, "") + .await + .starts_with("HTTP/1.1 200") + ); + let before = f.connector.calls.load(Ordering::SeqCst); + let foreign = hostname("gateway-b", "sandbox-a", "web"); + assert!( + !request(&f, &foreign, &foreign, "") + .await + .starts_with("HTTP/1.1 200") + ); + assert_eq!(f.connector.calls.load(Ordering::SeqCst), before); + + let second = f.host.router().instance("sandbox-b").unwrap(); + let second_routes = second.routes.clone(); + let connector = Arc::new(Connector { + calls: AtomicUsize::new(0), + }); + let handle = second.start(rpc_test::loopback(connector.clone())); + // A canonical hostname collision cannot steal another instance's mapping. + second_routes.install(&installed(vec![first.clone()])); + assert!(second_routes.sender.borrow().routes.is_empty()); + let second_host = hostname("gateway-a", "sandbox-b", "web"); + second_routes.install(&installed(vec![SandboxServiceRoute { + application_hostname: second_host.clone(), + endpoint_id: "endpoint-b".into(), + ..first.clone() + }])); + assert!( + request(&f, &second_host, &second_host, "") + .await + .starts_with("HTTP/1.1 200") + ); + assert_eq!(connector.calls.load(Ordering::SeqCst), 1); + let (_, token) = f + .routes + .resolve(&first_host, "default--sandbox-a", "svc.openshell.internal") + .unwrap(); + let mut replacement = first.clone(); + replacement.application_hostname.clone_from(&foreign); + f.routes.install(&installed(vec![replacement])); + assert!(!f.routes.valid(&first, token)); + assert!( + !request(&f, &first_host, &first_host, "") + .await + .starts_with("HTTP/1.1 200") + ); + assert!( + request(&f, &foreign, &foreign, "") + .await + .starts_with("HTTP/1.1 200") + ); + drop(handle); + assert!( + !request(&f, &second_host, &second_host, "") + .await + .starts_with("HTTP/1.1 200") + ); + } + #[tokio::test] async fn shared_listener_keeps_instance_connectors_and_lifetimes_separate() { let f = fixture(); @@ -1364,7 +1511,18 @@ mod tests { }); let mut installing_routes = f.routes.clone(); installing_routes.install_probe = Some(probe.clone()); - let snapshot = installed(vec![f.route.clone()]); + let hostname = format!( + "{}.svc.openshell.internal", + openshell_core::app_listener::service_route_hash_label( + "gateway-a", + "default", + "sandbox-a", + Some("web") + ) + ); + let mut route = f.route.clone(); + route.application_hostname.clone_from(&hostname); + let snapshot = installed(vec![route.clone()]); let installer = std::thread::spawn(move || installing_routes.install(&snapshot)); probe.checked_running.wait(); // Complete shutdown after install's optimistic stopped check but @@ -1380,12 +1538,20 @@ mod tests { .unwrap() .contains_key("default--sandbox-a") ); + assert!( + !f.host + .router() + .registrations + .lock() + .unwrap() + .contains_key(&hostname) + ); let replacement = f.host.router().instance("sandbox-a").unwrap(); - replacement.routes.install(&installed(vec![f.route])); + replacement.routes.install(&installed(vec![route])); assert!( replacement .routes - .resolve(HOST, "default--sandbox-a", "svc.openshell.internal") + .resolve(&hostname, "default--sandbox-a", "svc.openshell.internal") .is_some(), "the stopped instance must not prevent its replacement from serving" ); @@ -1400,6 +1566,7 @@ mod tests { target_port: 8080, authorization_mode: 1, resource_version: 1, + application_hostname: String::new(), }; routes.install(&installed(vec![route.clone()])); for _ in 0..10 { @@ -1512,47 +1679,68 @@ mod tests { assert!(!headers.contains_key("x-internal")); } #[tokio::test] - async fn websocket_echo_closes_on_route_withdrawal() { - let f = fixture(); - let stream = openshell_core::net::connect_tcp_nodelay_best_effort(&[f.address]) - .await - .unwrap(); - let name = rustls::pki_types::ServerName::try_from(HOST.to_string()).unwrap(); - let tls = tokio_rustls::TlsConnector::from(f.tls.clone()) - .connect(name, stream) - .await - .unwrap(); - let mut request = format!("wss://{HOST}/echo").into_client_request().unwrap(); - request - .headers_mut() - .insert(header::ORIGIN, format!("https://{HOST}").parse().unwrap()); - let (mut ws, response) = tokio_tungstenite::client_async(request, tls).await.unwrap(); - assert_eq!(response.headers()["x-test-upstream-host"], HOST); - assert_eq!( - response.headers()["x-test-upstream-origin"], - format!("https://{HOST}") - ); - ws.send(tokio_tungstenite::tungstenite::Message::Text( - "hello".into(), - )) - .await - .unwrap(); - assert_eq!( - tokio::time::timeout(Duration::from_secs(2), ws.next()) + async fn readable_and_hashed_websocket_echo_closes_on_route_withdrawal() { + for hashed in [false, true] { + let f = fixture(); + let hostname = if hashed { + format!( + "{}.svc.openshell.internal", + openshell_core::app_listener::service_route_hash_label( + "gateway-a", + "default", + "sandbox-a", + Some("web") + ) + ) + } else { + HOST.to_string() + }; + let mut route = f.route.clone(); + route.application_hostname.clone_from(&hostname); + f.routes.install(&installed(vec![route])); + let stream = openshell_core::net::connect_tcp_nodelay_best_effort(&[f.address]) .await - .unwrap() - .unwrap() - .unwrap() - .to_text() - .unwrap(), - "hello" - ); - f.routes.suspend(); - let closed = tokio::time::timeout(Duration::from_secs(2), ws.next()) + .unwrap(); + let name = rustls::pki_types::ServerName::try_from(hostname.clone()).unwrap(); + let tls = tokio_rustls::TlsConnector::from(f.tls.clone()) + .connect(name, stream) + .await + .unwrap(); + let mut request = format!("wss://{hostname}/echo") + .into_client_request() + .unwrap(); + request.headers_mut().insert( + header::ORIGIN, + format!("https://{hostname}").parse().unwrap(), + ); + let (mut ws, response) = tokio_tungstenite::client_async(request, tls).await.unwrap(); + assert_eq!(response.headers()["x-test-upstream-host"], hostname); + assert_eq!( + response.headers()["x-test-upstream-origin"], + format!("https://{hostname}") + ); + ws.send(tokio_tungstenite::tungstenite::Message::Text( + "hello".into(), + )) .await .unwrap(); - assert!(closed.is_none() || closed.unwrap().is_err()); - assert_eq!(f.connector.calls.load(Ordering::SeqCst), 1); + assert_eq!( + tokio::time::timeout(Duration::from_secs(2), ws.next()) + .await + .unwrap() + .unwrap() + .unwrap() + .to_text() + .unwrap(), + "hello" + ); + f.routes.suspend(); + let closed = tokio::time::timeout(Duration::from_secs(2), ws.next()) + .await + .unwrap(); + assert!(closed.is_none() || closed.unwrap().is_err()); + assert_eq!(f.connector.calls.load(Ordering::SeqCst), 1); + } } type TestWebSocket = diff --git a/docs/how-it-works/gateways/configuration.mdx b/docs/how-it-works/gateways/configuration.mdx index dfd43556af..6e7ce0e4be 100644 --- a/docs/how-it-works/gateways/configuration.mdx +++ b/docs/how-it-works/gateways/configuration.mdx @@ -1767,6 +1767,7 @@ ingress remains available independently. enabled = true port = 9443 domain = "services.example.com" +public_url = "https://services.example.com" [openshell.drivers.kubernetes.sandbox_runtime.app_listener] tls_secret_name = "openshell-app-tls" @@ -1792,9 +1793,25 @@ Expose services with the existing commands. For direct listener testing, an unnamed service on sandbox `demo` in workspace `default` uses SNI and Host `default--demo.services.example.com`; a service named `web` uses `default--demo--web.services.example.com`. Configure DNS or your ingress to reach -the owning supervisor while preserving that hostname. The discovery API supplies accepted supervisor endpoints. Application URL -publication is separate work; current CLI URLs still identify the existing -gateway path. +the owning supervisor while preserving that hostname. + +Both ingress paths use `[openshell.gateway].service_hostname_format`; the app +listener has no separate naming mode. Keeping the same installation name and +public HTTPS domain/port preserves the URL when switching between gateway and +app-listener ingress. + +With `[openshell.gateway].service_hostname_format = "hashed"`, application URLs +and discovery use the same 16-character label as gateway-mediated URLs. The +label includes the gateway installation name, workspace, sandbox, and service; +application and gateway origins may still have different domains and ports. +Use a unique gateway `name` among installations sharing an application domain. +Supervisors install the gateway-declared hostname and retain readable aliases +for migration. Proxies and controllers consume discovery hostnames as returned. +Upgrade gateways and supervisors together before enabling hashed application +hostnames; older supervisors cannot serve the new hashed routes. + +The discovery API supplies accepted supervisor endpoints. Configure `public_url` +to publish the application origin; otherwise CLI URLs retain the gateway path. Enabled supervisors report installed service routes through their authenticated gateway sessions. The gateway accepts only current configuration and ownership, @@ -1824,3 +1841,23 @@ and HTTP authority when routing directly to the endpoint. Remove absent routes and expire cached entries even during discovery errors. An API error does not mean the workspace has no routes. Watches end after one minute; reconnect and relist when they end. Discovery-only machine credentials are separate work. + +Set `public_url` to the external HTTPS origin of your application ingress. Its +hostname must match `domain`; it cannot contain credentials, a path, query, or +fragment. A nondefault public port is supported, for example +`https://services.example.com:10443`. This port is independent of the internal +listener `port` and the gateway API address. Setting `public_url` requires +explicit listener enablement and does not create DNS, certificates, or ingress. + +When configured, service responses return the application address as `url` and +`application_url`, with `gateway_url` available as a separate alternative. +Create-time exposure returns corresponding preferred, application, and gateway +URL maps. CLI and SDK callers preserve the application origin, including its +port, when the gateway is reached through a port-forward. Without `public_url`, +the existing gateway URL behavior remains. Replayed mutations retain their +original resource identities and use the current URL configuration. A configured +URL does not mean that a supervisor installed the route or ingress is ready. + +Upgrade the CLI alongside the gateway when enabling `public_url`. Older CLI +versions can replace the public application port with the gateway connection's +port. diff --git a/proto/openshell.proto b/proto/openshell.proto index 092a5ddf11..03739a34b7 100644 --- a/proto/openshell.proto +++ b/proto/openshell.proto @@ -1606,6 +1606,11 @@ message SandboxResponse { // Service URLs created by CreateSandbox, keyed by service name. The empty // key identifies the unnamed service. Other sandbox RPCs return an empty map. map service_urls = 2; + // Configured application origins for the same services. These are + // authoritative public addresses, not installation or health evidence. + map application_service_urls = 3; + // Gateway-routed alternatives, independent of application ingress. + map gateway_service_urls = 4; } // List sandboxes response. @@ -1988,10 +1993,15 @@ message ServiceEndpoint { ServiceAuthorizationMode authorization_mode = 7; } -// Response containing a service endpoint and, when available, its local URL. +// Service addresses. A configured address does not establish readiness. message ServiceEndpointResponse { ServiceEndpoint endpoint = 1; + // Preferred address: application_url when configured, otherwise gateway_url. string url = 2; + // Authoritative configured public application address. Preserve its port. + string application_url = 3; + // Gateway-routed alternative; existing gateway routing remains available. + string gateway_url = 4; } // Revoke SSH session request. diff --git a/proto/sandbox.proto b/proto/sandbox.proto index b57c5d4cda..b4d9931dd5 100644 --- a/proto/sandbox.proto +++ b/proto/sandbox.proto @@ -490,6 +490,10 @@ message SandboxServiceRoute { uint32 target_port = 3; int32 authorization_mode = 4; uint64 resource_version = 5; + // Canonical application SNI/Host selected by the gateway's hostname format. + // Includes the application domain, without a scheme or port. An empty value + // from an older gateway retains readable workspace--sandbox[--service] routing. + string application_hostname = 6; } // Connection details for one operator-registered supervisor middleware service. diff --git a/python/openshell/sandbox.py b/python/openshell/sandbox.py index 2eaf6a64fa..d8f98615d6 100644 --- a/python/openshell/sandbox.py +++ b/python/openshell/sandbox.py @@ -535,10 +535,24 @@ class SandboxRef: ) # Stable SSH host identity; absent on older runtimes. host_key_fingerprint: str | None = None + application_service_urls: Mapping[str, str] = field( + default_factory=_ImmutableLabels, compare=False + ) + gateway_service_urls: Mapping[str, str] = field( + default_factory=_ImmutableLabels, compare=False + ) def __post_init__(self) -> None: object.__setattr__(self, "labels", _ImmutableLabels(self.labels)) object.__setattr__(self, "service_urls", _ImmutableLabels(self.service_urls)) + object.__setattr__( + self, + "application_service_urls", + _ImmutableLabels(self.application_service_urls), + ) + object.__setattr__( + self, "gateway_service_urls", _ImmutableLabels(self.gateway_service_urls) + ) @property def phase(self) -> int: @@ -880,7 +894,12 @@ def create( ), timeout=self._timeout, ) - sandbox_ref = _sandbox_ref(response.sandbox, response.service_urls) + sandbox_ref = _sandbox_ref( + response.sandbox, + response.service_urls, + response.application_service_urls, + response.gateway_service_urls, + ) if sandbox_ref.id == "": raise SandboxError("CreateSandbox returned empty sandbox id") return sandbox_ref @@ -909,7 +928,12 @@ def create_from_template( ), timeout=self._timeout, ) - sandbox_ref = _sandbox_ref(response.sandbox, response.service_urls) + sandbox_ref = _sandbox_ref( + response.sandbox, + response.service_urls, + response.application_service_urls, + response.gateway_service_urls, + ) if sandbox_ref.id == "": raise SandboxError("CreateSandbox returned empty sandbox id") return sandbox_ref @@ -1887,6 +1911,8 @@ def _sandbox_conditions( def _sandbox_ref( sandbox: openshell_pb2.Sandbox, service_urls: Mapping[str, str] | None = None, + application_service_urls: Mapping[str, str] | None = None, + gateway_service_urls: Mapping[str, str] | None = None, ) -> SandboxRef: status = sandbox.status if sandbox.HasField("status") else None provenance = ( @@ -1919,6 +1945,8 @@ def _sandbox_ref( labels=sandbox.metadata.labels if sandbox.metadata else {}, created_from_workload_template=provenance, service_urls=service_urls or {}, + application_service_urls=application_service_urls or {}, + gateway_service_urls=gateway_service_urls or {}, host_key_fingerprint=sandbox.host_key_fingerprint or None, ) diff --git a/python/openshell/sandbox_test.py b/python/openshell/sandbox_test.py index c1df0d46e0..8c7ec0de7a 100644 --- a/python/openshell/sandbox_test.py +++ b/python/openshell/sandbox_test.py @@ -2112,6 +2112,14 @@ def CreateSandbox( dict(request.labels), workspace=_request_workspace(request) or "default", ), + application_service_urls={ + exposure.service: f"https://{exposure.service}.example.test/" + for exposure in request.service_exposures + }, + gateway_service_urls={ + exposure.service: f"https://{exposure.service}.gateway.test:8080/" + for exposure in request.service_exposures + }, service_urls={ exposure.service: f"https://{exposure.service}.example.test/" for exposure in request.service_exposures @@ -2280,6 +2288,27 @@ def test_create_forwards_service_exposures() -> None: "": "https://.example.test/", "metrics": "https://metrics.example.test/", } + assert dict(ref.application_service_urls) == dict(ref.service_urls) + assert ref.gateway_service_urls["metrics"] == "https://metrics.gateway.test:8080/" + with pytest.raises(TypeError): + ref.application_service_urls["metrics"] = "changed" # type: ignore[index] + + +def test_application_addresses_preserve_existing_positional_sandbox_ref() -> None: + original = _sandbox_ref(_make_sandbox_proto("id", "demo")) + ref = SandboxRef( + original.id, + original.name, + original.workspace, + original.status, + {}, + None, + {}, + "SHA256:test-host-identity", + ) + assert ref.host_key_fingerprint == "SHA256:test-host-identity" + assert dict(ref.application_service_urls) == {} + assert dict(ref.gateway_service_urls) == {} def test_create_from_template_forwards_workload_template() -> None: diff --git a/sdk/go/openshell/v1/internal/converter/service.go b/sdk/go/openshell/v1/internal/converter/service.go index bb2803c6bb..7bf2b9a670 100644 --- a/sdk/go/openshell/v1/internal/converter/service.go +++ b/sdk/go/openshell/v1/internal/converter/service.go @@ -17,7 +17,9 @@ func ServiceEndpointFromProto(resp *pb.ServiceEndpointResponse) *types.ServiceEn } result := &types.ServiceEndpoint{ - URL: resp.GetUrl(), + URL: resp.GetUrl(), + ApplicationURL: resp.GetApplicationUrl(), + GatewayURL: resp.GetGatewayUrl(), } if ep := resp.GetEndpoint(); ep != nil { @@ -56,7 +58,9 @@ func ServiceEndpointToProto(se *types.ServiceEndpoint) *pb.ServiceEndpointRespon Domain: se.Domain, AuthorizationMode: serviceAuthorizationModeToProto(se.AuthorizationMode), }, - Url: se.URL, + Url: se.URL, + ApplicationUrl: se.ApplicationURL, + GatewayUrl: se.GatewayURL, } } diff --git a/sdk/go/openshell/v1/internal/converter/service_test.go b/sdk/go/openshell/v1/internal/converter/service_test.go index 6db09701e3..227b8ddba0 100644 --- a/sdk/go/openshell/v1/internal/converter/service_test.go +++ b/sdk/go/openshell/v1/internal/converter/service_test.go @@ -26,7 +26,9 @@ func TestServiceEndpointFromProto(t *testing.T) { Domain: true, AuthorizationMode: pb.ServiceAuthorizationMode_SERVICE_AUTHORIZATION_MODE_BEARER_PASSTHROUGH, }, - Url: "https://svc-1.example.com", + Url: "https://svc-1.example.com", + ApplicationUrl: "https://svc-1.example.com", + GatewayUrl: "https://gateway-svc.example.com:8080", } se := ServiceEndpointFromProto(resp) @@ -40,6 +42,9 @@ func TestServiceEndpointFromProto(t *testing.T) { assert.True(t, se.Domain) assert.Equal(t, v1.ServiceAuthorizationModeBearerPassthrough, se.AuthorizationMode) assert.Equal(t, "https://svc-1.example.com", se.URL) + assert.Equal(t, resp.ApplicationUrl, se.ApplicationURL) + assert.Equal(t, resp.GatewayUrl, se.GatewayURL) + assert.Equal(t, resp, ServiceEndpointToProto(se)) } func TestServiceEndpointFromProto_NilEndpoint(t *testing.T) { diff --git a/sdk/go/openshell/v1/sandbox_client.go b/sdk/go/openshell/v1/sandbox_client.go index 5d042b2101..488aa8c6fb 100644 --- a/sdk/go/openshell/v1/sandbox_client.go +++ b/sdk/go/openshell/v1/sandbox_client.go @@ -51,6 +51,8 @@ func (s *sandboxClient) Create(ctx context.Context, workspace, name string, spec sandbox := converter.SandboxFromProto(resp.GetSandbox()) if sandbox != nil { sandbox.ServiceURLs = converter.CopyStringMap(resp.GetServiceUrls()) + sandbox.ApplicationServiceURLs = converter.CopyStringMap(resp.GetApplicationServiceUrls()) + sandbox.GatewayServiceURLs = converter.CopyStringMap(resp.GetGatewayServiceUrls()) } return sandbox, nil } @@ -83,6 +85,8 @@ func (s *sandboxClient) CreateFromTemplate(ctx context.Context, workspace, name, sandbox := converter.SandboxFromProto(resp.GetSandbox()) if sandbox != nil { sandbox.ServiceURLs = converter.CopyStringMap(resp.GetServiceUrls()) + sandbox.ApplicationServiceURLs = converter.CopyStringMap(resp.GetApplicationServiceUrls()) + sandbox.GatewayServiceURLs = converter.CopyStringMap(resp.GetGatewayServiceUrls()) } return sandbox, nil } diff --git a/sdk/go/openshell/v1/types/sandbox.go b/sdk/go/openshell/v1/types/sandbox.go index f1cedb595f..0836ea5e10 100644 --- a/sdk/go/openshell/v1/types/sandbox.go +++ b/sdk/go/openshell/v1/types/sandbox.go @@ -22,7 +22,9 @@ type Sandbox struct { Status SandboxStatus // ServiceURLs is populated by create operations and keyed by service name. // The empty key identifies the unnamed service. - ServiceURLs map[string]string + ServiceURLs map[string]string + ApplicationServiceURLs map[string]string + GatewayServiceURLs map[string]string } // SandboxSpec holds the desired state of a sandbox. diff --git a/sdk/go/openshell/v1/types/service.go b/sdk/go/openshell/v1/types/service.go index 5ae7aa0b9e..b7713477c3 100644 --- a/sdk/go/openshell/v1/types/service.go +++ b/sdk/go/openshell/v1/types/service.go @@ -28,6 +28,8 @@ type ServiceEndpoint struct { Name string TargetPort uint32 Domain bool + ApplicationURL string + GatewayURL string URL string Workspace string AuthorizationMode ServiceAuthorizationMode diff --git a/sdk/go/proto/openshellv1/openshell.pb.go b/sdk/go/proto/openshellv1/openshell.pb.go index 9979786e6d..0ea3a3bf38 100644 --- a/sdk/go/proto/openshellv1/openshell.pb.go +++ b/sdk/go/proto/openshellv1/openshell.pb.go @@ -5149,9 +5149,14 @@ type SandboxResponse struct { Sandbox *Sandbox `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` // Service URLs created by CreateSandbox, keyed by service name. The empty // key identifies the unnamed service. Other sandbox RPCs return an empty map. - ServiceUrls map[string]string `protobuf:"bytes,2,rep,name=service_urls,json=serviceUrls,proto3" json:"service_urls,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + ServiceUrls map[string]string `protobuf:"bytes,2,rep,name=service_urls,json=serviceUrls,proto3" json:"service_urls,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + // Configured application origins for the same services. These are + // authoritative public addresses, not installation or health evidence. + ApplicationServiceUrls map[string]string `protobuf:"bytes,3,rep,name=application_service_urls,json=applicationServiceUrls,proto3" json:"application_service_urls,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + // Gateway-routed alternatives, independent of application ingress. + GatewayServiceUrls map[string]string `protobuf:"bytes,4,rep,name=gateway_service_urls,json=gatewayServiceUrls,proto3" json:"gateway_service_urls,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SandboxResponse) Reset() { @@ -5198,6 +5203,20 @@ func (x *SandboxResponse) GetServiceUrls() map[string]string { return nil } +func (x *SandboxResponse) GetApplicationServiceUrls() map[string]string { + if x != nil { + return x.ApplicationServiceUrls + } + return nil +} + +func (x *SandboxResponse) GetGatewayServiceUrls() map[string]string { + if x != nil { + return x.GatewayServiceUrls + } + return nil +} + // List sandboxes response. type ListSandboxesResponse struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -7129,11 +7148,16 @@ func (x *ServiceEndpoint) GetAuthorizationMode() ServiceAuthorizationMode { return ServiceAuthorizationMode_SERVICE_AUTHORIZATION_MODE_UNSPECIFIED } -// Response containing a service endpoint and, when available, its local URL. +// Service addresses. A configured address does not establish readiness. type ServiceEndpointResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Endpoint *ServiceEndpoint `protobuf:"bytes,1,opt,name=endpoint,proto3" json:"endpoint,omitempty"` - Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Endpoint *ServiceEndpoint `protobuf:"bytes,1,opt,name=endpoint,proto3" json:"endpoint,omitempty"` + // Preferred address: application_url when configured, otherwise gateway_url. + Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` + // Authoritative configured public application address. Preserve its port. + ApplicationUrl string `protobuf:"bytes,3,opt,name=application_url,json=applicationUrl,proto3" json:"application_url,omitempty"` + // Gateway-routed alternative; existing gateway routing remains available. + GatewayUrl string `protobuf:"bytes,4,opt,name=gateway_url,json=gatewayUrl,proto3" json:"gateway_url,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -7182,6 +7206,20 @@ func (x *ServiceEndpointResponse) GetUrl() string { return "" } +func (x *ServiceEndpointResponse) GetApplicationUrl() string { + if x != nil { + return x.ApplicationUrl + } + return "" +} + +func (x *ServiceEndpointResponse) GetGatewayUrl() string { + if x != nil { + return x.GatewayUrl + } + return "" +} + // Revoke SSH session request. type RevokeSshSessionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -20625,12 +20663,20 @@ const file_openshell_proto_rawDesc = "" + "\x14ResumeSandboxRequest\x12R\n" + "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x127\n" + - "\x18restart_if_restore_fails\x18\x03 \x01(\bR\x15restartIfRestoreFails\"\xd5\x01\n" + + "\x18restart_if_restore_fails\x18\x03 \x01(\bR\x15restartIfRestoreFails\"\xc5\x04\n" + "\x0fSandboxResponse\x12/\n" + "\asandbox\x18\x01 \x01(\v2\x15.openshell.v1.SandboxR\asandbox\x12Q\n" + - "\fservice_urls\x18\x02 \x03(\v2..openshell.v1.SandboxResponse.ServiceUrlsEntryR\vserviceUrls\x1a>\n" + + "\fservice_urls\x18\x02 \x03(\v2..openshell.v1.SandboxResponse.ServiceUrlsEntryR\vserviceUrls\x12s\n" + + "\x18application_service_urls\x18\x03 \x03(\v29.openshell.v1.SandboxResponse.ApplicationServiceUrlsEntryR\x16applicationServiceUrls\x12g\n" + + "\x14gateway_service_urls\x18\x04 \x03(\v25.openshell.v1.SandboxResponse.GatewayServiceUrlsEntryR\x12gatewayServiceUrls\x1a>\n" + "\x10ServiceUrlsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1aI\n" + + "\x1bApplicationServiceUrlsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1aE\n" + + "\x17GatewayServiceUrlsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"t\n" + "\x15ListSandboxesResponse\x123\n" + "\tsandboxes\x18\x01 \x03(\v2\x15.openshell.v1.SandboxR\tsandboxes\x12&\n" + @@ -20790,10 +20836,13 @@ const file_openshell_proto_rawDesc = "" + "\vtarget_port\x18\x05 \x01(\rR\n" + "targetPort\x12\x16\n" + "\x06domain\x18\x06 \x01(\bR\x06domain\x12U\n" + - "\x12authorization_mode\x18\a \x01(\x0e2&.openshell.v1.ServiceAuthorizationModeR\x11authorizationMode\"f\n" + + "\x12authorization_mode\x18\a \x01(\x0e2&.openshell.v1.ServiceAuthorizationModeR\x11authorizationMode\"\xb0\x01\n" + "\x17ServiceEndpointResponse\x129\n" + "\bendpoint\x18\x01 \x01(\v2\x1d.openshell.v1.ServiceEndpointR\bendpoint\x12\x10\n" + - "\x03url\x18\x02 \x01(\tR\x03url\"Z\n" + + "\x03url\x18\x02 \x01(\tR\x03url\x12'\n" + + "\x0fapplication_url\x18\x03 \x01(\tR\x0eapplicationUrl\x12\x1f\n" + + "\vgateway_url\x18\x04 \x01(\tR\n" + + "gatewayUrl\"Z\n" + "\x17RevokeSshSessionRequest\x12\x1a\n" + "\x05token\x18\x01 \x01(\tB\x04\x88\xb5\x18\x01R\x05token\x12#\n" + "\rallow_missing\x18\x02 \x01(\bR\fallowMissing\"b\n" + @@ -22192,7 +22241,7 @@ func file_openshell_proto_rawDescGZIP() []byte { } var file_openshell_proto_enumTypes = make([]protoimpl.EnumInfo, 22) -var file_openshell_proto_msgTypes = make([]protoimpl.MessageInfo, 290) +var file_openshell_proto_msgTypes = make([]protoimpl.MessageInfo, 292) var file_openshell_proto_goTypes = []any{ (ExtensionKind)(0), // 0: openshell.v1.ExtensionKind (SandboxPhase)(0), // 1: openshell.v1.SandboxPhase @@ -22489,50 +22538,52 @@ var file_openshell_proto_goTypes = []any{ nil, // 292: openshell.v1.CreateSandboxRequest.AnnotationsEntry nil, // 293: openshell.v1.SandboxMapUpdate.ValuesEntry nil, // 294: openshell.v1.SandboxResponse.ServiceUrlsEntry - nil, // 295: openshell.v1.ExecSandboxRequest.EnvironmentEntry - nil, // 296: openshell.v1.SandboxLogLine.FieldsEntry - nil, // 297: openshell.v1.GetProviderCredentialsResponse.CredentialsEntry - nil, // 298: openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry - nil, // 299: openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry - nil, // 300: openshell.v1.ProviderProfile.AnnotationsEntry - nil, // 301: openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry - nil, // 302: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry - nil, // 303: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry - nil, // 304: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry - nil, // 305: openshell.v1.GetSandboxProviderEnvironmentResponse.FilesEntry - nil, // 306: openshell.v1.ProviderEnvironmentSnapshot.DynamicCredentialsEntry - nil, // 307: openshell.v1.ProviderEnvironmentSnapshot.FilesEntry - nil, // 308: openshell.v1.UpdateConfigRequest.AnnotationsEntry - nil, // 309: openshell.v1.UpdateConfigResponse.AnnotationsEntry - nil, // 310: openshell.v1.SandboxPolicyRevision.ProvenanceEntry - nil, // 311: openshell.v1.CreateWorkspaceRequest.LabelsEntry - (*timestamppb.Timestamp)(nil), // 312: google.protobuf.Timestamp - (*datamodelv1.ObjectMeta)(nil), // 313: openshell.datamodel.v1.ObjectMeta - (*sandboxv1.SandboxPolicy)(nil), // 314: openshell.sandbox.v1.SandboxPolicy - (*structpb.Struct)(nil), // 315: google.protobuf.Struct - (*durationpb.Duration)(nil), // 316: google.protobuf.Duration - (*datamodelv1.WorkspaceSelector)(nil), // 317: openshell.datamodel.v1.WorkspaceSelector - (*datamodelv1.Provider)(nil), // 318: openshell.datamodel.v1.Provider - (sandboxv1.PolicySource)(0), // 319: openshell.sandbox.v1.PolicySource - (*sandboxv1.NetworkEndpoint)(nil), // 320: openshell.sandbox.v1.NetworkEndpoint - (*sandboxv1.NetworkBinary)(nil), // 321: openshell.sandbox.v1.NetworkBinary - (*sandboxv1.SettingValue)(nil), // 322: openshell.sandbox.v1.SettingValue - (*sandboxv1.NetworkPolicyRule)(nil), // 323: openshell.sandbox.v1.NetworkPolicyRule - (*sandboxv1.L7DenyRule)(nil), // 324: openshell.sandbox.v1.L7DenyRule - (*sandboxv1.L7Rule)(nil), // 325: openshell.sandbox.v1.L7Rule - (*sandboxv1.SandboxConfigSnapshot)(nil), // 326: openshell.sandbox.v1.SandboxConfigSnapshot - (*datamodelv1.Workspace)(nil), // 327: openshell.datamodel.v1.Workspace - (*sandboxv1.SandboxServiceRoute)(nil), // 328: openshell.sandbox.v1.SandboxServiceRoute - (*sandboxv1.GetSandboxConfigRequest)(nil), // 329: openshell.sandbox.v1.GetSandboxConfigRequest - (*sandboxv1.GetGatewayConfigRequest)(nil), // 330: openshell.sandbox.v1.GetGatewayConfigRequest - (*sandboxv1.GetSandboxConfigResponse)(nil), // 331: openshell.sandbox.v1.GetSandboxConfigResponse - (*sandboxv1.GetGatewayConfigResponse)(nil), // 332: openshell.sandbox.v1.GetGatewayConfigResponse + nil, // 295: openshell.v1.SandboxResponse.ApplicationServiceUrlsEntry + nil, // 296: openshell.v1.SandboxResponse.GatewayServiceUrlsEntry + nil, // 297: openshell.v1.ExecSandboxRequest.EnvironmentEntry + nil, // 298: openshell.v1.SandboxLogLine.FieldsEntry + nil, // 299: openshell.v1.GetProviderCredentialsResponse.CredentialsEntry + nil, // 300: openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry + nil, // 301: openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry + nil, // 302: openshell.v1.ProviderProfile.AnnotationsEntry + nil, // 303: openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry + nil, // 304: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry + nil, // 305: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry + nil, // 306: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry + nil, // 307: openshell.v1.GetSandboxProviderEnvironmentResponse.FilesEntry + nil, // 308: openshell.v1.ProviderEnvironmentSnapshot.DynamicCredentialsEntry + nil, // 309: openshell.v1.ProviderEnvironmentSnapshot.FilesEntry + nil, // 310: openshell.v1.UpdateConfigRequest.AnnotationsEntry + nil, // 311: openshell.v1.UpdateConfigResponse.AnnotationsEntry + nil, // 312: openshell.v1.SandboxPolicyRevision.ProvenanceEntry + nil, // 313: openshell.v1.CreateWorkspaceRequest.LabelsEntry + (*timestamppb.Timestamp)(nil), // 314: google.protobuf.Timestamp + (*datamodelv1.ObjectMeta)(nil), // 315: openshell.datamodel.v1.ObjectMeta + (*sandboxv1.SandboxPolicy)(nil), // 316: openshell.sandbox.v1.SandboxPolicy + (*structpb.Struct)(nil), // 317: google.protobuf.Struct + (*durationpb.Duration)(nil), // 318: google.protobuf.Duration + (*datamodelv1.WorkspaceSelector)(nil), // 319: openshell.datamodel.v1.WorkspaceSelector + (*datamodelv1.Provider)(nil), // 320: openshell.datamodel.v1.Provider + (sandboxv1.PolicySource)(0), // 321: openshell.sandbox.v1.PolicySource + (*sandboxv1.NetworkEndpoint)(nil), // 322: openshell.sandbox.v1.NetworkEndpoint + (*sandboxv1.NetworkBinary)(nil), // 323: openshell.sandbox.v1.NetworkBinary + (*sandboxv1.SettingValue)(nil), // 324: openshell.sandbox.v1.SettingValue + (*sandboxv1.NetworkPolicyRule)(nil), // 325: openshell.sandbox.v1.NetworkPolicyRule + (*sandboxv1.L7DenyRule)(nil), // 326: openshell.sandbox.v1.L7DenyRule + (*sandboxv1.L7Rule)(nil), // 327: openshell.sandbox.v1.L7Rule + (*sandboxv1.SandboxConfigSnapshot)(nil), // 328: openshell.sandbox.v1.SandboxConfigSnapshot + (*datamodelv1.Workspace)(nil), // 329: openshell.datamodel.v1.Workspace + (*sandboxv1.SandboxServiceRoute)(nil), // 330: openshell.sandbox.v1.SandboxServiceRoute + (*sandboxv1.GetSandboxConfigRequest)(nil), // 331: openshell.sandbox.v1.GetSandboxConfigRequest + (*sandboxv1.GetGatewayConfigRequest)(nil), // 332: openshell.sandbox.v1.GetGatewayConfigRequest + (*sandboxv1.GetSandboxConfigResponse)(nil), // 333: openshell.sandbox.v1.GetSandboxConfigResponse + (*sandboxv1.GetGatewayConfigResponse)(nil), // 334: openshell.sandbox.v1.GetGatewayConfigResponse } var file_openshell_proto_depIdxs = []int32{ - 312, // 0: openshell.v1.IssueSandboxTokenResponse.expiration_time:type_name -> google.protobuf.Timestamp - 312, // 1: openshell.v1.RefreshSandboxTokenResponse.expiration_time:type_name -> google.protobuf.Timestamp + 314, // 0: openshell.v1.IssueSandboxTokenResponse.expiration_time:type_name -> google.protobuf.Timestamp + 314, // 1: openshell.v1.RefreshSandboxTokenResponse.expiration_time:type_name -> google.protobuf.Timestamp 266, // 2: openshell.v1.RefreshSandboxTokenResponse.extension_credentials:type_name -> openshell.v1.ExtensionServiceCredential - 312, // 3: openshell.v1.RefreshSandboxTokenResponse.sandbox_expiration_time:type_name -> google.protobuf.Timestamp + 314, // 3: openshell.v1.RefreshSandboxTokenResponse.sandbox_expiration_time:type_name -> google.protobuf.Timestamp 14, // 4: openshell.v1.HealthResponse.status:type_name -> openshell.v1.ServiceStatus 14, // 5: openshell.v1.GetGatewayInfoResponse.status:type_name -> openshell.v1.ServiceStatus 33, // 6: openshell.v1.GetGatewayInfoResponse.compute_drivers:type_name -> openshell.v1.ComputeDriverInfo @@ -22543,554 +22594,556 @@ var file_openshell_proto_depIdxs = []int32{ 36, // 11: openshell.v1.ResourceCapabilities.cpu:type_name -> openshell.v1.CpuResourceCapabilities 37, // 12: openshell.v1.ResourceCapabilities.memory:type_name -> openshell.v1.MemoryResourceCapabilities 38, // 13: openshell.v1.ResourceCapabilities.gpu:type_name -> openshell.v1.GpuResourceCapabilities - 313, // 14: openshell.v1.Sandbox.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 315, // 14: openshell.v1.Sandbox.metadata:type_name -> openshell.datamodel.v1.ObjectMeta 40, // 15: openshell.v1.Sandbox.spec:type_name -> openshell.v1.SandboxSpec 51, // 16: openshell.v1.Sandbox.status:type_name -> openshell.v1.SandboxStatus 50, // 17: openshell.v1.Sandbox.created_from_workload_template:type_name -> openshell.v1.SandboxWorkloadTemplateProvenance 285, // 18: openshell.v1.SandboxSpec.environment:type_name -> openshell.v1.SandboxSpec.EnvironmentEntry 43, // 19: openshell.v1.SandboxSpec.template:type_name -> openshell.v1.SandboxTemplate - 314, // 20: openshell.v1.SandboxSpec.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 316, // 20: openshell.v1.SandboxSpec.policy:type_name -> openshell.sandbox.v1.SandboxPolicy 41, // 21: openshell.v1.SandboxSpec.resource_requirements:type_name -> openshell.v1.ResourceRequirements 17, // 22: openshell.v1.SandboxSpec.restart_policy:type_name -> openshell.v1.SandboxRestartPolicy 42, // 23: openshell.v1.ResourceRequirements.gpu:type_name -> openshell.v1.GpuResourceRequirements 286, // 24: openshell.v1.SandboxTemplate.labels:type_name -> openshell.v1.SandboxTemplate.LabelsEntry 287, // 25: openshell.v1.SandboxTemplate.annotations:type_name -> openshell.v1.SandboxTemplate.AnnotationsEntry 288, // 26: openshell.v1.SandboxTemplate.environment:type_name -> openshell.v1.SandboxTemplate.EnvironmentEntry - 315, // 27: openshell.v1.SandboxTemplate.resources:type_name -> google.protobuf.Struct - 315, // 28: openshell.v1.SandboxTemplate.driver_config:type_name -> google.protobuf.Struct - 313, // 29: openshell.v1.SandboxWorkloadTemplate.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 317, // 27: openshell.v1.SandboxTemplate.resources:type_name -> google.protobuf.Struct + 317, // 28: openshell.v1.SandboxTemplate.driver_config:type_name -> google.protobuf.Struct + 315, // 29: openshell.v1.SandboxWorkloadTemplate.metadata:type_name -> openshell.datamodel.v1.ObjectMeta 45, // 30: openshell.v1.SandboxWorkloadTemplate.spec:type_name -> openshell.v1.SandboxWorkloadTemplateSpec 46, // 31: openshell.v1.SandboxWorkloadTemplateSpec.workload:type_name -> openshell.v1.SandboxWorkloadConfig - 315, // 32: openshell.v1.SandboxWorkloadTemplateSpec.driver_config:type_name -> google.protobuf.Struct + 317, // 32: openshell.v1.SandboxWorkloadTemplateSpec.driver_config:type_name -> google.protobuf.Struct 48, // 33: openshell.v1.SandboxWorkloadTemplateSpec.desired_service_level:type_name -> openshell.v1.SandboxServiceLevel 289, // 34: openshell.v1.SandboxWorkloadConfig.environment:type_name -> openshell.v1.SandboxWorkloadConfig.EnvironmentEntry 47, // 35: openshell.v1.SandboxWorkloadConfig.resources:type_name -> openshell.v1.SandboxResources 42, // 36: openshell.v1.SandboxResources.gpu:type_name -> openshell.v1.GpuResourceRequirements 49, // 37: openshell.v1.SandboxServiceLevel.startup:type_name -> openshell.v1.SandboxStartup - 316, // 38: openshell.v1.SandboxStartup.ready_within:type_name -> google.protobuf.Duration + 318, // 38: openshell.v1.SandboxStartup.ready_within:type_name -> google.protobuf.Duration 52, // 39: openshell.v1.SandboxStatus.conditions:type_name -> openshell.v1.SandboxCondition 1, // 40: openshell.v1.SandboxStatus.phase:type_name -> openshell.v1.SandboxPhase 272, // 41: openshell.v1.SandboxStatus.endpoint_statuses:type_name -> openshell.v1.EndpointStatus 188, // 42: openshell.v1.SandboxStatus.configuration_admission:type_name -> openshell.v1.SandboxConfigurationAdmission 273, // 43: openshell.v1.SandboxStatus.provisioning:type_name -> openshell.v1.SandboxProvisioning - 312, // 44: openshell.v1.SandboxStatus.next_restart_time:type_name -> google.protobuf.Timestamp - 312, // 45: openshell.v1.SandboxStatus.main_process_started_time:type_name -> google.protobuf.Timestamp + 314, // 44: openshell.v1.SandboxStatus.next_restart_time:type_name -> google.protobuf.Timestamp + 314, // 45: openshell.v1.SandboxStatus.main_process_started_time:type_name -> google.protobuf.Timestamp 270, // 46: openshell.v1.SandboxStatus.last_token_grant_failure:type_name -> openshell.v1.TokenGrantFailureObservation - 312, // 47: openshell.v1.SandboxCondition.transition_time:type_name -> google.protobuf.Timestamp - 312, // 48: openshell.v1.PlatformEvent.event_time:type_name -> google.protobuf.Timestamp + 314, // 47: openshell.v1.SandboxCondition.transition_time:type_name -> google.protobuf.Timestamp + 314, // 48: openshell.v1.PlatformEvent.event_time:type_name -> google.protobuf.Timestamp 290, // 49: openshell.v1.PlatformEvent.metadata:type_name -> openshell.v1.PlatformEvent.MetadataEntry - 317, // 50: openshell.v1.CreateSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 50: openshell.v1.CreateSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 40, // 51: openshell.v1.CreateSandboxRequest.spec:type_name -> openshell.v1.SandboxSpec 291, // 52: openshell.v1.CreateSandboxRequest.labels:type_name -> openshell.v1.CreateSandboxRequest.LabelsEntry 292, // 53: openshell.v1.CreateSandboxRequest.annotations:type_name -> openshell.v1.CreateSandboxRequest.AnnotationsEntry 274, // 54: openshell.v1.CreateSandboxRequest.service_exposures:type_name -> openshell.v1.SandboxServiceExposure - 317, // 55: openshell.v1.UpdateSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 55: openshell.v1.UpdateSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 56, // 56: openshell.v1.UpdateSandboxRequest.command:type_name -> openshell.v1.SandboxCommandUpdate 57, // 57: openshell.v1.UpdateSandboxRequest.environment:type_name -> openshell.v1.SandboxMapUpdate 57, // 58: openshell.v1.UpdateSandboxRequest.labels:type_name -> openshell.v1.SandboxMapUpdate 57, // 59: openshell.v1.UpdateSandboxRequest.annotations:type_name -> openshell.v1.SandboxMapUpdate 293, // 60: openshell.v1.SandboxMapUpdate.values:type_name -> openshell.v1.SandboxMapUpdate.ValuesEntry - 317, // 61: openshell.v1.CreateSandboxTemplateRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 61: openshell.v1.CreateSandboxTemplateRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 44, // 62: openshell.v1.CreateSandboxTemplateRequest.template:type_name -> openshell.v1.SandboxWorkloadTemplate - 317, // 63: openshell.v1.GetSandboxTemplateRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 64: openshell.v1.ListSandboxTemplatesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 65: openshell.v1.DeleteSandboxTemplateRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 63: openshell.v1.GetSandboxTemplateRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 64: openshell.v1.ListSandboxTemplatesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 65: openshell.v1.DeleteSandboxTemplateRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 44, // 66: openshell.v1.SandboxTemplateResponse.template:type_name -> openshell.v1.SandboxWorkloadTemplate 44, // 67: openshell.v1.ListSandboxTemplatesResponse.templates:type_name -> openshell.v1.SandboxWorkloadTemplate 18, // 68: openshell.v1.DeleteSandboxTemplateResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 317, // 69: openshell.v1.BeginRootfsTarStagingRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 312, // 70: openshell.v1.BeginRootfsTarStagingResponse.expiration_time:type_name -> google.protobuf.Timestamp - 317, // 71: openshell.v1.GetSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 72: openshell.v1.ListSandboxesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 73: openshell.v1.ListSandboxProvidersRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 74: openshell.v1.AttachSandboxProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 75: openshell.v1.DetachSandboxProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 76: openshell.v1.DeleteSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 77: openshell.v1.StopSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 78: openshell.v1.StartSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 79: openshell.v1.SuspendSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 80: openshell.v1.ResumeSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 69: openshell.v1.BeginRootfsTarStagingRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 314, // 70: openshell.v1.BeginRootfsTarStagingResponse.expiration_time:type_name -> google.protobuf.Timestamp + 319, // 71: openshell.v1.GetSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 72: openshell.v1.ListSandboxesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 73: openshell.v1.ListSandboxProvidersRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 74: openshell.v1.AttachSandboxProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 75: openshell.v1.DetachSandboxProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 76: openshell.v1.DeleteSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 77: openshell.v1.StopSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 78: openshell.v1.StartSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 79: openshell.v1.SuspendSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 80: openshell.v1.ResumeSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector 39, // 81: openshell.v1.SandboxResponse.sandbox:type_name -> openshell.v1.Sandbox 294, // 82: openshell.v1.SandboxResponse.service_urls:type_name -> openshell.v1.SandboxResponse.ServiceUrlsEntry - 39, // 83: openshell.v1.ListSandboxesResponse.sandboxes:type_name -> openshell.v1.Sandbox - 318, // 84: openshell.v1.ListSandboxProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider - 39, // 85: openshell.v1.AttachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox - 86, // 86: openshell.v1.AttachSandboxProviderResponse.receipt:type_name -> openshell.v1.ProviderMutationReceipt - 39, // 87: openshell.v1.DetachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox - 86, // 88: openshell.v1.DetachSandboxProviderResponse.receipt:type_name -> openshell.v1.ProviderMutationReceipt - 84, // 89: openshell.v1.ConfigSnapshotRevision.sandbox_config:type_name -> openshell.v1.SandboxConfigRevision - 82, // 90: openshell.v1.ConfigSnapshotRevision.provider_target:type_name -> openshell.v1.ProviderDesiredIdentity - 319, // 91: openshell.v1.SandboxConfigRevision.policy_source:type_name -> openshell.sandbox.v1.PolicySource - 5, // 92: openshell.v1.ConfigUpdateOperation.component:type_name -> openshell.v1.ConfigComponent - 83, // 93: openshell.v1.ConfigUpdateOperation.target_revision:type_name -> openshell.v1.ConfigSnapshotRevision - 7, // 94: openshell.v1.ConfigUpdateOperation.state:type_name -> openshell.v1.ConfigUpdateOperationState - 6, // 95: openshell.v1.ConfigUpdateOperation.outcome:type_name -> openshell.v1.ConfigApplyOutcome - 312, // 96: openshell.v1.ConfigUpdateOperation.created_time:type_name -> google.protobuf.Timestamp - 312, // 97: openshell.v1.ConfigUpdateOperation.updated_time:type_name -> google.protobuf.Timestamp - 312, // 98: openshell.v1.ConfigUpdateOperation.completed_time:type_name -> google.protobuf.Timestamp - 2, // 99: openshell.v1.ProviderMutationReceipt.kind:type_name -> openshell.v1.ProviderMutationKind - 82, // 100: openshell.v1.ProviderMutationReceipt.desired:type_name -> openshell.v1.ProviderDesiredIdentity - 312, // 101: openshell.v1.ProviderMutationReceipt.persisted_time:type_name -> google.protobuf.Timestamp - 4, // 102: openshell.v1.ProviderReadinessObservation.reason:type_name -> openshell.v1.ProviderReadinessReason - 86, // 103: openshell.v1.ProviderReadinessStatus.receipt:type_name -> openshell.v1.ProviderMutationReceipt - 3, // 104: openshell.v1.ProviderReadinessStatus.state:type_name -> openshell.v1.ProviderReadinessState - 4, // 105: openshell.v1.ProviderReadinessStatus.reason:type_name -> openshell.v1.ProviderReadinessReason - 87, // 106: openshell.v1.ProviderReadinessStatus.observed:type_name -> openshell.v1.ProviderReadinessObservation - 312, // 107: openshell.v1.ProviderReadinessStatus.observed_time:type_name -> google.protobuf.Timestamp - 312, // 108: openshell.v1.ProviderReadinessStatus.evaluated_time:type_name -> google.protobuf.Timestamp - 85, // 109: openshell.v1.ProviderReadinessStatus.operation:type_name -> openshell.v1.ConfigUpdateOperation - 317, // 110: openshell.v1.GetSandboxProviderStatusRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 88, // 111: openshell.v1.GetSandboxProviderStatusResponse.status:type_name -> openshell.v1.ProviderReadinessStatus - 87, // 112: openshell.v1.ReportProviderReadinessRequest.observation:type_name -> openshell.v1.ProviderReadinessObservation - 316, // 113: openshell.v1.ReportProviderReadinessResponse.report_interval:type_name -> google.protobuf.Duration - 316, // 114: openshell.v1.ReportProviderReadinessResponse.observation_ttl:type_name -> google.protobuf.Duration - 18, // 115: openshell.v1.DeleteSandboxResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 317, // 116: openshell.v1.CreateSshSessionRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 312, // 117: openshell.v1.CreateSshSessionResponse.expiration_time:type_name -> google.protobuf.Timestamp - 317, // 118: openshell.v1.ExposeServiceRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 20, // 119: openshell.v1.ExposeServiceRequest.authorization_mode:type_name -> openshell.v1.ServiceAuthorizationMode - 317, // 120: openshell.v1.GetServiceRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 121: openshell.v1.ListServicesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 103, // 122: openshell.v1.ListServicesResponse.services:type_name -> openshell.v1.ServiceEndpointResponse - 317, // 123: openshell.v1.DeleteServiceRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 18, // 124: openshell.v1.DeleteServiceResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 313, // 125: openshell.v1.ServiceEndpoint.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 20, // 126: openshell.v1.ServiceEndpoint.authorization_mode:type_name -> openshell.v1.ServiceAuthorizationMode - 102, // 127: openshell.v1.ServiceEndpointResponse.endpoint:type_name -> openshell.v1.ServiceEndpoint - 18, // 128: openshell.v1.RevokeSshSessionResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 317, // 129: openshell.v1.ExecSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 295, // 130: openshell.v1.ExecSandboxRequest.environment:type_name -> openshell.v1.ExecSandboxRequest.EnvironmentEntry - 316, // 131: openshell.v1.ExecSandboxRequest.execution_timeout:type_name -> google.protobuf.Duration - 107, // 132: openshell.v1.ExecSandboxEvent.stdout:type_name -> openshell.v1.ExecSandboxStdout - 108, // 133: openshell.v1.ExecSandboxEvent.stderr:type_name -> openshell.v1.ExecSandboxStderr - 109, // 134: openshell.v1.ExecSandboxEvent.exit:type_name -> openshell.v1.ExecSandboxExit - 213, // 135: openshell.v1.TcpForwardInit.ssh:type_name -> openshell.v1.SshRelayTarget - 214, // 136: openshell.v1.TcpForwardInit.tcp:type_name -> openshell.v1.TcpRelayTarget - 111, // 137: openshell.v1.TcpForwardFrame.init:type_name -> openshell.v1.TcpForwardInit - 106, // 138: openshell.v1.ExecSandboxInput.start:type_name -> openshell.v1.ExecSandboxRequest - 114, // 139: openshell.v1.ExecSandboxInput.resize:type_name -> openshell.v1.ExecSandboxWindowResize - 313, // 140: openshell.v1.SshSession.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 312, // 141: openshell.v1.SshSession.expiration_time:type_name -> google.protobuf.Timestamp - 317, // 142: openshell.v1.WatchSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 312, // 143: openshell.v1.WatchSandboxRequest.since_time:type_name -> google.protobuf.Timestamp - 39, // 144: openshell.v1.SandboxStreamEvent.sandbox:type_name -> openshell.v1.Sandbox - 118, // 145: openshell.v1.SandboxStreamEvent.log:type_name -> openshell.v1.SandboxLogLine - 53, // 146: openshell.v1.SandboxStreamEvent.event:type_name -> openshell.v1.PlatformEvent - 119, // 147: openshell.v1.SandboxStreamEvent.warning:type_name -> openshell.v1.SandboxStreamWarning - 230, // 148: openshell.v1.SandboxStreamEvent.draft_policy_update:type_name -> openshell.v1.DraftPolicyUpdate - 312, // 149: openshell.v1.SandboxLogLine.event_time:type_name -> google.protobuf.Timestamp - 296, // 150: openshell.v1.SandboxLogLine.fields:type_name -> openshell.v1.SandboxLogLine.FieldsEntry - 317, // 151: openshell.v1.CreateProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 318, // 152: openshell.v1.CreateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider - 317, // 153: openshell.v1.GetProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 154: openshell.v1.GetProviderCredentialsRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 316, // 155: openshell.v1.GetProviderCredentialsRequest.minimum_remaining_lifetime:type_name -> google.protobuf.Duration - 312, // 156: openshell.v1.ProviderCredentialValue.expiration_time:type_name -> google.protobuf.Timestamp - 297, // 157: openshell.v1.GetProviderCredentialsResponse.credentials:type_name -> openshell.v1.GetProviderCredentialsResponse.CredentialsEntry - 317, // 158: openshell.v1.ListProvidersRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 159: openshell.v1.UpdateProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 318, // 160: openshell.v1.UpdateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider - 298, // 161: openshell.v1.UpdateProviderRequest.credential_expiration_times:type_name -> openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry - 317, // 162: openshell.v1.DeleteProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 318, // 163: openshell.v1.ProviderResponse.provider:type_name -> openshell.datamodel.v1.Provider - 86, // 164: openshell.v1.ProviderResponse.target_receipts:type_name -> openshell.v1.ProviderMutationReceipt - 318, // 165: openshell.v1.ListProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider - 317, // 166: openshell.v1.ListProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 167: openshell.v1.GetProviderProfileRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 151, // 168: openshell.v1.ProviderProfileImportItem.profile:type_name -> openshell.v1.ProviderProfile - 316, // 169: openshell.v1.ProviderCredentialTokenGrant.cache_ttl:type_name -> google.protobuf.Duration - 134, // 170: openshell.v1.ProviderCredentialTokenGrant.audience_overrides:type_name -> openshell.v1.ProviderCredentialTokenGrantAudienceOverride - 8, // 171: openshell.v1.ProviderCredentialTokenGrant.grant_type:type_name -> openshell.v1.ProviderCredentialTokenGrantType - 135, // 172: openshell.v1.ProviderCredentialTokenGrant.subject_token:type_name -> openshell.v1.ProviderCredentialTokenGrantSubjectToken - 140, // 173: openshell.v1.ProviderProfileCredential.refresh:type_name -> openshell.v1.ProviderCredentialRefresh - 136, // 174: openshell.v1.ProviderProfileCredential.token_grant:type_name -> openshell.v1.ProviderCredentialTokenGrant - 9, // 175: openshell.v1.ProviderCredentialRefresh.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 316, // 176: openshell.v1.ProviderCredentialRefresh.refresh_before:type_name -> google.protobuf.Duration - 316, // 177: openshell.v1.ProviderCredentialRefresh.max_lifetime:type_name -> google.protobuf.Duration - 138, // 178: openshell.v1.ProviderCredentialRefresh.material:type_name -> openshell.v1.ProviderCredentialRefreshMaterial - 139, // 179: openshell.v1.ProviderCredentialRefresh.additional_outputs:type_name -> openshell.v1.ProviderCredentialRefreshOutput - 9, // 180: openshell.v1.ProviderCredentialRefreshStatus.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 312, // 181: openshell.v1.ProviderCredentialRefreshStatus.expiration_time:type_name -> google.protobuf.Timestamp - 312, // 182: openshell.v1.ProviderCredentialRefreshStatus.next_refresh_time:type_name -> google.protobuf.Timestamp - 312, // 183: openshell.v1.ProviderCredentialRefreshStatus.last_refresh_time:type_name -> google.protobuf.Timestamp - 16, // 184: openshell.v1.ProviderCredentialRefreshStatus.recovery_action:type_name -> openshell.v1.ProviderCredentialRefreshRecoveryAction - 312, // 185: openshell.v1.ProviderCredentialRefreshStatus.last_error_time:type_name -> google.protobuf.Timestamp - 317, // 186: openshell.v1.GetProviderRefreshStatusRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 141, // 187: openshell.v1.GetProviderRefreshStatusResponse.credentials:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 317, // 188: openshell.v1.ConfigureProviderRefreshRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 9, // 189: openshell.v1.ConfigureProviderRefreshRequest.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 299, // 190: openshell.v1.ConfigureProviderRefreshRequest.material:type_name -> openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry - 312, // 191: openshell.v1.ConfigureProviderRefreshRequest.expiration_time:type_name -> google.protobuf.Timestamp - 141, // 192: openshell.v1.ConfigureProviderRefreshResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 317, // 193: openshell.v1.RotateProviderCredentialRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 141, // 194: openshell.v1.RotateProviderCredentialResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 317, // 195: openshell.v1.DeleteProviderRefreshRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 18, // 196: openshell.v1.DeleteProviderRefreshResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 10, // 197: openshell.v1.ProviderProfile.category:type_name -> openshell.v1.ProviderProfileCategory - 137, // 198: openshell.v1.ProviderProfile.credentials:type_name -> openshell.v1.ProviderProfileCredential - 320, // 199: openshell.v1.ProviderProfile.endpoints:type_name -> openshell.sandbox.v1.NetworkEndpoint - 321, // 200: openshell.v1.ProviderProfile.binaries:type_name -> openshell.sandbox.v1.NetworkBinary - 142, // 201: openshell.v1.ProviderProfile.discovery:type_name -> openshell.v1.ProviderProfileDiscovery - 300, // 202: openshell.v1.ProviderProfile.annotations:type_name -> openshell.v1.ProviderProfile.AnnotationsEntry - 152, // 203: openshell.v1.ProviderProfile.files:type_name -> openshell.v1.ProviderProfileFile - 151, // 204: openshell.v1.ProviderProfileResponse.profile:type_name -> openshell.v1.ProviderProfile - 151, // 205: openshell.v1.ListProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile - 317, // 206: openshell.v1.ImportProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 132, // 207: openshell.v1.ImportProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem - 133, // 208: openshell.v1.ImportProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 151, // 209: openshell.v1.ImportProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile - 317, // 210: openshell.v1.UpdateProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 132, // 211: openshell.v1.UpdateProviderProfilesRequest.profile:type_name -> openshell.v1.ProviderProfileImportItem - 133, // 212: openshell.v1.UpdateProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 151, // 213: openshell.v1.UpdateProviderProfilesResponse.profile:type_name -> openshell.v1.ProviderProfile - 317, // 214: openshell.v1.LintProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 132, // 215: openshell.v1.LintProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem - 133, // 216: openshell.v1.LintProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 18, // 217: openshell.v1.DeleteProviderResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 317, // 218: openshell.v1.DeleteProviderProfileRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 18, // 219: openshell.v1.DeleteProviderProfileResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 165, // 220: openshell.v1.StaticCredentialBinding.endpoints:type_name -> openshell.v1.StaticCredentialEndpointBinding - 301, // 221: openshell.v1.GetSandboxProviderEnvironmentResponse.environment:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry - 302, // 222: openshell.v1.GetSandboxProviderEnvironmentResponse.credential_expiration_times:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry - 303, // 223: openshell.v1.GetSandboxProviderEnvironmentResponse.dynamic_credentials:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry - 304, // 224: openshell.v1.GetSandboxProviderEnvironmentResponse.static_credential_bindings:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry - 4, // 225: openshell.v1.GetSandboxProviderEnvironmentResponse.readiness_reason:type_name -> openshell.v1.ProviderReadinessReason - 305, // 226: openshell.v1.GetSandboxProviderEnvironmentResponse.files:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.FilesEntry - 11, // 227: openshell.v1.ProviderEnvironmentValue.classification:type_name -> openshell.v1.ProviderEnvironmentValueClassification - 166, // 228: openshell.v1.ProviderEnvironmentValue.static_credential_binding:type_name -> openshell.v1.StaticCredentialBinding - 312, // 229: openshell.v1.ProviderEnvironmentValue.expiration_time:type_name -> google.protobuf.Timestamp - 168, // 230: openshell.v1.ProviderEnvironmentSnapshot.values:type_name -> openshell.v1.ProviderEnvironmentValue - 306, // 231: openshell.v1.ProviderEnvironmentSnapshot.dynamic_credentials:type_name -> openshell.v1.ProviderEnvironmentSnapshot.DynamicCredentialsEntry - 4, // 232: openshell.v1.ProviderEnvironmentSnapshot.readiness_reason:type_name -> openshell.v1.ProviderReadinessReason - 307, // 233: openshell.v1.ProviderEnvironmentSnapshot.files:type_name -> openshell.v1.ProviderEnvironmentSnapshot.FilesEntry - 316, // 234: openshell.v1.ExchangeProviderSubjectTokenResponse.expires_after:type_name -> google.protobuf.Duration - 317, // 235: openshell.v1.UpdateConfigRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 314, // 236: openshell.v1.UpdateConfigRequest.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 322, // 237: openshell.v1.UpdateConfigRequest.setting_value:type_name -> openshell.sandbox.v1.SettingValue - 173, // 238: openshell.v1.UpdateConfigRequest.merge_operations:type_name -> openshell.v1.PolicyMergeOperation - 308, // 239: openshell.v1.UpdateConfigRequest.annotations:type_name -> openshell.v1.UpdateConfigRequest.AnnotationsEntry - 174, // 240: openshell.v1.PolicyMergeOperation.add_rule:type_name -> openshell.v1.AddNetworkRule - 175, // 241: openshell.v1.PolicyMergeOperation.remove_endpoint:type_name -> openshell.v1.RemoveNetworkEndpoint - 176, // 242: openshell.v1.PolicyMergeOperation.remove_rule:type_name -> openshell.v1.RemoveNetworkRule - 178, // 243: openshell.v1.PolicyMergeOperation.add_deny_rules:type_name -> openshell.v1.AddDenyRules - 179, // 244: openshell.v1.PolicyMergeOperation.add_allow_rules:type_name -> openshell.v1.AddAllowRules - 180, // 245: openshell.v1.PolicyMergeOperation.remove_binary:type_name -> openshell.v1.RemoveNetworkBinary - 323, // 246: openshell.v1.AddNetworkRule.rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 321, // 247: openshell.v1.L7RuleTarget.binaries:type_name -> openshell.sandbox.v1.NetworkBinary - 324, // 248: openshell.v1.AddDenyRules.deny_rules:type_name -> openshell.sandbox.v1.L7DenyRule - 177, // 249: openshell.v1.AddDenyRules.target:type_name -> openshell.v1.L7RuleTarget - 325, // 250: openshell.v1.AddAllowRules.rules:type_name -> openshell.sandbox.v1.L7Rule - 177, // 251: openshell.v1.AddAllowRules.target:type_name -> openshell.v1.L7RuleTarget - 309, // 252: openshell.v1.UpdateConfigResponse.annotations:type_name -> openshell.v1.UpdateConfigResponse.AnnotationsEntry - 317, // 253: openshell.v1.GetSandboxPolicyStatusRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 191, // 254: openshell.v1.GetSandboxPolicyStatusResponse.revision:type_name -> openshell.v1.SandboxPolicyRevision - 317, // 255: openshell.v1.ListSandboxPoliciesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 191, // 256: openshell.v1.ListSandboxPoliciesResponse.revisions:type_name -> openshell.v1.SandboxPolicyRevision - 13, // 257: openshell.v1.ReportPolicyStatusRequest.status:type_name -> openshell.v1.PolicyStatus - 12, // 258: openshell.v1.SandboxConfigurationAdmission.state:type_name -> openshell.v1.ConfigurationAdmissionState - 188, // 259: openshell.v1.ReportSandboxConfigurationRequest.admission:type_name -> openshell.v1.SandboxConfigurationAdmission - 13, // 260: openshell.v1.SandboxPolicyRevision.status:type_name -> openshell.v1.PolicyStatus - 312, // 261: openshell.v1.SandboxPolicyRevision.created_time:type_name -> google.protobuf.Timestamp - 312, // 262: openshell.v1.SandboxPolicyRevision.loaded_time:type_name -> google.protobuf.Timestamp - 314, // 263: openshell.v1.SandboxPolicyRevision.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 310, // 264: openshell.v1.SandboxPolicyRevision.provenance:type_name -> openshell.v1.SandboxPolicyRevision.ProvenanceEntry - 317, // 265: openshell.v1.GetSandboxLogsRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 312, // 266: openshell.v1.GetSandboxLogsRequest.since_time:type_name -> google.protobuf.Timestamp - 118, // 267: openshell.v1.PushSandboxLogsRequest.logs:type_name -> openshell.v1.SandboxLogLine - 118, // 268: openshell.v1.GetSandboxLogsResponse.logs:type_name -> openshell.v1.SandboxLogLine - 198, // 269: openshell.v1.SupervisorMessage.hello:type_name -> openshell.v1.SupervisorHello - 206, // 270: openshell.v1.SupervisorMessage.heartbeat:type_name -> openshell.v1.SupervisorHeartbeat - 223, // 271: openshell.v1.SupervisorMessage.relay_open_result:type_name -> openshell.v1.RelayOpenResult - 224, // 272: openshell.v1.SupervisorMessage.relay_close:type_name -> openshell.v1.RelayClose - 202, // 273: openshell.v1.SupervisorMessage.config_update_result:type_name -> openshell.v1.ConfigUpdateResult - 277, // 274: openshell.v1.SupervisorMessage.service_advertisement:type_name -> openshell.v1.SupervisorServiceAdvertisement - 199, // 275: openshell.v1.GatewayMessage.session_accepted:type_name -> openshell.v1.SessionAccepted - 204, // 276: openshell.v1.GatewayMessage.session_rejected:type_name -> openshell.v1.SessionRejected - 207, // 277: openshell.v1.GatewayMessage.heartbeat:type_name -> openshell.v1.GatewayHeartbeat - 212, // 278: openshell.v1.GatewayMessage.relay_open:type_name -> openshell.v1.RelayOpen - 224, // 279: openshell.v1.GatewayMessage.relay_close:type_name -> openshell.v1.RelayClose - 205, // 280: openshell.v1.GatewayMessage.session_redirect:type_name -> openshell.v1.SessionRedirect - 200, // 281: openshell.v1.GatewayMessage.config_update:type_name -> openshell.v1.ConfigUpdate - 276, // 282: openshell.v1.GatewayMessage.service_route_lease:type_name -> openshell.v1.ServiceRouteLease - 200, // 283: openshell.v1.SessionAccepted.bootstrap:type_name -> openshell.v1.ConfigUpdate - 316, // 284: openshell.v1.SessionAccepted.heartbeat_interval:type_name -> google.protobuf.Duration - 326, // 285: openshell.v1.ConfigUpdate.sandbox_config:type_name -> openshell.sandbox.v1.SandboxConfigSnapshot - 169, // 286: openshell.v1.ConfigUpdate.provider_environment:type_name -> openshell.v1.ProviderEnvironmentSnapshot - 6, // 287: openshell.v1.ConfigUpdateResult.outcome:type_name -> openshell.v1.ConfigApplyOutcome - 201, // 288: openshell.v1.ConfigUpdateResult.failure:type_name -> openshell.v1.ConfigApplyFailure - 188, // 289: openshell.v1.ConfigUpdateResult.admission:type_name -> openshell.v1.SandboxConfigurationAdmission - 203, // 290: openshell.v1.ConfigUpdateResult.policy_load:type_name -> openshell.v1.PolicyLoadResult - 213, // 291: openshell.v1.RelayOpen.ssh:type_name -> openshell.v1.SshRelayTarget - 214, // 292: openshell.v1.RelayOpen.tcp:type_name -> openshell.v1.TcpRelayTarget - 215, // 293: openshell.v1.RelayFrame.init:type_name -> openshell.v1.RelayInit - 212, // 294: openshell.v1.PeerRelayInit.relay_open:type_name -> openshell.v1.RelayOpen - 217, // 295: openshell.v1.PeerRelayFrame.init:type_name -> openshell.v1.PeerRelayInit - 219, // 296: openshell.v1.PeerNotifyConfigUpdateRequest.sandbox:type_name -> openshell.v1.PeerConfigSandboxTarget - 220, // 297: openshell.v1.PeerNotifyConfigUpdateRequest.provider:type_name -> openshell.v1.PeerConfigProviderTarget - 312, // 298: openshell.v1.DenialSummary.first_seen_time:type_name -> google.protobuf.Timestamp - 312, // 299: openshell.v1.DenialSummary.last_seen_time:type_name -> google.protobuf.Timestamp - 225, // 300: openshell.v1.DenialSummary.l7_request_samples:type_name -> openshell.v1.L7RequestSample - 227, // 301: openshell.v1.NetworkActivitySummary.denials_by_group:type_name -> openshell.v1.DenialGroupCount - 323, // 302: openshell.v1.PolicyChunk.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 312, // 303: openshell.v1.PolicyChunk.created_time:type_name -> google.protobuf.Timestamp - 312, // 304: openshell.v1.PolicyChunk.decided_time:type_name -> google.protobuf.Timestamp - 312, // 305: openshell.v1.PolicyChunk.first_seen_time:type_name -> google.protobuf.Timestamp - 312, // 306: openshell.v1.PolicyChunk.last_seen_time:type_name -> google.protobuf.Timestamp - 314, // 307: openshell.v1.PolicyChunk.current_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 314, // 308: openshell.v1.PolicyChunk.candidate_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 317, // 309: openshell.v1.SubmitPolicyAnalysisRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 226, // 310: openshell.v1.SubmitPolicyAnalysisRequest.summaries:type_name -> openshell.v1.DenialSummary - 229, // 311: openshell.v1.SubmitPolicyAnalysisRequest.proposed_chunks:type_name -> openshell.v1.PolicyChunk - 228, // 312: openshell.v1.SubmitPolicyAnalysisRequest.network_activity_summaries:type_name -> openshell.v1.NetworkActivitySummary - 317, // 313: openshell.v1.GetDraftPolicyRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 229, // 314: openshell.v1.GetDraftPolicyResponse.chunks:type_name -> openshell.v1.PolicyChunk - 312, // 315: openshell.v1.GetDraftPolicyResponse.last_analyzed_time:type_name -> google.protobuf.Timestamp - 317, // 316: openshell.v1.ApproveDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 317: openshell.v1.RejectDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 318: openshell.v1.ApproveAllDraftChunksRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 239, // 319: openshell.v1.ApproveAllDraftChunksRequest.approvals:type_name -> openshell.v1.DraftChunkApproval - 317, // 320: openshell.v1.EditDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 323, // 321: openshell.v1.EditDraftChunkRequest.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 317, // 322: openshell.v1.UndoDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 323: openshell.v1.ClearDraftChunksRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 317, // 324: openshell.v1.GetDraftHistoryRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 312, // 325: openshell.v1.DraftHistoryEntry.event_time:type_name -> google.protobuf.Timestamp - 249, // 326: openshell.v1.GetDraftHistoryResponse.entries:type_name -> openshell.v1.DraftHistoryEntry - 311, // 327: openshell.v1.CreateWorkspaceRequest.labels:type_name -> openshell.v1.CreateWorkspaceRequest.LabelsEntry - 327, // 328: openshell.v1.CreateWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace - 327, // 329: openshell.v1.GetWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace - 327, // 330: openshell.v1.ListWorkspacesResponse.workspaces:type_name -> openshell.datamodel.v1.Workspace - 18, // 331: openshell.v1.DeleteWorkspaceResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 313, // 332: openshell.v1.WorkspaceMember.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 15, // 333: openshell.v1.WorkspaceMember.role:type_name -> openshell.v1.WorkspaceRole - 317, // 334: openshell.v1.AddWorkspaceMemberRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 15, // 335: openshell.v1.AddWorkspaceMemberRequest.role:type_name -> openshell.v1.WorkspaceRole - 259, // 336: openshell.v1.AddWorkspaceMemberResponse.member:type_name -> openshell.v1.WorkspaceMember - 317, // 337: openshell.v1.RemoveWorkspaceMemberRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 18, // 338: openshell.v1.RemoveWorkspaceMemberResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 317, // 339: openshell.v1.ListWorkspaceMembersRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 259, // 340: openshell.v1.ListWorkspaceMembersResponse.members:type_name -> openshell.v1.WorkspaceMember - 312, // 341: openshell.v1.ExtensionServiceCredential.expiration_time:type_name -> google.protobuf.Timestamp - 19, // 342: openshell.v1.EndpointObservation.result:type_name -> openshell.v1.EndpointResult - 275, // 343: openshell.v1.EndpointObservation.failure:type_name -> openshell.v1.EndpointFailure - 267, // 344: openshell.v1.ReportEndpointStatusRequest.observations:type_name -> openshell.v1.EndpointObservation - 269, // 345: openshell.v1.ReportEndpointStatusRequest.token_grant_status:type_name -> openshell.v1.TokenGrantStatus - 270, // 346: openshell.v1.TokenGrantStatus.last_failure:type_name -> openshell.v1.TokenGrantFailureObservation - 275, // 347: openshell.v1.TokenGrantFailureObservation.failure:type_name -> openshell.v1.EndpointFailure - 19, // 348: openshell.v1.EndpointStatus.last_result:type_name -> openshell.v1.EndpointResult - 312, // 349: openshell.v1.EndpointStatus.last_reported_time:type_name -> google.protobuf.Timestamp - 275, // 350: openshell.v1.EndpointStatus.last_failure:type_name -> openshell.v1.EndpointFailure - 312, // 351: openshell.v1.SandboxProvisioning.configuration_change_time:type_name -> google.protobuf.Timestamp - 312, // 352: openshell.v1.SandboxProvisioning.first_rejection_time:type_name -> google.protobuf.Timestamp - 312, // 353: openshell.v1.SandboxProvisioning.deadline:type_name -> google.protobuf.Timestamp - 312, // 354: openshell.v1.SandboxProvisioning.timeout_time:type_name -> google.protobuf.Timestamp - 312, // 355: openshell.v1.SandboxProvisioning.cleanup_completed_time:type_name -> google.protobuf.Timestamp - 312, // 356: openshell.v1.SandboxProvisioning.cleanup_retry_time:type_name -> google.protobuf.Timestamp - 312, // 357: openshell.v1.SandboxProvisioning.attachment_change_time:type_name -> google.protobuf.Timestamp - 312, // 358: openshell.v1.SandboxProvisioning.preparation_deadline:type_name -> google.protobuf.Timestamp - 312, // 359: openshell.v1.SandboxProvisioning.admission_start_time:type_name -> google.protobuf.Timestamp - 20, // 360: openshell.v1.SandboxServiceExposure.authorization_mode:type_name -> openshell.v1.ServiceAuthorizationMode - 21, // 361: openshell.v1.EndpointFailure.domain:type_name -> openshell.v1.EndpointFailureDomain - 16, // 362: openshell.v1.EndpointFailure.recovery_action:type_name -> openshell.v1.ProviderCredentialRefreshRecoveryAction - 316, // 363: openshell.v1.ServiceRouteLease.duration:type_name -> google.protobuf.Duration - 312, // 364: openshell.v1.ServiceRouteLease.expires_at:type_name -> google.protobuf.Timestamp - 328, // 365: openshell.v1.SupervisorServiceAdvertisement.routes:type_name -> openshell.sandbox.v1.SandboxServiceRoute - 317, // 366: openshell.v1.GetServiceRouteSnapshotRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 282, // 367: openshell.v1.GetServiceRouteSnapshotResponse.snapshot:type_name -> openshell.v1.ServiceRouteSnapshot - 317, // 368: openshell.v1.WatchServiceRoutesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 282, // 369: openshell.v1.WatchServiceRoutesResponse.snapshot:type_name -> openshell.v1.ServiceRouteSnapshot - 284, // 370: openshell.v1.ServiceRouteSnapshot.routes:type_name -> openshell.v1.DiscoveredServiceRoute - 283, // 371: openshell.v1.DiscoveredServiceRoute.endpoint:type_name -> openshell.v1.ServiceRouteEndpoint - 312, // 372: openshell.v1.DiscoveredServiceRoute.expires_at:type_name -> google.protobuf.Timestamp - 123, // 373: openshell.v1.GetProviderCredentialsResponse.CredentialsEntry.value:type_name -> openshell.v1.ProviderCredentialValue - 312, // 374: openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry.value:type_name -> google.protobuf.Timestamp - 312, // 375: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry.value:type_name -> google.protobuf.Timestamp - 137, // 376: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential - 166, // 377: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry.value:type_name -> openshell.v1.StaticCredentialBinding - 137, // 378: openshell.v1.ProviderEnvironmentSnapshot.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential - 26, // 379: openshell.v1.OpenShell.Health:input_type -> openshell.v1.HealthRequest - 28, // 380: openshell.v1.OpenShell.GetCurrentUser:input_type -> openshell.v1.GetCurrentUserRequest - 30, // 381: openshell.v1.OpenShell.GetGatewayInfo:input_type -> openshell.v1.GetGatewayInfoRequest - 54, // 382: openshell.v1.OpenShell.CreateSandbox:input_type -> openshell.v1.CreateSandboxRequest - 65, // 383: openshell.v1.OpenShell.BeginRootfsTarStaging:input_type -> openshell.v1.BeginRootfsTarStagingRequest - 67, // 384: openshell.v1.OpenShell.GetSandbox:input_type -> openshell.v1.GetSandboxRequest - 68, // 385: openshell.v1.OpenShell.ListSandboxes:input_type -> openshell.v1.ListSandboxesRequest - 58, // 386: openshell.v1.OpenShell.CreateSandboxTemplate:input_type -> openshell.v1.CreateSandboxTemplateRequest - 59, // 387: openshell.v1.OpenShell.GetSandboxTemplate:input_type -> openshell.v1.GetSandboxTemplateRequest - 60, // 388: openshell.v1.OpenShell.ListSandboxTemplates:input_type -> openshell.v1.ListSandboxTemplatesRequest - 61, // 389: openshell.v1.OpenShell.DeleteSandboxTemplate:input_type -> openshell.v1.DeleteSandboxTemplateRequest - 69, // 390: openshell.v1.OpenShell.ListSandboxProviders:input_type -> openshell.v1.ListSandboxProvidersRequest - 70, // 391: openshell.v1.OpenShell.AttachSandboxProvider:input_type -> openshell.v1.AttachSandboxProviderRequest - 71, // 392: openshell.v1.OpenShell.DetachSandboxProvider:input_type -> openshell.v1.DetachSandboxProviderRequest - 89, // 393: openshell.v1.OpenShell.GetSandboxProviderStatus:input_type -> openshell.v1.GetSandboxProviderStatusRequest - 72, // 394: openshell.v1.OpenShell.DeleteSandbox:input_type -> openshell.v1.DeleteSandboxRequest - 73, // 395: openshell.v1.OpenShell.StopSandbox:input_type -> openshell.v1.StopSandboxRequest - 74, // 396: openshell.v1.OpenShell.StartSandbox:input_type -> openshell.v1.StartSandboxRequest - 75, // 397: openshell.v1.OpenShell.SuspendSandbox:input_type -> openshell.v1.SuspendSandboxRequest - 76, // 398: openshell.v1.OpenShell.ResumeSandbox:input_type -> openshell.v1.ResumeSandboxRequest - 94, // 399: openshell.v1.OpenShell.CreateSshSession:input_type -> openshell.v1.CreateSshSessionRequest - 96, // 400: openshell.v1.OpenShell.ExposeService:input_type -> openshell.v1.ExposeServiceRequest - 97, // 401: openshell.v1.OpenShell.GetService:input_type -> openshell.v1.GetServiceRequest - 98, // 402: openshell.v1.OpenShell.ListServices:input_type -> openshell.v1.ListServicesRequest - 278, // 403: openshell.v1.OpenShell.GetServiceRouteSnapshot:input_type -> openshell.v1.GetServiceRouteSnapshotRequest - 280, // 404: openshell.v1.OpenShell.WatchServiceRoutes:input_type -> openshell.v1.WatchServiceRoutesRequest - 100, // 405: openshell.v1.OpenShell.DeleteService:input_type -> openshell.v1.DeleteServiceRequest - 104, // 406: openshell.v1.OpenShell.RevokeSshSession:input_type -> openshell.v1.RevokeSshSessionRequest - 106, // 407: openshell.v1.OpenShell.ExecSandbox:input_type -> openshell.v1.ExecSandboxRequest - 112, // 408: openshell.v1.OpenShell.ForwardTcp:input_type -> openshell.v1.TcpForwardFrame - 113, // 409: openshell.v1.OpenShell.ExecSandboxInteractive:input_type -> openshell.v1.ExecSandboxInput - 120, // 410: openshell.v1.OpenShell.CreateProvider:input_type -> openshell.v1.CreateProviderRequest - 121, // 411: openshell.v1.OpenShell.GetProvider:input_type -> openshell.v1.GetProviderRequest - 122, // 412: openshell.v1.OpenShell.GetProviderCredentials:input_type -> openshell.v1.GetProviderCredentialsRequest - 125, // 413: openshell.v1.OpenShell.ListProviders:input_type -> openshell.v1.ListProvidersRequest - 130, // 414: openshell.v1.OpenShell.ListProviderProfiles:input_type -> openshell.v1.ListProviderProfilesRequest - 131, // 415: openshell.v1.OpenShell.GetProviderProfile:input_type -> openshell.v1.GetProviderProfileRequest - 155, // 416: openshell.v1.OpenShell.ImportProviderProfiles:input_type -> openshell.v1.ImportProviderProfilesRequest - 157, // 417: openshell.v1.OpenShell.UpdateProviderProfiles:input_type -> openshell.v1.UpdateProviderProfilesRequest - 159, // 418: openshell.v1.OpenShell.LintProviderProfiles:input_type -> openshell.v1.LintProviderProfilesRequest - 126, // 419: openshell.v1.OpenShell.UpdateProvider:input_type -> openshell.v1.UpdateProviderRequest - 143, // 420: openshell.v1.OpenShell.GetProviderRefreshStatus:input_type -> openshell.v1.GetProviderRefreshStatusRequest - 145, // 421: openshell.v1.OpenShell.ConfigureProviderRefresh:input_type -> openshell.v1.ConfigureProviderRefreshRequest - 147, // 422: openshell.v1.OpenShell.RotateProviderCredential:input_type -> openshell.v1.RotateProviderCredentialRequest - 149, // 423: openshell.v1.OpenShell.DeleteProviderRefresh:input_type -> openshell.v1.DeleteProviderRefreshRequest - 127, // 424: openshell.v1.OpenShell.DeleteProvider:input_type -> openshell.v1.DeleteProviderRequest - 162, // 425: openshell.v1.OpenShell.DeleteProviderProfile:input_type -> openshell.v1.DeleteProviderProfileRequest - 329, // 426: openshell.v1.OpenShell.GetSandboxConfig:input_type -> openshell.sandbox.v1.GetSandboxConfigRequest - 330, // 427: openshell.v1.OpenShell.GetGatewayConfig:input_type -> openshell.sandbox.v1.GetGatewayConfigRequest - 172, // 428: openshell.v1.OpenShell.UpdateConfig:input_type -> openshell.v1.UpdateConfigRequest - 182, // 429: openshell.v1.OpenShell.GetSandboxPolicyStatus:input_type -> openshell.v1.GetSandboxPolicyStatusRequest - 184, // 430: openshell.v1.OpenShell.ListSandboxPolicies:input_type -> openshell.v1.ListSandboxPoliciesRequest - 186, // 431: openshell.v1.OpenShell.ReportPolicyStatus:input_type -> openshell.v1.ReportPolicyStatusRequest - 268, // 432: openshell.v1.OpenShell.ReportEndpointStatus:input_type -> openshell.v1.ReportEndpointStatusRequest - 91, // 433: openshell.v1.OpenShell.ReportProviderReadiness:input_type -> openshell.v1.ReportProviderReadinessRequest - 189, // 434: openshell.v1.OpenShell.ReportSandboxConfiguration:input_type -> openshell.v1.ReportSandboxConfigurationRequest - 164, // 435: openshell.v1.OpenShell.GetSandboxProviderEnvironment:input_type -> openshell.v1.GetSandboxProviderEnvironmentRequest - 170, // 436: openshell.v1.OpenShell.ExchangeProviderSubjectToken:input_type -> openshell.v1.ExchangeProviderSubjectTokenRequest - 192, // 437: openshell.v1.OpenShell.GetSandboxLogs:input_type -> openshell.v1.GetSandboxLogsRequest - 193, // 438: openshell.v1.OpenShell.PushSandboxLogs:input_type -> openshell.v1.PushSandboxLogsRequest - 196, // 439: openshell.v1.OpenShell.ConnectSupervisor:input_type -> openshell.v1.SupervisorMessage - 208, // 440: openshell.v1.OpenShell.ReportMainProcessExit:input_type -> openshell.v1.ReportMainProcessExitRequest - 210, // 441: openshell.v1.OpenShell.FinalizeMainProcessExit:input_type -> openshell.v1.FinalizeMainProcessExitRequest - 216, // 442: openshell.v1.OpenShell.RelayStream:input_type -> openshell.v1.RelayFrame - 218, // 443: openshell.v1.OpenShell.PeerRelay:input_type -> openshell.v1.PeerRelayFrame - 91, // 444: openshell.v1.OpenShell.PeerReportProviderReadiness:input_type -> openshell.v1.ReportProviderReadinessRequest - 268, // 445: openshell.v1.OpenShell.PeerReportEndpointStatus:input_type -> openshell.v1.ReportEndpointStatusRequest - 89, // 446: openshell.v1.OpenShell.PeerGetSandboxProviderStatus:input_type -> openshell.v1.GetSandboxProviderStatusRequest - 221, // 447: openshell.v1.OpenShell.PeerNotifyConfigUpdate:input_type -> openshell.v1.PeerNotifyConfigUpdateRequest - 116, // 448: openshell.v1.OpenShell.WatchSandbox:input_type -> openshell.v1.WatchSandboxRequest - 231, // 449: openshell.v1.OpenShell.SubmitPolicyAnalysis:input_type -> openshell.v1.SubmitPolicyAnalysisRequest - 233, // 450: openshell.v1.OpenShell.GetDraftPolicy:input_type -> openshell.v1.GetDraftPolicyRequest - 235, // 451: openshell.v1.OpenShell.ApproveDraftChunk:input_type -> openshell.v1.ApproveDraftChunkRequest - 237, // 452: openshell.v1.OpenShell.RejectDraftChunk:input_type -> openshell.v1.RejectDraftChunkRequest - 240, // 453: openshell.v1.OpenShell.ApproveAllDraftChunks:input_type -> openshell.v1.ApproveAllDraftChunksRequest - 242, // 454: openshell.v1.OpenShell.EditDraftChunk:input_type -> openshell.v1.EditDraftChunkRequest - 244, // 455: openshell.v1.OpenShell.UndoDraftChunk:input_type -> openshell.v1.UndoDraftChunkRequest - 246, // 456: openshell.v1.OpenShell.ClearDraftChunks:input_type -> openshell.v1.ClearDraftChunksRequest - 248, // 457: openshell.v1.OpenShell.GetDraftHistory:input_type -> openshell.v1.GetDraftHistoryRequest - 22, // 458: openshell.v1.OpenShell.IssueSandboxToken:input_type -> openshell.v1.IssueSandboxTokenRequest - 24, // 459: openshell.v1.OpenShell.RefreshSandboxToken:input_type -> openshell.v1.RefreshSandboxTokenRequest - 251, // 460: openshell.v1.OpenShell.CreateWorkspace:input_type -> openshell.v1.CreateWorkspaceRequest - 253, // 461: openshell.v1.OpenShell.GetWorkspace:input_type -> openshell.v1.GetWorkspaceRequest - 255, // 462: openshell.v1.OpenShell.ListWorkspaces:input_type -> openshell.v1.ListWorkspacesRequest - 257, // 463: openshell.v1.OpenShell.DeleteWorkspace:input_type -> openshell.v1.DeleteWorkspaceRequest - 260, // 464: openshell.v1.OpenShell.AddWorkspaceMember:input_type -> openshell.v1.AddWorkspaceMemberRequest - 262, // 465: openshell.v1.OpenShell.RemoveWorkspaceMember:input_type -> openshell.v1.RemoveWorkspaceMemberRequest - 264, // 466: openshell.v1.OpenShell.ListWorkspaceMembers:input_type -> openshell.v1.ListWorkspaceMembersRequest - 55, // 467: openshell.v1.SandboxUpdates.UpdateSandbox:input_type -> openshell.v1.UpdateSandboxRequest - 27, // 468: openshell.v1.OpenShell.Health:output_type -> openshell.v1.HealthResponse - 29, // 469: openshell.v1.OpenShell.GetCurrentUser:output_type -> openshell.v1.GetCurrentUserResponse - 31, // 470: openshell.v1.OpenShell.GetGatewayInfo:output_type -> openshell.v1.GetGatewayInfoResponse - 77, // 471: openshell.v1.OpenShell.CreateSandbox:output_type -> openshell.v1.SandboxResponse - 66, // 472: openshell.v1.OpenShell.BeginRootfsTarStaging:output_type -> openshell.v1.BeginRootfsTarStagingResponse - 77, // 473: openshell.v1.OpenShell.GetSandbox:output_type -> openshell.v1.SandboxResponse - 78, // 474: openshell.v1.OpenShell.ListSandboxes:output_type -> openshell.v1.ListSandboxesResponse - 62, // 475: openshell.v1.OpenShell.CreateSandboxTemplate:output_type -> openshell.v1.SandboxTemplateResponse - 62, // 476: openshell.v1.OpenShell.GetSandboxTemplate:output_type -> openshell.v1.SandboxTemplateResponse - 63, // 477: openshell.v1.OpenShell.ListSandboxTemplates:output_type -> openshell.v1.ListSandboxTemplatesResponse - 64, // 478: openshell.v1.OpenShell.DeleteSandboxTemplate:output_type -> openshell.v1.DeleteSandboxTemplateResponse - 79, // 479: openshell.v1.OpenShell.ListSandboxProviders:output_type -> openshell.v1.ListSandboxProvidersResponse - 80, // 480: openshell.v1.OpenShell.AttachSandboxProvider:output_type -> openshell.v1.AttachSandboxProviderResponse - 81, // 481: openshell.v1.OpenShell.DetachSandboxProvider:output_type -> openshell.v1.DetachSandboxProviderResponse - 90, // 482: openshell.v1.OpenShell.GetSandboxProviderStatus:output_type -> openshell.v1.GetSandboxProviderStatusResponse - 93, // 483: openshell.v1.OpenShell.DeleteSandbox:output_type -> openshell.v1.DeleteSandboxResponse - 77, // 484: openshell.v1.OpenShell.StopSandbox:output_type -> openshell.v1.SandboxResponse - 77, // 485: openshell.v1.OpenShell.StartSandbox:output_type -> openshell.v1.SandboxResponse - 77, // 486: openshell.v1.OpenShell.SuspendSandbox:output_type -> openshell.v1.SandboxResponse - 77, // 487: openshell.v1.OpenShell.ResumeSandbox:output_type -> openshell.v1.SandboxResponse - 95, // 488: openshell.v1.OpenShell.CreateSshSession:output_type -> openshell.v1.CreateSshSessionResponse - 103, // 489: openshell.v1.OpenShell.ExposeService:output_type -> openshell.v1.ServiceEndpointResponse - 103, // 490: openshell.v1.OpenShell.GetService:output_type -> openshell.v1.ServiceEndpointResponse - 99, // 491: openshell.v1.OpenShell.ListServices:output_type -> openshell.v1.ListServicesResponse - 279, // 492: openshell.v1.OpenShell.GetServiceRouteSnapshot:output_type -> openshell.v1.GetServiceRouteSnapshotResponse - 281, // 493: openshell.v1.OpenShell.WatchServiceRoutes:output_type -> openshell.v1.WatchServiceRoutesResponse - 101, // 494: openshell.v1.OpenShell.DeleteService:output_type -> openshell.v1.DeleteServiceResponse - 105, // 495: openshell.v1.OpenShell.RevokeSshSession:output_type -> openshell.v1.RevokeSshSessionResponse - 110, // 496: openshell.v1.OpenShell.ExecSandbox:output_type -> openshell.v1.ExecSandboxEvent - 112, // 497: openshell.v1.OpenShell.ForwardTcp:output_type -> openshell.v1.TcpForwardFrame - 110, // 498: openshell.v1.OpenShell.ExecSandboxInteractive:output_type -> openshell.v1.ExecSandboxEvent - 128, // 499: openshell.v1.OpenShell.CreateProvider:output_type -> openshell.v1.ProviderResponse - 128, // 500: openshell.v1.OpenShell.GetProvider:output_type -> openshell.v1.ProviderResponse - 124, // 501: openshell.v1.OpenShell.GetProviderCredentials:output_type -> openshell.v1.GetProviderCredentialsResponse - 129, // 502: openshell.v1.OpenShell.ListProviders:output_type -> openshell.v1.ListProvidersResponse - 154, // 503: openshell.v1.OpenShell.ListProviderProfiles:output_type -> openshell.v1.ListProviderProfilesResponse - 153, // 504: openshell.v1.OpenShell.GetProviderProfile:output_type -> openshell.v1.ProviderProfileResponse - 156, // 505: openshell.v1.OpenShell.ImportProviderProfiles:output_type -> openshell.v1.ImportProviderProfilesResponse - 158, // 506: openshell.v1.OpenShell.UpdateProviderProfiles:output_type -> openshell.v1.UpdateProviderProfilesResponse - 160, // 507: openshell.v1.OpenShell.LintProviderProfiles:output_type -> openshell.v1.LintProviderProfilesResponse - 128, // 508: openshell.v1.OpenShell.UpdateProvider:output_type -> openshell.v1.ProviderResponse - 144, // 509: openshell.v1.OpenShell.GetProviderRefreshStatus:output_type -> openshell.v1.GetProviderRefreshStatusResponse - 146, // 510: openshell.v1.OpenShell.ConfigureProviderRefresh:output_type -> openshell.v1.ConfigureProviderRefreshResponse - 148, // 511: openshell.v1.OpenShell.RotateProviderCredential:output_type -> openshell.v1.RotateProviderCredentialResponse - 150, // 512: openshell.v1.OpenShell.DeleteProviderRefresh:output_type -> openshell.v1.DeleteProviderRefreshResponse - 161, // 513: openshell.v1.OpenShell.DeleteProvider:output_type -> openshell.v1.DeleteProviderResponse - 163, // 514: openshell.v1.OpenShell.DeleteProviderProfile:output_type -> openshell.v1.DeleteProviderProfileResponse - 331, // 515: openshell.v1.OpenShell.GetSandboxConfig:output_type -> openshell.sandbox.v1.GetSandboxConfigResponse - 332, // 516: openshell.v1.OpenShell.GetGatewayConfig:output_type -> openshell.sandbox.v1.GetGatewayConfigResponse - 181, // 517: openshell.v1.OpenShell.UpdateConfig:output_type -> openshell.v1.UpdateConfigResponse - 183, // 518: openshell.v1.OpenShell.GetSandboxPolicyStatus:output_type -> openshell.v1.GetSandboxPolicyStatusResponse - 185, // 519: openshell.v1.OpenShell.ListSandboxPolicies:output_type -> openshell.v1.ListSandboxPoliciesResponse - 187, // 520: openshell.v1.OpenShell.ReportPolicyStatus:output_type -> openshell.v1.ReportPolicyStatusResponse - 271, // 521: openshell.v1.OpenShell.ReportEndpointStatus:output_type -> openshell.v1.ReportEndpointStatusResponse - 92, // 522: openshell.v1.OpenShell.ReportProviderReadiness:output_type -> openshell.v1.ReportProviderReadinessResponse - 190, // 523: openshell.v1.OpenShell.ReportSandboxConfiguration:output_type -> openshell.v1.ReportSandboxConfigurationResponse - 167, // 524: openshell.v1.OpenShell.GetSandboxProviderEnvironment:output_type -> openshell.v1.GetSandboxProviderEnvironmentResponse - 171, // 525: openshell.v1.OpenShell.ExchangeProviderSubjectToken:output_type -> openshell.v1.ExchangeProviderSubjectTokenResponse - 195, // 526: openshell.v1.OpenShell.GetSandboxLogs:output_type -> openshell.v1.GetSandboxLogsResponse - 194, // 527: openshell.v1.OpenShell.PushSandboxLogs:output_type -> openshell.v1.PushSandboxLogsResponse - 197, // 528: openshell.v1.OpenShell.ConnectSupervisor:output_type -> openshell.v1.GatewayMessage - 209, // 529: openshell.v1.OpenShell.ReportMainProcessExit:output_type -> openshell.v1.ReportMainProcessExitResponse - 211, // 530: openshell.v1.OpenShell.FinalizeMainProcessExit:output_type -> openshell.v1.FinalizeMainProcessExitResponse - 216, // 531: openshell.v1.OpenShell.RelayStream:output_type -> openshell.v1.RelayFrame - 218, // 532: openshell.v1.OpenShell.PeerRelay:output_type -> openshell.v1.PeerRelayFrame - 92, // 533: openshell.v1.OpenShell.PeerReportProviderReadiness:output_type -> openshell.v1.ReportProviderReadinessResponse - 271, // 534: openshell.v1.OpenShell.PeerReportEndpointStatus:output_type -> openshell.v1.ReportEndpointStatusResponse - 90, // 535: openshell.v1.OpenShell.PeerGetSandboxProviderStatus:output_type -> openshell.v1.GetSandboxProviderStatusResponse - 222, // 536: openshell.v1.OpenShell.PeerNotifyConfigUpdate:output_type -> openshell.v1.PeerNotifyConfigUpdateResponse - 117, // 537: openshell.v1.OpenShell.WatchSandbox:output_type -> openshell.v1.SandboxStreamEvent - 232, // 538: openshell.v1.OpenShell.SubmitPolicyAnalysis:output_type -> openshell.v1.SubmitPolicyAnalysisResponse - 234, // 539: openshell.v1.OpenShell.GetDraftPolicy:output_type -> openshell.v1.GetDraftPolicyResponse - 236, // 540: openshell.v1.OpenShell.ApproveDraftChunk:output_type -> openshell.v1.ApproveDraftChunkResponse - 238, // 541: openshell.v1.OpenShell.RejectDraftChunk:output_type -> openshell.v1.RejectDraftChunkResponse - 241, // 542: openshell.v1.OpenShell.ApproveAllDraftChunks:output_type -> openshell.v1.ApproveAllDraftChunksResponse - 243, // 543: openshell.v1.OpenShell.EditDraftChunk:output_type -> openshell.v1.EditDraftChunkResponse - 245, // 544: openshell.v1.OpenShell.UndoDraftChunk:output_type -> openshell.v1.UndoDraftChunkResponse - 247, // 545: openshell.v1.OpenShell.ClearDraftChunks:output_type -> openshell.v1.ClearDraftChunksResponse - 250, // 546: openshell.v1.OpenShell.GetDraftHistory:output_type -> openshell.v1.GetDraftHistoryResponse - 23, // 547: openshell.v1.OpenShell.IssueSandboxToken:output_type -> openshell.v1.IssueSandboxTokenResponse - 25, // 548: openshell.v1.OpenShell.RefreshSandboxToken:output_type -> openshell.v1.RefreshSandboxTokenResponse - 252, // 549: openshell.v1.OpenShell.CreateWorkspace:output_type -> openshell.v1.CreateWorkspaceResponse - 254, // 550: openshell.v1.OpenShell.GetWorkspace:output_type -> openshell.v1.GetWorkspaceResponse - 256, // 551: openshell.v1.OpenShell.ListWorkspaces:output_type -> openshell.v1.ListWorkspacesResponse - 258, // 552: openshell.v1.OpenShell.DeleteWorkspace:output_type -> openshell.v1.DeleteWorkspaceResponse - 261, // 553: openshell.v1.OpenShell.AddWorkspaceMember:output_type -> openshell.v1.AddWorkspaceMemberResponse - 263, // 554: openshell.v1.OpenShell.RemoveWorkspaceMember:output_type -> openshell.v1.RemoveWorkspaceMemberResponse - 265, // 555: openshell.v1.OpenShell.ListWorkspaceMembers:output_type -> openshell.v1.ListWorkspaceMembersResponse - 77, // 556: openshell.v1.SandboxUpdates.UpdateSandbox:output_type -> openshell.v1.SandboxResponse - 468, // [468:557] is the sub-list for method output_type - 379, // [379:468] is the sub-list for method input_type - 379, // [379:379] is the sub-list for extension type_name - 379, // [379:379] is the sub-list for extension extendee - 0, // [0:379] is the sub-list for field type_name + 295, // 83: openshell.v1.SandboxResponse.application_service_urls:type_name -> openshell.v1.SandboxResponse.ApplicationServiceUrlsEntry + 296, // 84: openshell.v1.SandboxResponse.gateway_service_urls:type_name -> openshell.v1.SandboxResponse.GatewayServiceUrlsEntry + 39, // 85: openshell.v1.ListSandboxesResponse.sandboxes:type_name -> openshell.v1.Sandbox + 320, // 86: openshell.v1.ListSandboxProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider + 39, // 87: openshell.v1.AttachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox + 86, // 88: openshell.v1.AttachSandboxProviderResponse.receipt:type_name -> openshell.v1.ProviderMutationReceipt + 39, // 89: openshell.v1.DetachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox + 86, // 90: openshell.v1.DetachSandboxProviderResponse.receipt:type_name -> openshell.v1.ProviderMutationReceipt + 84, // 91: openshell.v1.ConfigSnapshotRevision.sandbox_config:type_name -> openshell.v1.SandboxConfigRevision + 82, // 92: openshell.v1.ConfigSnapshotRevision.provider_target:type_name -> openshell.v1.ProviderDesiredIdentity + 321, // 93: openshell.v1.SandboxConfigRevision.policy_source:type_name -> openshell.sandbox.v1.PolicySource + 5, // 94: openshell.v1.ConfigUpdateOperation.component:type_name -> openshell.v1.ConfigComponent + 83, // 95: openshell.v1.ConfigUpdateOperation.target_revision:type_name -> openshell.v1.ConfigSnapshotRevision + 7, // 96: openshell.v1.ConfigUpdateOperation.state:type_name -> openshell.v1.ConfigUpdateOperationState + 6, // 97: openshell.v1.ConfigUpdateOperation.outcome:type_name -> openshell.v1.ConfigApplyOutcome + 314, // 98: openshell.v1.ConfigUpdateOperation.created_time:type_name -> google.protobuf.Timestamp + 314, // 99: openshell.v1.ConfigUpdateOperation.updated_time:type_name -> google.protobuf.Timestamp + 314, // 100: openshell.v1.ConfigUpdateOperation.completed_time:type_name -> google.protobuf.Timestamp + 2, // 101: openshell.v1.ProviderMutationReceipt.kind:type_name -> openshell.v1.ProviderMutationKind + 82, // 102: openshell.v1.ProviderMutationReceipt.desired:type_name -> openshell.v1.ProviderDesiredIdentity + 314, // 103: openshell.v1.ProviderMutationReceipt.persisted_time:type_name -> google.protobuf.Timestamp + 4, // 104: openshell.v1.ProviderReadinessObservation.reason:type_name -> openshell.v1.ProviderReadinessReason + 86, // 105: openshell.v1.ProviderReadinessStatus.receipt:type_name -> openshell.v1.ProviderMutationReceipt + 3, // 106: openshell.v1.ProviderReadinessStatus.state:type_name -> openshell.v1.ProviderReadinessState + 4, // 107: openshell.v1.ProviderReadinessStatus.reason:type_name -> openshell.v1.ProviderReadinessReason + 87, // 108: openshell.v1.ProviderReadinessStatus.observed:type_name -> openshell.v1.ProviderReadinessObservation + 314, // 109: openshell.v1.ProviderReadinessStatus.observed_time:type_name -> google.protobuf.Timestamp + 314, // 110: openshell.v1.ProviderReadinessStatus.evaluated_time:type_name -> google.protobuf.Timestamp + 85, // 111: openshell.v1.ProviderReadinessStatus.operation:type_name -> openshell.v1.ConfigUpdateOperation + 319, // 112: openshell.v1.GetSandboxProviderStatusRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 88, // 113: openshell.v1.GetSandboxProviderStatusResponse.status:type_name -> openshell.v1.ProviderReadinessStatus + 87, // 114: openshell.v1.ReportProviderReadinessRequest.observation:type_name -> openshell.v1.ProviderReadinessObservation + 318, // 115: openshell.v1.ReportProviderReadinessResponse.report_interval:type_name -> google.protobuf.Duration + 318, // 116: openshell.v1.ReportProviderReadinessResponse.observation_ttl:type_name -> google.protobuf.Duration + 18, // 117: openshell.v1.DeleteSandboxResponse.outcome:type_name -> openshell.v1.DeletionOutcome + 319, // 118: openshell.v1.CreateSshSessionRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 314, // 119: openshell.v1.CreateSshSessionResponse.expiration_time:type_name -> google.protobuf.Timestamp + 319, // 120: openshell.v1.ExposeServiceRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 20, // 121: openshell.v1.ExposeServiceRequest.authorization_mode:type_name -> openshell.v1.ServiceAuthorizationMode + 319, // 122: openshell.v1.GetServiceRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 123: openshell.v1.ListServicesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 103, // 124: openshell.v1.ListServicesResponse.services:type_name -> openshell.v1.ServiceEndpointResponse + 319, // 125: openshell.v1.DeleteServiceRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 18, // 126: openshell.v1.DeleteServiceResponse.outcome:type_name -> openshell.v1.DeletionOutcome + 315, // 127: openshell.v1.ServiceEndpoint.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 20, // 128: openshell.v1.ServiceEndpoint.authorization_mode:type_name -> openshell.v1.ServiceAuthorizationMode + 102, // 129: openshell.v1.ServiceEndpointResponse.endpoint:type_name -> openshell.v1.ServiceEndpoint + 18, // 130: openshell.v1.RevokeSshSessionResponse.outcome:type_name -> openshell.v1.DeletionOutcome + 319, // 131: openshell.v1.ExecSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 297, // 132: openshell.v1.ExecSandboxRequest.environment:type_name -> openshell.v1.ExecSandboxRequest.EnvironmentEntry + 318, // 133: openshell.v1.ExecSandboxRequest.execution_timeout:type_name -> google.protobuf.Duration + 107, // 134: openshell.v1.ExecSandboxEvent.stdout:type_name -> openshell.v1.ExecSandboxStdout + 108, // 135: openshell.v1.ExecSandboxEvent.stderr:type_name -> openshell.v1.ExecSandboxStderr + 109, // 136: openshell.v1.ExecSandboxEvent.exit:type_name -> openshell.v1.ExecSandboxExit + 213, // 137: openshell.v1.TcpForwardInit.ssh:type_name -> openshell.v1.SshRelayTarget + 214, // 138: openshell.v1.TcpForwardInit.tcp:type_name -> openshell.v1.TcpRelayTarget + 111, // 139: openshell.v1.TcpForwardFrame.init:type_name -> openshell.v1.TcpForwardInit + 106, // 140: openshell.v1.ExecSandboxInput.start:type_name -> openshell.v1.ExecSandboxRequest + 114, // 141: openshell.v1.ExecSandboxInput.resize:type_name -> openshell.v1.ExecSandboxWindowResize + 315, // 142: openshell.v1.SshSession.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 314, // 143: openshell.v1.SshSession.expiration_time:type_name -> google.protobuf.Timestamp + 319, // 144: openshell.v1.WatchSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 314, // 145: openshell.v1.WatchSandboxRequest.since_time:type_name -> google.protobuf.Timestamp + 39, // 146: openshell.v1.SandboxStreamEvent.sandbox:type_name -> openshell.v1.Sandbox + 118, // 147: openshell.v1.SandboxStreamEvent.log:type_name -> openshell.v1.SandboxLogLine + 53, // 148: openshell.v1.SandboxStreamEvent.event:type_name -> openshell.v1.PlatformEvent + 119, // 149: openshell.v1.SandboxStreamEvent.warning:type_name -> openshell.v1.SandboxStreamWarning + 230, // 150: openshell.v1.SandboxStreamEvent.draft_policy_update:type_name -> openshell.v1.DraftPolicyUpdate + 314, // 151: openshell.v1.SandboxLogLine.event_time:type_name -> google.protobuf.Timestamp + 298, // 152: openshell.v1.SandboxLogLine.fields:type_name -> openshell.v1.SandboxLogLine.FieldsEntry + 319, // 153: openshell.v1.CreateProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 320, // 154: openshell.v1.CreateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider + 319, // 155: openshell.v1.GetProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 156: openshell.v1.GetProviderCredentialsRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 318, // 157: openshell.v1.GetProviderCredentialsRequest.minimum_remaining_lifetime:type_name -> google.protobuf.Duration + 314, // 158: openshell.v1.ProviderCredentialValue.expiration_time:type_name -> google.protobuf.Timestamp + 299, // 159: openshell.v1.GetProviderCredentialsResponse.credentials:type_name -> openshell.v1.GetProviderCredentialsResponse.CredentialsEntry + 319, // 160: openshell.v1.ListProvidersRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 161: openshell.v1.UpdateProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 320, // 162: openshell.v1.UpdateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider + 300, // 163: openshell.v1.UpdateProviderRequest.credential_expiration_times:type_name -> openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry + 319, // 164: openshell.v1.DeleteProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 320, // 165: openshell.v1.ProviderResponse.provider:type_name -> openshell.datamodel.v1.Provider + 86, // 166: openshell.v1.ProviderResponse.target_receipts:type_name -> openshell.v1.ProviderMutationReceipt + 320, // 167: openshell.v1.ListProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider + 319, // 168: openshell.v1.ListProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 169: openshell.v1.GetProviderProfileRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 151, // 170: openshell.v1.ProviderProfileImportItem.profile:type_name -> openshell.v1.ProviderProfile + 318, // 171: openshell.v1.ProviderCredentialTokenGrant.cache_ttl:type_name -> google.protobuf.Duration + 134, // 172: openshell.v1.ProviderCredentialTokenGrant.audience_overrides:type_name -> openshell.v1.ProviderCredentialTokenGrantAudienceOverride + 8, // 173: openshell.v1.ProviderCredentialTokenGrant.grant_type:type_name -> openshell.v1.ProviderCredentialTokenGrantType + 135, // 174: openshell.v1.ProviderCredentialTokenGrant.subject_token:type_name -> openshell.v1.ProviderCredentialTokenGrantSubjectToken + 140, // 175: openshell.v1.ProviderProfileCredential.refresh:type_name -> openshell.v1.ProviderCredentialRefresh + 136, // 176: openshell.v1.ProviderProfileCredential.token_grant:type_name -> openshell.v1.ProviderCredentialTokenGrant + 9, // 177: openshell.v1.ProviderCredentialRefresh.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy + 318, // 178: openshell.v1.ProviderCredentialRefresh.refresh_before:type_name -> google.protobuf.Duration + 318, // 179: openshell.v1.ProviderCredentialRefresh.max_lifetime:type_name -> google.protobuf.Duration + 138, // 180: openshell.v1.ProviderCredentialRefresh.material:type_name -> openshell.v1.ProviderCredentialRefreshMaterial + 139, // 181: openshell.v1.ProviderCredentialRefresh.additional_outputs:type_name -> openshell.v1.ProviderCredentialRefreshOutput + 9, // 182: openshell.v1.ProviderCredentialRefreshStatus.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy + 314, // 183: openshell.v1.ProviderCredentialRefreshStatus.expiration_time:type_name -> google.protobuf.Timestamp + 314, // 184: openshell.v1.ProviderCredentialRefreshStatus.next_refresh_time:type_name -> google.protobuf.Timestamp + 314, // 185: openshell.v1.ProviderCredentialRefreshStatus.last_refresh_time:type_name -> google.protobuf.Timestamp + 16, // 186: openshell.v1.ProviderCredentialRefreshStatus.recovery_action:type_name -> openshell.v1.ProviderCredentialRefreshRecoveryAction + 314, // 187: openshell.v1.ProviderCredentialRefreshStatus.last_error_time:type_name -> google.protobuf.Timestamp + 319, // 188: openshell.v1.GetProviderRefreshStatusRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 141, // 189: openshell.v1.GetProviderRefreshStatusResponse.credentials:type_name -> openshell.v1.ProviderCredentialRefreshStatus + 319, // 190: openshell.v1.ConfigureProviderRefreshRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 9, // 191: openshell.v1.ConfigureProviderRefreshRequest.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy + 301, // 192: openshell.v1.ConfigureProviderRefreshRequest.material:type_name -> openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry + 314, // 193: openshell.v1.ConfigureProviderRefreshRequest.expiration_time:type_name -> google.protobuf.Timestamp + 141, // 194: openshell.v1.ConfigureProviderRefreshResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus + 319, // 195: openshell.v1.RotateProviderCredentialRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 141, // 196: openshell.v1.RotateProviderCredentialResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus + 319, // 197: openshell.v1.DeleteProviderRefreshRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 18, // 198: openshell.v1.DeleteProviderRefreshResponse.outcome:type_name -> openshell.v1.DeletionOutcome + 10, // 199: openshell.v1.ProviderProfile.category:type_name -> openshell.v1.ProviderProfileCategory + 137, // 200: openshell.v1.ProviderProfile.credentials:type_name -> openshell.v1.ProviderProfileCredential + 322, // 201: openshell.v1.ProviderProfile.endpoints:type_name -> openshell.sandbox.v1.NetworkEndpoint + 323, // 202: openshell.v1.ProviderProfile.binaries:type_name -> openshell.sandbox.v1.NetworkBinary + 142, // 203: openshell.v1.ProviderProfile.discovery:type_name -> openshell.v1.ProviderProfileDiscovery + 302, // 204: openshell.v1.ProviderProfile.annotations:type_name -> openshell.v1.ProviderProfile.AnnotationsEntry + 152, // 205: openshell.v1.ProviderProfile.files:type_name -> openshell.v1.ProviderProfileFile + 151, // 206: openshell.v1.ProviderProfileResponse.profile:type_name -> openshell.v1.ProviderProfile + 151, // 207: openshell.v1.ListProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile + 319, // 208: openshell.v1.ImportProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 132, // 209: openshell.v1.ImportProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem + 133, // 210: openshell.v1.ImportProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic + 151, // 211: openshell.v1.ImportProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile + 319, // 212: openshell.v1.UpdateProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 132, // 213: openshell.v1.UpdateProviderProfilesRequest.profile:type_name -> openshell.v1.ProviderProfileImportItem + 133, // 214: openshell.v1.UpdateProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic + 151, // 215: openshell.v1.UpdateProviderProfilesResponse.profile:type_name -> openshell.v1.ProviderProfile + 319, // 216: openshell.v1.LintProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 132, // 217: openshell.v1.LintProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem + 133, // 218: openshell.v1.LintProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic + 18, // 219: openshell.v1.DeleteProviderResponse.outcome:type_name -> openshell.v1.DeletionOutcome + 319, // 220: openshell.v1.DeleteProviderProfileRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 18, // 221: openshell.v1.DeleteProviderProfileResponse.outcome:type_name -> openshell.v1.DeletionOutcome + 165, // 222: openshell.v1.StaticCredentialBinding.endpoints:type_name -> openshell.v1.StaticCredentialEndpointBinding + 303, // 223: openshell.v1.GetSandboxProviderEnvironmentResponse.environment:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry + 304, // 224: openshell.v1.GetSandboxProviderEnvironmentResponse.credential_expiration_times:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry + 305, // 225: openshell.v1.GetSandboxProviderEnvironmentResponse.dynamic_credentials:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry + 306, // 226: openshell.v1.GetSandboxProviderEnvironmentResponse.static_credential_bindings:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry + 4, // 227: openshell.v1.GetSandboxProviderEnvironmentResponse.readiness_reason:type_name -> openshell.v1.ProviderReadinessReason + 307, // 228: openshell.v1.GetSandboxProviderEnvironmentResponse.files:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.FilesEntry + 11, // 229: openshell.v1.ProviderEnvironmentValue.classification:type_name -> openshell.v1.ProviderEnvironmentValueClassification + 166, // 230: openshell.v1.ProviderEnvironmentValue.static_credential_binding:type_name -> openshell.v1.StaticCredentialBinding + 314, // 231: openshell.v1.ProviderEnvironmentValue.expiration_time:type_name -> google.protobuf.Timestamp + 168, // 232: openshell.v1.ProviderEnvironmentSnapshot.values:type_name -> openshell.v1.ProviderEnvironmentValue + 308, // 233: openshell.v1.ProviderEnvironmentSnapshot.dynamic_credentials:type_name -> openshell.v1.ProviderEnvironmentSnapshot.DynamicCredentialsEntry + 4, // 234: openshell.v1.ProviderEnvironmentSnapshot.readiness_reason:type_name -> openshell.v1.ProviderReadinessReason + 309, // 235: openshell.v1.ProviderEnvironmentSnapshot.files:type_name -> openshell.v1.ProviderEnvironmentSnapshot.FilesEntry + 318, // 236: openshell.v1.ExchangeProviderSubjectTokenResponse.expires_after:type_name -> google.protobuf.Duration + 319, // 237: openshell.v1.UpdateConfigRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 316, // 238: openshell.v1.UpdateConfigRequest.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 324, // 239: openshell.v1.UpdateConfigRequest.setting_value:type_name -> openshell.sandbox.v1.SettingValue + 173, // 240: openshell.v1.UpdateConfigRequest.merge_operations:type_name -> openshell.v1.PolicyMergeOperation + 310, // 241: openshell.v1.UpdateConfigRequest.annotations:type_name -> openshell.v1.UpdateConfigRequest.AnnotationsEntry + 174, // 242: openshell.v1.PolicyMergeOperation.add_rule:type_name -> openshell.v1.AddNetworkRule + 175, // 243: openshell.v1.PolicyMergeOperation.remove_endpoint:type_name -> openshell.v1.RemoveNetworkEndpoint + 176, // 244: openshell.v1.PolicyMergeOperation.remove_rule:type_name -> openshell.v1.RemoveNetworkRule + 178, // 245: openshell.v1.PolicyMergeOperation.add_deny_rules:type_name -> openshell.v1.AddDenyRules + 179, // 246: openshell.v1.PolicyMergeOperation.add_allow_rules:type_name -> openshell.v1.AddAllowRules + 180, // 247: openshell.v1.PolicyMergeOperation.remove_binary:type_name -> openshell.v1.RemoveNetworkBinary + 325, // 248: openshell.v1.AddNetworkRule.rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 323, // 249: openshell.v1.L7RuleTarget.binaries:type_name -> openshell.sandbox.v1.NetworkBinary + 326, // 250: openshell.v1.AddDenyRules.deny_rules:type_name -> openshell.sandbox.v1.L7DenyRule + 177, // 251: openshell.v1.AddDenyRules.target:type_name -> openshell.v1.L7RuleTarget + 327, // 252: openshell.v1.AddAllowRules.rules:type_name -> openshell.sandbox.v1.L7Rule + 177, // 253: openshell.v1.AddAllowRules.target:type_name -> openshell.v1.L7RuleTarget + 311, // 254: openshell.v1.UpdateConfigResponse.annotations:type_name -> openshell.v1.UpdateConfigResponse.AnnotationsEntry + 319, // 255: openshell.v1.GetSandboxPolicyStatusRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 191, // 256: openshell.v1.GetSandboxPolicyStatusResponse.revision:type_name -> openshell.v1.SandboxPolicyRevision + 319, // 257: openshell.v1.ListSandboxPoliciesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 191, // 258: openshell.v1.ListSandboxPoliciesResponse.revisions:type_name -> openshell.v1.SandboxPolicyRevision + 13, // 259: openshell.v1.ReportPolicyStatusRequest.status:type_name -> openshell.v1.PolicyStatus + 12, // 260: openshell.v1.SandboxConfigurationAdmission.state:type_name -> openshell.v1.ConfigurationAdmissionState + 188, // 261: openshell.v1.ReportSandboxConfigurationRequest.admission:type_name -> openshell.v1.SandboxConfigurationAdmission + 13, // 262: openshell.v1.SandboxPolicyRevision.status:type_name -> openshell.v1.PolicyStatus + 314, // 263: openshell.v1.SandboxPolicyRevision.created_time:type_name -> google.protobuf.Timestamp + 314, // 264: openshell.v1.SandboxPolicyRevision.loaded_time:type_name -> google.protobuf.Timestamp + 316, // 265: openshell.v1.SandboxPolicyRevision.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 312, // 266: openshell.v1.SandboxPolicyRevision.provenance:type_name -> openshell.v1.SandboxPolicyRevision.ProvenanceEntry + 319, // 267: openshell.v1.GetSandboxLogsRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 314, // 268: openshell.v1.GetSandboxLogsRequest.since_time:type_name -> google.protobuf.Timestamp + 118, // 269: openshell.v1.PushSandboxLogsRequest.logs:type_name -> openshell.v1.SandboxLogLine + 118, // 270: openshell.v1.GetSandboxLogsResponse.logs:type_name -> openshell.v1.SandboxLogLine + 198, // 271: openshell.v1.SupervisorMessage.hello:type_name -> openshell.v1.SupervisorHello + 206, // 272: openshell.v1.SupervisorMessage.heartbeat:type_name -> openshell.v1.SupervisorHeartbeat + 223, // 273: openshell.v1.SupervisorMessage.relay_open_result:type_name -> openshell.v1.RelayOpenResult + 224, // 274: openshell.v1.SupervisorMessage.relay_close:type_name -> openshell.v1.RelayClose + 202, // 275: openshell.v1.SupervisorMessage.config_update_result:type_name -> openshell.v1.ConfigUpdateResult + 277, // 276: openshell.v1.SupervisorMessage.service_advertisement:type_name -> openshell.v1.SupervisorServiceAdvertisement + 199, // 277: openshell.v1.GatewayMessage.session_accepted:type_name -> openshell.v1.SessionAccepted + 204, // 278: openshell.v1.GatewayMessage.session_rejected:type_name -> openshell.v1.SessionRejected + 207, // 279: openshell.v1.GatewayMessage.heartbeat:type_name -> openshell.v1.GatewayHeartbeat + 212, // 280: openshell.v1.GatewayMessage.relay_open:type_name -> openshell.v1.RelayOpen + 224, // 281: openshell.v1.GatewayMessage.relay_close:type_name -> openshell.v1.RelayClose + 205, // 282: openshell.v1.GatewayMessage.session_redirect:type_name -> openshell.v1.SessionRedirect + 200, // 283: openshell.v1.GatewayMessage.config_update:type_name -> openshell.v1.ConfigUpdate + 276, // 284: openshell.v1.GatewayMessage.service_route_lease:type_name -> openshell.v1.ServiceRouteLease + 200, // 285: openshell.v1.SessionAccepted.bootstrap:type_name -> openshell.v1.ConfigUpdate + 318, // 286: openshell.v1.SessionAccepted.heartbeat_interval:type_name -> google.protobuf.Duration + 328, // 287: openshell.v1.ConfigUpdate.sandbox_config:type_name -> openshell.sandbox.v1.SandboxConfigSnapshot + 169, // 288: openshell.v1.ConfigUpdate.provider_environment:type_name -> openshell.v1.ProviderEnvironmentSnapshot + 6, // 289: openshell.v1.ConfigUpdateResult.outcome:type_name -> openshell.v1.ConfigApplyOutcome + 201, // 290: openshell.v1.ConfigUpdateResult.failure:type_name -> openshell.v1.ConfigApplyFailure + 188, // 291: openshell.v1.ConfigUpdateResult.admission:type_name -> openshell.v1.SandboxConfigurationAdmission + 203, // 292: openshell.v1.ConfigUpdateResult.policy_load:type_name -> openshell.v1.PolicyLoadResult + 213, // 293: openshell.v1.RelayOpen.ssh:type_name -> openshell.v1.SshRelayTarget + 214, // 294: openshell.v1.RelayOpen.tcp:type_name -> openshell.v1.TcpRelayTarget + 215, // 295: openshell.v1.RelayFrame.init:type_name -> openshell.v1.RelayInit + 212, // 296: openshell.v1.PeerRelayInit.relay_open:type_name -> openshell.v1.RelayOpen + 217, // 297: openshell.v1.PeerRelayFrame.init:type_name -> openshell.v1.PeerRelayInit + 219, // 298: openshell.v1.PeerNotifyConfigUpdateRequest.sandbox:type_name -> openshell.v1.PeerConfigSandboxTarget + 220, // 299: openshell.v1.PeerNotifyConfigUpdateRequest.provider:type_name -> openshell.v1.PeerConfigProviderTarget + 314, // 300: openshell.v1.DenialSummary.first_seen_time:type_name -> google.protobuf.Timestamp + 314, // 301: openshell.v1.DenialSummary.last_seen_time:type_name -> google.protobuf.Timestamp + 225, // 302: openshell.v1.DenialSummary.l7_request_samples:type_name -> openshell.v1.L7RequestSample + 227, // 303: openshell.v1.NetworkActivitySummary.denials_by_group:type_name -> openshell.v1.DenialGroupCount + 325, // 304: openshell.v1.PolicyChunk.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 314, // 305: openshell.v1.PolicyChunk.created_time:type_name -> google.protobuf.Timestamp + 314, // 306: openshell.v1.PolicyChunk.decided_time:type_name -> google.protobuf.Timestamp + 314, // 307: openshell.v1.PolicyChunk.first_seen_time:type_name -> google.protobuf.Timestamp + 314, // 308: openshell.v1.PolicyChunk.last_seen_time:type_name -> google.protobuf.Timestamp + 316, // 309: openshell.v1.PolicyChunk.current_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 316, // 310: openshell.v1.PolicyChunk.candidate_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 319, // 311: openshell.v1.SubmitPolicyAnalysisRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 226, // 312: openshell.v1.SubmitPolicyAnalysisRequest.summaries:type_name -> openshell.v1.DenialSummary + 229, // 313: openshell.v1.SubmitPolicyAnalysisRequest.proposed_chunks:type_name -> openshell.v1.PolicyChunk + 228, // 314: openshell.v1.SubmitPolicyAnalysisRequest.network_activity_summaries:type_name -> openshell.v1.NetworkActivitySummary + 319, // 315: openshell.v1.GetDraftPolicyRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 229, // 316: openshell.v1.GetDraftPolicyResponse.chunks:type_name -> openshell.v1.PolicyChunk + 314, // 317: openshell.v1.GetDraftPolicyResponse.last_analyzed_time:type_name -> google.protobuf.Timestamp + 319, // 318: openshell.v1.ApproveDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 319: openshell.v1.RejectDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 320: openshell.v1.ApproveAllDraftChunksRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 239, // 321: openshell.v1.ApproveAllDraftChunksRequest.approvals:type_name -> openshell.v1.DraftChunkApproval + 319, // 322: openshell.v1.EditDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 325, // 323: openshell.v1.EditDraftChunkRequest.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 319, // 324: openshell.v1.UndoDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 325: openshell.v1.ClearDraftChunksRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 319, // 326: openshell.v1.GetDraftHistoryRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 314, // 327: openshell.v1.DraftHistoryEntry.event_time:type_name -> google.protobuf.Timestamp + 249, // 328: openshell.v1.GetDraftHistoryResponse.entries:type_name -> openshell.v1.DraftHistoryEntry + 313, // 329: openshell.v1.CreateWorkspaceRequest.labels:type_name -> openshell.v1.CreateWorkspaceRequest.LabelsEntry + 329, // 330: openshell.v1.CreateWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace + 329, // 331: openshell.v1.GetWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace + 329, // 332: openshell.v1.ListWorkspacesResponse.workspaces:type_name -> openshell.datamodel.v1.Workspace + 18, // 333: openshell.v1.DeleteWorkspaceResponse.outcome:type_name -> openshell.v1.DeletionOutcome + 315, // 334: openshell.v1.WorkspaceMember.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 15, // 335: openshell.v1.WorkspaceMember.role:type_name -> openshell.v1.WorkspaceRole + 319, // 336: openshell.v1.AddWorkspaceMemberRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 15, // 337: openshell.v1.AddWorkspaceMemberRequest.role:type_name -> openshell.v1.WorkspaceRole + 259, // 338: openshell.v1.AddWorkspaceMemberResponse.member:type_name -> openshell.v1.WorkspaceMember + 319, // 339: openshell.v1.RemoveWorkspaceMemberRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 18, // 340: openshell.v1.RemoveWorkspaceMemberResponse.outcome:type_name -> openshell.v1.DeletionOutcome + 319, // 341: openshell.v1.ListWorkspaceMembersRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 259, // 342: openshell.v1.ListWorkspaceMembersResponse.members:type_name -> openshell.v1.WorkspaceMember + 314, // 343: openshell.v1.ExtensionServiceCredential.expiration_time:type_name -> google.protobuf.Timestamp + 19, // 344: openshell.v1.EndpointObservation.result:type_name -> openshell.v1.EndpointResult + 275, // 345: openshell.v1.EndpointObservation.failure:type_name -> openshell.v1.EndpointFailure + 267, // 346: openshell.v1.ReportEndpointStatusRequest.observations:type_name -> openshell.v1.EndpointObservation + 269, // 347: openshell.v1.ReportEndpointStatusRequest.token_grant_status:type_name -> openshell.v1.TokenGrantStatus + 270, // 348: openshell.v1.TokenGrantStatus.last_failure:type_name -> openshell.v1.TokenGrantFailureObservation + 275, // 349: openshell.v1.TokenGrantFailureObservation.failure:type_name -> openshell.v1.EndpointFailure + 19, // 350: openshell.v1.EndpointStatus.last_result:type_name -> openshell.v1.EndpointResult + 314, // 351: openshell.v1.EndpointStatus.last_reported_time:type_name -> google.protobuf.Timestamp + 275, // 352: openshell.v1.EndpointStatus.last_failure:type_name -> openshell.v1.EndpointFailure + 314, // 353: openshell.v1.SandboxProvisioning.configuration_change_time:type_name -> google.protobuf.Timestamp + 314, // 354: openshell.v1.SandboxProvisioning.first_rejection_time:type_name -> google.protobuf.Timestamp + 314, // 355: openshell.v1.SandboxProvisioning.deadline:type_name -> google.protobuf.Timestamp + 314, // 356: openshell.v1.SandboxProvisioning.timeout_time:type_name -> google.protobuf.Timestamp + 314, // 357: openshell.v1.SandboxProvisioning.cleanup_completed_time:type_name -> google.protobuf.Timestamp + 314, // 358: openshell.v1.SandboxProvisioning.cleanup_retry_time:type_name -> google.protobuf.Timestamp + 314, // 359: openshell.v1.SandboxProvisioning.attachment_change_time:type_name -> google.protobuf.Timestamp + 314, // 360: openshell.v1.SandboxProvisioning.preparation_deadline:type_name -> google.protobuf.Timestamp + 314, // 361: openshell.v1.SandboxProvisioning.admission_start_time:type_name -> google.protobuf.Timestamp + 20, // 362: openshell.v1.SandboxServiceExposure.authorization_mode:type_name -> openshell.v1.ServiceAuthorizationMode + 21, // 363: openshell.v1.EndpointFailure.domain:type_name -> openshell.v1.EndpointFailureDomain + 16, // 364: openshell.v1.EndpointFailure.recovery_action:type_name -> openshell.v1.ProviderCredentialRefreshRecoveryAction + 318, // 365: openshell.v1.ServiceRouteLease.duration:type_name -> google.protobuf.Duration + 314, // 366: openshell.v1.ServiceRouteLease.expires_at:type_name -> google.protobuf.Timestamp + 330, // 367: openshell.v1.SupervisorServiceAdvertisement.routes:type_name -> openshell.sandbox.v1.SandboxServiceRoute + 319, // 368: openshell.v1.GetServiceRouteSnapshotRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 282, // 369: openshell.v1.GetServiceRouteSnapshotResponse.snapshot:type_name -> openshell.v1.ServiceRouteSnapshot + 319, // 370: openshell.v1.WatchServiceRoutesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector + 282, // 371: openshell.v1.WatchServiceRoutesResponse.snapshot:type_name -> openshell.v1.ServiceRouteSnapshot + 284, // 372: openshell.v1.ServiceRouteSnapshot.routes:type_name -> openshell.v1.DiscoveredServiceRoute + 283, // 373: openshell.v1.DiscoveredServiceRoute.endpoint:type_name -> openshell.v1.ServiceRouteEndpoint + 314, // 374: openshell.v1.DiscoveredServiceRoute.expires_at:type_name -> google.protobuf.Timestamp + 123, // 375: openshell.v1.GetProviderCredentialsResponse.CredentialsEntry.value:type_name -> openshell.v1.ProviderCredentialValue + 314, // 376: openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry.value:type_name -> google.protobuf.Timestamp + 314, // 377: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry.value:type_name -> google.protobuf.Timestamp + 137, // 378: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential + 166, // 379: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry.value:type_name -> openshell.v1.StaticCredentialBinding + 137, // 380: openshell.v1.ProviderEnvironmentSnapshot.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential + 26, // 381: openshell.v1.OpenShell.Health:input_type -> openshell.v1.HealthRequest + 28, // 382: openshell.v1.OpenShell.GetCurrentUser:input_type -> openshell.v1.GetCurrentUserRequest + 30, // 383: openshell.v1.OpenShell.GetGatewayInfo:input_type -> openshell.v1.GetGatewayInfoRequest + 54, // 384: openshell.v1.OpenShell.CreateSandbox:input_type -> openshell.v1.CreateSandboxRequest + 65, // 385: openshell.v1.OpenShell.BeginRootfsTarStaging:input_type -> openshell.v1.BeginRootfsTarStagingRequest + 67, // 386: openshell.v1.OpenShell.GetSandbox:input_type -> openshell.v1.GetSandboxRequest + 68, // 387: openshell.v1.OpenShell.ListSandboxes:input_type -> openshell.v1.ListSandboxesRequest + 58, // 388: openshell.v1.OpenShell.CreateSandboxTemplate:input_type -> openshell.v1.CreateSandboxTemplateRequest + 59, // 389: openshell.v1.OpenShell.GetSandboxTemplate:input_type -> openshell.v1.GetSandboxTemplateRequest + 60, // 390: openshell.v1.OpenShell.ListSandboxTemplates:input_type -> openshell.v1.ListSandboxTemplatesRequest + 61, // 391: openshell.v1.OpenShell.DeleteSandboxTemplate:input_type -> openshell.v1.DeleteSandboxTemplateRequest + 69, // 392: openshell.v1.OpenShell.ListSandboxProviders:input_type -> openshell.v1.ListSandboxProvidersRequest + 70, // 393: openshell.v1.OpenShell.AttachSandboxProvider:input_type -> openshell.v1.AttachSandboxProviderRequest + 71, // 394: openshell.v1.OpenShell.DetachSandboxProvider:input_type -> openshell.v1.DetachSandboxProviderRequest + 89, // 395: openshell.v1.OpenShell.GetSandboxProviderStatus:input_type -> openshell.v1.GetSandboxProviderStatusRequest + 72, // 396: openshell.v1.OpenShell.DeleteSandbox:input_type -> openshell.v1.DeleteSandboxRequest + 73, // 397: openshell.v1.OpenShell.StopSandbox:input_type -> openshell.v1.StopSandboxRequest + 74, // 398: openshell.v1.OpenShell.StartSandbox:input_type -> openshell.v1.StartSandboxRequest + 75, // 399: openshell.v1.OpenShell.SuspendSandbox:input_type -> openshell.v1.SuspendSandboxRequest + 76, // 400: openshell.v1.OpenShell.ResumeSandbox:input_type -> openshell.v1.ResumeSandboxRequest + 94, // 401: openshell.v1.OpenShell.CreateSshSession:input_type -> openshell.v1.CreateSshSessionRequest + 96, // 402: openshell.v1.OpenShell.ExposeService:input_type -> openshell.v1.ExposeServiceRequest + 97, // 403: openshell.v1.OpenShell.GetService:input_type -> openshell.v1.GetServiceRequest + 98, // 404: openshell.v1.OpenShell.ListServices:input_type -> openshell.v1.ListServicesRequest + 278, // 405: openshell.v1.OpenShell.GetServiceRouteSnapshot:input_type -> openshell.v1.GetServiceRouteSnapshotRequest + 280, // 406: openshell.v1.OpenShell.WatchServiceRoutes:input_type -> openshell.v1.WatchServiceRoutesRequest + 100, // 407: openshell.v1.OpenShell.DeleteService:input_type -> openshell.v1.DeleteServiceRequest + 104, // 408: openshell.v1.OpenShell.RevokeSshSession:input_type -> openshell.v1.RevokeSshSessionRequest + 106, // 409: openshell.v1.OpenShell.ExecSandbox:input_type -> openshell.v1.ExecSandboxRequest + 112, // 410: openshell.v1.OpenShell.ForwardTcp:input_type -> openshell.v1.TcpForwardFrame + 113, // 411: openshell.v1.OpenShell.ExecSandboxInteractive:input_type -> openshell.v1.ExecSandboxInput + 120, // 412: openshell.v1.OpenShell.CreateProvider:input_type -> openshell.v1.CreateProviderRequest + 121, // 413: openshell.v1.OpenShell.GetProvider:input_type -> openshell.v1.GetProviderRequest + 122, // 414: openshell.v1.OpenShell.GetProviderCredentials:input_type -> openshell.v1.GetProviderCredentialsRequest + 125, // 415: openshell.v1.OpenShell.ListProviders:input_type -> openshell.v1.ListProvidersRequest + 130, // 416: openshell.v1.OpenShell.ListProviderProfiles:input_type -> openshell.v1.ListProviderProfilesRequest + 131, // 417: openshell.v1.OpenShell.GetProviderProfile:input_type -> openshell.v1.GetProviderProfileRequest + 155, // 418: openshell.v1.OpenShell.ImportProviderProfiles:input_type -> openshell.v1.ImportProviderProfilesRequest + 157, // 419: openshell.v1.OpenShell.UpdateProviderProfiles:input_type -> openshell.v1.UpdateProviderProfilesRequest + 159, // 420: openshell.v1.OpenShell.LintProviderProfiles:input_type -> openshell.v1.LintProviderProfilesRequest + 126, // 421: openshell.v1.OpenShell.UpdateProvider:input_type -> openshell.v1.UpdateProviderRequest + 143, // 422: openshell.v1.OpenShell.GetProviderRefreshStatus:input_type -> openshell.v1.GetProviderRefreshStatusRequest + 145, // 423: openshell.v1.OpenShell.ConfigureProviderRefresh:input_type -> openshell.v1.ConfigureProviderRefreshRequest + 147, // 424: openshell.v1.OpenShell.RotateProviderCredential:input_type -> openshell.v1.RotateProviderCredentialRequest + 149, // 425: openshell.v1.OpenShell.DeleteProviderRefresh:input_type -> openshell.v1.DeleteProviderRefreshRequest + 127, // 426: openshell.v1.OpenShell.DeleteProvider:input_type -> openshell.v1.DeleteProviderRequest + 162, // 427: openshell.v1.OpenShell.DeleteProviderProfile:input_type -> openshell.v1.DeleteProviderProfileRequest + 331, // 428: openshell.v1.OpenShell.GetSandboxConfig:input_type -> openshell.sandbox.v1.GetSandboxConfigRequest + 332, // 429: openshell.v1.OpenShell.GetGatewayConfig:input_type -> openshell.sandbox.v1.GetGatewayConfigRequest + 172, // 430: openshell.v1.OpenShell.UpdateConfig:input_type -> openshell.v1.UpdateConfigRequest + 182, // 431: openshell.v1.OpenShell.GetSandboxPolicyStatus:input_type -> openshell.v1.GetSandboxPolicyStatusRequest + 184, // 432: openshell.v1.OpenShell.ListSandboxPolicies:input_type -> openshell.v1.ListSandboxPoliciesRequest + 186, // 433: openshell.v1.OpenShell.ReportPolicyStatus:input_type -> openshell.v1.ReportPolicyStatusRequest + 268, // 434: openshell.v1.OpenShell.ReportEndpointStatus:input_type -> openshell.v1.ReportEndpointStatusRequest + 91, // 435: openshell.v1.OpenShell.ReportProviderReadiness:input_type -> openshell.v1.ReportProviderReadinessRequest + 189, // 436: openshell.v1.OpenShell.ReportSandboxConfiguration:input_type -> openshell.v1.ReportSandboxConfigurationRequest + 164, // 437: openshell.v1.OpenShell.GetSandboxProviderEnvironment:input_type -> openshell.v1.GetSandboxProviderEnvironmentRequest + 170, // 438: openshell.v1.OpenShell.ExchangeProviderSubjectToken:input_type -> openshell.v1.ExchangeProviderSubjectTokenRequest + 192, // 439: openshell.v1.OpenShell.GetSandboxLogs:input_type -> openshell.v1.GetSandboxLogsRequest + 193, // 440: openshell.v1.OpenShell.PushSandboxLogs:input_type -> openshell.v1.PushSandboxLogsRequest + 196, // 441: openshell.v1.OpenShell.ConnectSupervisor:input_type -> openshell.v1.SupervisorMessage + 208, // 442: openshell.v1.OpenShell.ReportMainProcessExit:input_type -> openshell.v1.ReportMainProcessExitRequest + 210, // 443: openshell.v1.OpenShell.FinalizeMainProcessExit:input_type -> openshell.v1.FinalizeMainProcessExitRequest + 216, // 444: openshell.v1.OpenShell.RelayStream:input_type -> openshell.v1.RelayFrame + 218, // 445: openshell.v1.OpenShell.PeerRelay:input_type -> openshell.v1.PeerRelayFrame + 91, // 446: openshell.v1.OpenShell.PeerReportProviderReadiness:input_type -> openshell.v1.ReportProviderReadinessRequest + 268, // 447: openshell.v1.OpenShell.PeerReportEndpointStatus:input_type -> openshell.v1.ReportEndpointStatusRequest + 89, // 448: openshell.v1.OpenShell.PeerGetSandboxProviderStatus:input_type -> openshell.v1.GetSandboxProviderStatusRequest + 221, // 449: openshell.v1.OpenShell.PeerNotifyConfigUpdate:input_type -> openshell.v1.PeerNotifyConfigUpdateRequest + 116, // 450: openshell.v1.OpenShell.WatchSandbox:input_type -> openshell.v1.WatchSandboxRequest + 231, // 451: openshell.v1.OpenShell.SubmitPolicyAnalysis:input_type -> openshell.v1.SubmitPolicyAnalysisRequest + 233, // 452: openshell.v1.OpenShell.GetDraftPolicy:input_type -> openshell.v1.GetDraftPolicyRequest + 235, // 453: openshell.v1.OpenShell.ApproveDraftChunk:input_type -> openshell.v1.ApproveDraftChunkRequest + 237, // 454: openshell.v1.OpenShell.RejectDraftChunk:input_type -> openshell.v1.RejectDraftChunkRequest + 240, // 455: openshell.v1.OpenShell.ApproveAllDraftChunks:input_type -> openshell.v1.ApproveAllDraftChunksRequest + 242, // 456: openshell.v1.OpenShell.EditDraftChunk:input_type -> openshell.v1.EditDraftChunkRequest + 244, // 457: openshell.v1.OpenShell.UndoDraftChunk:input_type -> openshell.v1.UndoDraftChunkRequest + 246, // 458: openshell.v1.OpenShell.ClearDraftChunks:input_type -> openshell.v1.ClearDraftChunksRequest + 248, // 459: openshell.v1.OpenShell.GetDraftHistory:input_type -> openshell.v1.GetDraftHistoryRequest + 22, // 460: openshell.v1.OpenShell.IssueSandboxToken:input_type -> openshell.v1.IssueSandboxTokenRequest + 24, // 461: openshell.v1.OpenShell.RefreshSandboxToken:input_type -> openshell.v1.RefreshSandboxTokenRequest + 251, // 462: openshell.v1.OpenShell.CreateWorkspace:input_type -> openshell.v1.CreateWorkspaceRequest + 253, // 463: openshell.v1.OpenShell.GetWorkspace:input_type -> openshell.v1.GetWorkspaceRequest + 255, // 464: openshell.v1.OpenShell.ListWorkspaces:input_type -> openshell.v1.ListWorkspacesRequest + 257, // 465: openshell.v1.OpenShell.DeleteWorkspace:input_type -> openshell.v1.DeleteWorkspaceRequest + 260, // 466: openshell.v1.OpenShell.AddWorkspaceMember:input_type -> openshell.v1.AddWorkspaceMemberRequest + 262, // 467: openshell.v1.OpenShell.RemoveWorkspaceMember:input_type -> openshell.v1.RemoveWorkspaceMemberRequest + 264, // 468: openshell.v1.OpenShell.ListWorkspaceMembers:input_type -> openshell.v1.ListWorkspaceMembersRequest + 55, // 469: openshell.v1.SandboxUpdates.UpdateSandbox:input_type -> openshell.v1.UpdateSandboxRequest + 27, // 470: openshell.v1.OpenShell.Health:output_type -> openshell.v1.HealthResponse + 29, // 471: openshell.v1.OpenShell.GetCurrentUser:output_type -> openshell.v1.GetCurrentUserResponse + 31, // 472: openshell.v1.OpenShell.GetGatewayInfo:output_type -> openshell.v1.GetGatewayInfoResponse + 77, // 473: openshell.v1.OpenShell.CreateSandbox:output_type -> openshell.v1.SandboxResponse + 66, // 474: openshell.v1.OpenShell.BeginRootfsTarStaging:output_type -> openshell.v1.BeginRootfsTarStagingResponse + 77, // 475: openshell.v1.OpenShell.GetSandbox:output_type -> openshell.v1.SandboxResponse + 78, // 476: openshell.v1.OpenShell.ListSandboxes:output_type -> openshell.v1.ListSandboxesResponse + 62, // 477: openshell.v1.OpenShell.CreateSandboxTemplate:output_type -> openshell.v1.SandboxTemplateResponse + 62, // 478: openshell.v1.OpenShell.GetSandboxTemplate:output_type -> openshell.v1.SandboxTemplateResponse + 63, // 479: openshell.v1.OpenShell.ListSandboxTemplates:output_type -> openshell.v1.ListSandboxTemplatesResponse + 64, // 480: openshell.v1.OpenShell.DeleteSandboxTemplate:output_type -> openshell.v1.DeleteSandboxTemplateResponse + 79, // 481: openshell.v1.OpenShell.ListSandboxProviders:output_type -> openshell.v1.ListSandboxProvidersResponse + 80, // 482: openshell.v1.OpenShell.AttachSandboxProvider:output_type -> openshell.v1.AttachSandboxProviderResponse + 81, // 483: openshell.v1.OpenShell.DetachSandboxProvider:output_type -> openshell.v1.DetachSandboxProviderResponse + 90, // 484: openshell.v1.OpenShell.GetSandboxProviderStatus:output_type -> openshell.v1.GetSandboxProviderStatusResponse + 93, // 485: openshell.v1.OpenShell.DeleteSandbox:output_type -> openshell.v1.DeleteSandboxResponse + 77, // 486: openshell.v1.OpenShell.StopSandbox:output_type -> openshell.v1.SandboxResponse + 77, // 487: openshell.v1.OpenShell.StartSandbox:output_type -> openshell.v1.SandboxResponse + 77, // 488: openshell.v1.OpenShell.SuspendSandbox:output_type -> openshell.v1.SandboxResponse + 77, // 489: openshell.v1.OpenShell.ResumeSandbox:output_type -> openshell.v1.SandboxResponse + 95, // 490: openshell.v1.OpenShell.CreateSshSession:output_type -> openshell.v1.CreateSshSessionResponse + 103, // 491: openshell.v1.OpenShell.ExposeService:output_type -> openshell.v1.ServiceEndpointResponse + 103, // 492: openshell.v1.OpenShell.GetService:output_type -> openshell.v1.ServiceEndpointResponse + 99, // 493: openshell.v1.OpenShell.ListServices:output_type -> openshell.v1.ListServicesResponse + 279, // 494: openshell.v1.OpenShell.GetServiceRouteSnapshot:output_type -> openshell.v1.GetServiceRouteSnapshotResponse + 281, // 495: openshell.v1.OpenShell.WatchServiceRoutes:output_type -> openshell.v1.WatchServiceRoutesResponse + 101, // 496: openshell.v1.OpenShell.DeleteService:output_type -> openshell.v1.DeleteServiceResponse + 105, // 497: openshell.v1.OpenShell.RevokeSshSession:output_type -> openshell.v1.RevokeSshSessionResponse + 110, // 498: openshell.v1.OpenShell.ExecSandbox:output_type -> openshell.v1.ExecSandboxEvent + 112, // 499: openshell.v1.OpenShell.ForwardTcp:output_type -> openshell.v1.TcpForwardFrame + 110, // 500: openshell.v1.OpenShell.ExecSandboxInteractive:output_type -> openshell.v1.ExecSandboxEvent + 128, // 501: openshell.v1.OpenShell.CreateProvider:output_type -> openshell.v1.ProviderResponse + 128, // 502: openshell.v1.OpenShell.GetProvider:output_type -> openshell.v1.ProviderResponse + 124, // 503: openshell.v1.OpenShell.GetProviderCredentials:output_type -> openshell.v1.GetProviderCredentialsResponse + 129, // 504: openshell.v1.OpenShell.ListProviders:output_type -> openshell.v1.ListProvidersResponse + 154, // 505: openshell.v1.OpenShell.ListProviderProfiles:output_type -> openshell.v1.ListProviderProfilesResponse + 153, // 506: openshell.v1.OpenShell.GetProviderProfile:output_type -> openshell.v1.ProviderProfileResponse + 156, // 507: openshell.v1.OpenShell.ImportProviderProfiles:output_type -> openshell.v1.ImportProviderProfilesResponse + 158, // 508: openshell.v1.OpenShell.UpdateProviderProfiles:output_type -> openshell.v1.UpdateProviderProfilesResponse + 160, // 509: openshell.v1.OpenShell.LintProviderProfiles:output_type -> openshell.v1.LintProviderProfilesResponse + 128, // 510: openshell.v1.OpenShell.UpdateProvider:output_type -> openshell.v1.ProviderResponse + 144, // 511: openshell.v1.OpenShell.GetProviderRefreshStatus:output_type -> openshell.v1.GetProviderRefreshStatusResponse + 146, // 512: openshell.v1.OpenShell.ConfigureProviderRefresh:output_type -> openshell.v1.ConfigureProviderRefreshResponse + 148, // 513: openshell.v1.OpenShell.RotateProviderCredential:output_type -> openshell.v1.RotateProviderCredentialResponse + 150, // 514: openshell.v1.OpenShell.DeleteProviderRefresh:output_type -> openshell.v1.DeleteProviderRefreshResponse + 161, // 515: openshell.v1.OpenShell.DeleteProvider:output_type -> openshell.v1.DeleteProviderResponse + 163, // 516: openshell.v1.OpenShell.DeleteProviderProfile:output_type -> openshell.v1.DeleteProviderProfileResponse + 333, // 517: openshell.v1.OpenShell.GetSandboxConfig:output_type -> openshell.sandbox.v1.GetSandboxConfigResponse + 334, // 518: openshell.v1.OpenShell.GetGatewayConfig:output_type -> openshell.sandbox.v1.GetGatewayConfigResponse + 181, // 519: openshell.v1.OpenShell.UpdateConfig:output_type -> openshell.v1.UpdateConfigResponse + 183, // 520: openshell.v1.OpenShell.GetSandboxPolicyStatus:output_type -> openshell.v1.GetSandboxPolicyStatusResponse + 185, // 521: openshell.v1.OpenShell.ListSandboxPolicies:output_type -> openshell.v1.ListSandboxPoliciesResponse + 187, // 522: openshell.v1.OpenShell.ReportPolicyStatus:output_type -> openshell.v1.ReportPolicyStatusResponse + 271, // 523: openshell.v1.OpenShell.ReportEndpointStatus:output_type -> openshell.v1.ReportEndpointStatusResponse + 92, // 524: openshell.v1.OpenShell.ReportProviderReadiness:output_type -> openshell.v1.ReportProviderReadinessResponse + 190, // 525: openshell.v1.OpenShell.ReportSandboxConfiguration:output_type -> openshell.v1.ReportSandboxConfigurationResponse + 167, // 526: openshell.v1.OpenShell.GetSandboxProviderEnvironment:output_type -> openshell.v1.GetSandboxProviderEnvironmentResponse + 171, // 527: openshell.v1.OpenShell.ExchangeProviderSubjectToken:output_type -> openshell.v1.ExchangeProviderSubjectTokenResponse + 195, // 528: openshell.v1.OpenShell.GetSandboxLogs:output_type -> openshell.v1.GetSandboxLogsResponse + 194, // 529: openshell.v1.OpenShell.PushSandboxLogs:output_type -> openshell.v1.PushSandboxLogsResponse + 197, // 530: openshell.v1.OpenShell.ConnectSupervisor:output_type -> openshell.v1.GatewayMessage + 209, // 531: openshell.v1.OpenShell.ReportMainProcessExit:output_type -> openshell.v1.ReportMainProcessExitResponse + 211, // 532: openshell.v1.OpenShell.FinalizeMainProcessExit:output_type -> openshell.v1.FinalizeMainProcessExitResponse + 216, // 533: openshell.v1.OpenShell.RelayStream:output_type -> openshell.v1.RelayFrame + 218, // 534: openshell.v1.OpenShell.PeerRelay:output_type -> openshell.v1.PeerRelayFrame + 92, // 535: openshell.v1.OpenShell.PeerReportProviderReadiness:output_type -> openshell.v1.ReportProviderReadinessResponse + 271, // 536: openshell.v1.OpenShell.PeerReportEndpointStatus:output_type -> openshell.v1.ReportEndpointStatusResponse + 90, // 537: openshell.v1.OpenShell.PeerGetSandboxProviderStatus:output_type -> openshell.v1.GetSandboxProviderStatusResponse + 222, // 538: openshell.v1.OpenShell.PeerNotifyConfigUpdate:output_type -> openshell.v1.PeerNotifyConfigUpdateResponse + 117, // 539: openshell.v1.OpenShell.WatchSandbox:output_type -> openshell.v1.SandboxStreamEvent + 232, // 540: openshell.v1.OpenShell.SubmitPolicyAnalysis:output_type -> openshell.v1.SubmitPolicyAnalysisResponse + 234, // 541: openshell.v1.OpenShell.GetDraftPolicy:output_type -> openshell.v1.GetDraftPolicyResponse + 236, // 542: openshell.v1.OpenShell.ApproveDraftChunk:output_type -> openshell.v1.ApproveDraftChunkResponse + 238, // 543: openshell.v1.OpenShell.RejectDraftChunk:output_type -> openshell.v1.RejectDraftChunkResponse + 241, // 544: openshell.v1.OpenShell.ApproveAllDraftChunks:output_type -> openshell.v1.ApproveAllDraftChunksResponse + 243, // 545: openshell.v1.OpenShell.EditDraftChunk:output_type -> openshell.v1.EditDraftChunkResponse + 245, // 546: openshell.v1.OpenShell.UndoDraftChunk:output_type -> openshell.v1.UndoDraftChunkResponse + 247, // 547: openshell.v1.OpenShell.ClearDraftChunks:output_type -> openshell.v1.ClearDraftChunksResponse + 250, // 548: openshell.v1.OpenShell.GetDraftHistory:output_type -> openshell.v1.GetDraftHistoryResponse + 23, // 549: openshell.v1.OpenShell.IssueSandboxToken:output_type -> openshell.v1.IssueSandboxTokenResponse + 25, // 550: openshell.v1.OpenShell.RefreshSandboxToken:output_type -> openshell.v1.RefreshSandboxTokenResponse + 252, // 551: openshell.v1.OpenShell.CreateWorkspace:output_type -> openshell.v1.CreateWorkspaceResponse + 254, // 552: openshell.v1.OpenShell.GetWorkspace:output_type -> openshell.v1.GetWorkspaceResponse + 256, // 553: openshell.v1.OpenShell.ListWorkspaces:output_type -> openshell.v1.ListWorkspacesResponse + 258, // 554: openshell.v1.OpenShell.DeleteWorkspace:output_type -> openshell.v1.DeleteWorkspaceResponse + 261, // 555: openshell.v1.OpenShell.AddWorkspaceMember:output_type -> openshell.v1.AddWorkspaceMemberResponse + 263, // 556: openshell.v1.OpenShell.RemoveWorkspaceMember:output_type -> openshell.v1.RemoveWorkspaceMemberResponse + 265, // 557: openshell.v1.OpenShell.ListWorkspaceMembers:output_type -> openshell.v1.ListWorkspaceMembersResponse + 77, // 558: openshell.v1.SandboxUpdates.UpdateSandbox:output_type -> openshell.v1.SandboxResponse + 470, // [470:559] is the sub-list for method output_type + 381, // [381:470] is the sub-list for method input_type + 381, // [381:381] is the sub-list for extension type_name + 381, // [381:381] is the sub-list for extension extendee + 0, // [0:381] is the sub-list for field type_name } func init() { file_openshell_proto_init() } @@ -23184,7 +23237,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: 22, - NumMessages: 290, + NumMessages: 292, NumExtensions: 0, NumServices: 2, }, diff --git a/sdk/go/proto/sandboxv1/sandbox.pb.go b/sdk/go/proto/sandboxv1/sandbox.pb.go index 045e15fac5..d5485bc88d 100644 --- a/sdk/go/proto/sandboxv1/sandbox.pb.go +++ b/sdk/go/proto/sandboxv1/sandbox.pb.go @@ -2497,8 +2497,12 @@ type SandboxServiceRoute struct { TargetPort uint32 `protobuf:"varint,3,opt,name=target_port,json=targetPort,proto3" json:"target_port,omitempty"` AuthorizationMode int32 `protobuf:"varint,4,opt,name=authorization_mode,json=authorizationMode,proto3" json:"authorization_mode,omitempty"` ResourceVersion uint64 `protobuf:"varint,5,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Canonical application SNI/Host selected by the gateway's hostname format. + // Includes the application domain, without a scheme or port. An empty value + // from an older gateway retains readable workspace--sandbox[--service] routing. + ApplicationHostname string `protobuf:"bytes,6,opt,name=application_hostname,json=applicationHostname,proto3" json:"application_hostname,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SandboxServiceRoute) Reset() { @@ -2566,6 +2570,13 @@ func (x *SandboxServiceRoute) GetResourceVersion() uint64 { return 0 } +func (x *SandboxServiceRoute) GetApplicationHostname() string { + if x != nil { + return x.ApplicationHostname + } + return "" +} + // Connection details for one operator-registered supervisor middleware service. // V1 supports plaintext and server-authenticated TLS gRPC. type SupervisorMiddlewareService struct { @@ -2898,7 +2909,7 @@ const file_sandbox_proto_rawDesc = "" + "\x0eservice_routes\x18\x12 \x03(\v2).openshell.sandbox.v1.SandboxServiceRouteR\rserviceRoutes\x1ac\n" + "\rSettingsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12<\n" + - "\x05value\x18\x02 \x01(\v2&.openshell.sandbox.v1.EffectiveSettingR\x05value:\x028\x01\"\xc5\x01\n" + + "\x05value\x18\x02 \x01(\v2&.openshell.sandbox.v1.EffectiveSettingR\x05value:\x028\x01\"\xf8\x01\n" + "\x13SandboxServiceRoute\x12\x1f\n" + "\vendpoint_id\x18\x01 \x01(\tR\n" + "endpointId\x12\x12\n" + @@ -2906,7 +2917,8 @@ const file_sandbox_proto_rawDesc = "" + "\vtarget_port\x18\x03 \x01(\rR\n" + "targetPort\x12-\n" + "\x12authorization_mode\x18\x04 \x01(\x05R\x11authorizationMode\x12)\n" + - "\x10resource_version\x18\x05 \x01(\x04R\x0fresourceVersion\"\x81\x04\n" + + "\x10resource_version\x18\x05 \x01(\x04R\x0fresourceVersion\x121\n" + + "\x14application_hostname\x18\x06 \x01(\tR\x13applicationHostname\"\x81\x04\n" + "\x1bSupervisorMiddlewareService\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12#\n" + "\rgrpc_endpoint\x18\x02 \x01(\tR\fgrpcEndpoint\x12*\n" + diff --git a/sdk/typescript/src/client.test.ts b/sdk/typescript/src/client.test.ts index 86fdfa3972..a9167ae58b 100644 --- a/sdk/typescript/src/client.test.ts +++ b/sdk/typescript/src/client.test.ts @@ -708,6 +708,8 @@ describe('create', () => { created = req; return { ...readySandbox('sb', 'sb-id'), + applicationServiceUrls: { '': 'https://sb.example.test/', metrics: 'https://metrics.sb.example.test/' }, + gatewayServiceUrls: { '': 'https://sb.gateway.test:8080/' }, serviceUrls: { '': 'https://sb.example.test/', metrics: 'https://metrics.sb.example.test/', @@ -742,6 +744,8 @@ describe('create', () => { authorizationMode: ProtoServiceAuthorizationMode.BEARER_PASSTHROUGH, }, ]); + expect(result.applicationServiceUrls).toEqual(result.serviceUrls); + expect(result.gatewayServiceUrls['']).toBe('https://sb.gateway.test:8080/'); expect(result.serviceUrls).toEqual({ '': 'https://sb.example.test/', metrics: 'https://metrics.sb.example.test/', diff --git a/sdk/typescript/src/client.ts b/sdk/typescript/src/client.ts index f82242d4a7..9292bbd970 100644 --- a/sdk/typescript/src/client.ts +++ b/sdk/typescript/src/client.ts @@ -246,6 +246,8 @@ export interface SandboxRef { createdFromWorkloadTemplate?: SandboxWorkloadTemplateProvenance; /** Service URLs returned by creation, keyed by service name. */ serviceUrls: Record; + applicationServiceUrls: Record; + gatewayServiceUrls: Record; restartCount: number; /** Creation time in epoch milliseconds; absent when the gateway omits it. */ createdAtMs?: number; @@ -623,7 +625,12 @@ function policySourceName(s: PolicySource): PolicySourceName { return POLICY_SOURCE_NAMES[s] ?? 'unspecified'; } -function sandboxRef(sandbox: Sandbox | undefined, serviceUrls: Record = {}): SandboxRef { +function sandboxRef( + sandbox: Sandbox | undefined, + serviceUrls: Record = {}, + applicationServiceUrls: Record = {}, + gatewayServiceUrls: Record = {}, +): SandboxRef { if (!sandbox) throw new SdkError('invalid_config', 'sandbox missing from gateway response'); const meta = sandbox.metadata; if (!meta?.id || !meta.name) { @@ -646,6 +653,8 @@ function sandboxRef(sandbox: Sandbox | undefined, serviceUrls: Record Date: Fri, 30 Oct 2026 21:29:04 -0700 Subject: [PATCH 2/2] test(services): use ty suppression for immutable application URLs Signed-off-by: Taylor Mutch --- python/openshell/sandbox_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/openshell/sandbox_test.py b/python/openshell/sandbox_test.py index 8c7ec0de7a..0a9e3c9597 100644 --- a/python/openshell/sandbox_test.py +++ b/python/openshell/sandbox_test.py @@ -2291,7 +2291,7 @@ def test_create_forwards_service_exposures() -> None: assert dict(ref.application_service_urls) == dict(ref.service_urls) assert ref.gateway_service_urls["metrics"] == "https://metrics.gateway.test:8080/" with pytest.raises(TypeError): - ref.application_service_urls["metrics"] = "changed" # type: ignore[index] + ref.application_service_urls["metrics"] = "changed" # ty: ignore[invalid-assignment] def test_application_addresses_preserve_existing_positional_sandbox_ref() -> None: