From ba19ea3fa65ce1a688c216bbb95c666fabe45d62 Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Wed, 26 Aug 2026 14:15:01 -0700 Subject: [PATCH 1/2] refactor(supervisor): prepare desired-state snapshots Signed-off-by: Piotr Mlocek --- crates/openshell-sandbox/src/lib.rs | 129 ++++++++++++------ crates/openshell-server/src/grpc/policy.rs | 94 ++++++++++++- crates/openshell-server/src/inference.rs | 85 ++++++++++-- .../src/inference_routes.rs | 83 ++++++++--- 4 files changed, 317 insertions(+), 74 deletions(-) diff --git a/crates/openshell-sandbox/src/lib.rs b/crates/openshell-sandbox/src/lib.rs index b1c226cebd..100b3d2c77 100644 --- a/crates/openshell-sandbox/src/lib.rs +++ b/crates/openshell-sandbox/src/lib.rs @@ -3948,47 +3948,12 @@ async fn run_policy_poll_loop_with_client( { Ok(env_result) => { let provider_env_revision = env_result.provider_env_revision; - let install_result = ctx.provider_credentials.install_bound_environment( - provider_env_revision, - env_result.environment, - env_result.credential_expires_at_ms, - env_result.dynamic_credentials, - env_result.static_credential_bindings, - env_result.non_secret_environment_keys, - ); - if let Err(error) = install_result { - ocsf_emit!( - ConfigStateChangeBuilder::new(ocsf_ctx()) - .severity(SeverityId::High) - .status(StatusId::Failure) - .state(StateId::Disabled, "fail_closed") - .message(format!( - "Rejected provider environment refresh; static provider credentials were revoked; fetched dynamic token grants remain active: {error}" - )) - .build() - ); - } else { - let child_env = ctx.provider_credentials.child_env_with_gcp_resolved(); - let env_count = child_env.len(); - if let Some(publisher) = ctx.sidecar_control_publisher.as_ref() { - publisher - .publish_provider_env(provider_env_revision, child_env.clone()); - } + if apply_provider_environment_snapshot( + &ctx.provider_credentials, + env_result, + ctx.sidecar_control_publisher.as_ref(), + ) { current_provider_env_revision = provider_env_revision; - ocsf_emit!( - ConfigStateChangeBuilder::new(ocsf_ctx()) - .severity(SeverityId::Informational) - .status(StatusId::Success) - .state(StateId::Enabled, "loaded") - .unmapped( - "provider_env_revision", - serde_json::json!(provider_env_revision) - ) - .message(format!( - "Provider environment refreshed [revision:{provider_env_revision} env_count:{env_count}]" - )) - .build() - ); } } Err(e) => { @@ -4262,6 +4227,61 @@ async fn run_policy_poll_loop_with_client( } } +/// Apply one complete provider-environment snapshot to the live credential state. +/// +/// The caller remains responsible for serializing snapshots and deciding whether +/// a failed application should be retried. Keeping transport outside this helper +/// lets polling and supervisor-session updates share the same installation path. +fn apply_provider_environment_snapshot( + provider_credentials: &ProviderCredentialState, + snapshot: openshell_core::grpc_client::ProviderEnvironmentResult, + sidecar_control_publisher: Option<&sidecar_control::Publisher>, +) -> bool { + let provider_env_revision = snapshot.provider_env_revision; + let install_result = provider_credentials.install_bound_environment( + provider_env_revision, + snapshot.environment, + snapshot.credential_expires_at_ms, + snapshot.dynamic_credentials, + snapshot.static_credential_bindings, + snapshot.non_secret_environment_keys, + ); + if let Err(error) = install_result { + ocsf_emit!( + ConfigStateChangeBuilder::new(ocsf_ctx()) + .severity(SeverityId::High) + .status(StatusId::Failure) + .state(StateId::Disabled, "fail_closed") + .message(format!( + "Rejected provider environment refresh; static provider credentials were revoked; fetched dynamic token grants remain active: {error}" + )) + .build() + ); + return false; + } + + let child_env = provider_credentials.child_env_with_gcp_resolved(); + let env_count = child_env.len(); + if let Some(publisher) = sidecar_control_publisher { + publisher.publish_provider_env(provider_env_revision, child_env); + } + ocsf_emit!( + ConfigStateChangeBuilder::new(ocsf_ctx()) + .severity(SeverityId::Informational) + .status(StatusId::Success) + .state(StateId::Enabled, "loaded") + .unmapped( + "provider_env_revision", + serde_json::json!(provider_env_revision) + ) + .message(format!( + "Provider environment refreshed [revision:{provider_env_revision} env_count:{env_count}]" + )) + .build() + ); + true +} + fn apply_ocsf_json_setting( enabled: &AtomicBool, settings: &std::collections::HashMap, @@ -4498,6 +4518,35 @@ mod tests { ); } + #[test] + fn provider_environment_snapshot_apply_installs_complete_snapshot() { + let provider_credentials = + ProviderCredentialState::from_child_env_snapshot(1, std::collections::HashMap::new()); + let applied = apply_provider_environment_snapshot( + &provider_credentials, + openshell_core::grpc_client::ProviderEnvironmentResult { + environment: std::collections::HashMap::from([( + "API_BASE".to_string(), + "https://example.test".to_string(), + )]), + provider_env_revision: 7, + credential_expires_at_ms: std::collections::HashMap::new(), + dynamic_credentials: std::collections::HashMap::new(), + static_credential_bindings: std::collections::HashMap::new(), + non_secret_environment_keys: vec!["API_BASE".to_string()], + }, + None, + ); + + assert!(applied); + let snapshot = provider_credentials.snapshot(); + assert_eq!(snapshot.revision, 7); + assert_eq!( + snapshot.child_env.get("API_BASE").map(String::as_str), + Some("openshell:resolve:env:v7_API_BASE") + ); + } + #[tokio::test] async fn sidecar_control_provider_env_update_orders_by_generation() { let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); diff --git a/crates/openshell-server/src/grpc/policy.rs b/crates/openshell-server/src/grpc/policy.rs index 114b43aba0..975e608f1f 100644 --- a/crates/openshell-server/src/grpc/policy.rs +++ b/crates/openshell-server/src/grpc/policy.rs @@ -2321,6 +2321,21 @@ pub(super) async fn handle_get_sandbox_config( let sandbox = super::sandbox::fetch_and_authorize_sandbox(state, &principal, &sandbox_id).await?; + build_sandbox_config_snapshot(state, &sandbox) + .await + .map(Response::new) +} + +/// Build the effective gateway-owned configuration for an authorized sandbox. +/// +/// Keeping request authentication outside this function lets supervisor session +/// bootstrap and reconciliation reuse the same snapshot construction path as +/// the public `GetSandboxConfig` RPC. +pub async fn build_sandbox_config_snapshot( + state: &ServerState, + sandbox: &Sandbox, +) -> Result { + let sandbox_id = sandbox.object_id().to_string(); let workspace = sandbox.object_workspace().to_string(); let sandbox_provider_names = sandbox .spec @@ -2498,7 +2513,7 @@ pub(super) async fn handle_get_sandbox_config( ); if let Some(policy) = policy.as_ref() { validate_policy_credential_bindings_for_sandbox( - state.as_ref(), + state, &provider_profile_catalog, &workspace, &sandbox_provider_names, @@ -2515,7 +2530,7 @@ pub(super) async fn handle_get_sandbox_config( ) .await?; - Ok(Response::new(GetSandboxConfigResponse { + Ok(GetSandboxConfigResponse { policy, version, policy_hash, @@ -2532,7 +2547,7 @@ pub(super) async fn handle_get_sandbox_config( .as_str() .to_string(), extension_authentication_enabled: state.sandbox_jwt_issuer.is_some(), - })) + }) } #[cfg(test)] @@ -3042,6 +3057,23 @@ pub(super) async fn handle_get_sandbox_provider_environment( .await .map_err(|e| Status::internal(format!("fetch sandbox failed: {e}")))? .ok_or_else(|| Status::not_found("sandbox not found"))?; + + build_provider_environment_snapshot(state, &sandbox, supports_static_credential_bindings) + .await + .map(Response::new) +} + +/// Build the gateway-owned provider environment for an authorized sandbox. +/// +/// `supports_static_credential_bindings` preserves the existing fetch RPC's +/// compatibility behavior. The required session protocol introduced by +/// #1731 will call this builder with binding support enabled. +pub async fn build_provider_environment_snapshot( + state: &ServerState, + sandbox: &Sandbox, + supports_static_credential_bindings: bool, +) -> Result { + let sandbox_id = sandbox.object_id().to_string(); let workspace = sandbox.object_workspace().to_string(); let spec = sandbox @@ -3061,10 +3093,10 @@ pub(super) async fn handle_get_sandbox_provider_environment( ) .await?; let effective_policy = current_effective_policy_for_sandbox( - state.as_ref(), + state, &provider_profile_catalog, &workspace, - &sandbox, + sandbox, &sandbox_id, ) .await?; @@ -3136,14 +3168,14 @@ pub(super) async fn handle_get_sandbox_provider_environment( .cloned() .collect(); - Ok(Response::new(GetSandboxProviderEnvironmentResponse { + Ok(GetSandboxProviderEnvironmentResponse { environment: provider_environment.environment, provider_env_revision, credential_expires_at_ms: provider_environment.credential_expires_at_ms, dynamic_credentials: provider_environment.dynamic_credentials, static_credential_bindings: provider_environment.static_credential_bindings, non_secret_environment_keys, - })) + }) } // --------------------------------------------------------------------------- @@ -9673,6 +9705,54 @@ mod tests { ); } + #[tokio::test] + async fn snapshot_builders_match_existing_fetch_rpc_payloads() { + use openshell_core::proto::GetSandboxProviderEnvironmentRequest; + + let state = test_server_state().await; + state + .store + .put_message(&test_provider("work-github", "github")) + .await + .unwrap(); + let sandbox = test_sandbox( + "sb-builder-parity", + "builder-parity", + test_policy_with_rule("sandbox_only", "sandbox.example.com"), + vec!["work-github".to_string()], + ); + state.store.put_message(&sandbox).await.unwrap(); + + let built_config = build_sandbox_config_snapshot(&state, &sandbox) + .await + .unwrap(); + let fetched_config = handle_get_sandbox_config( + &state, + with_user(Request::new(GetSandboxConfigRequest { + sandbox_id: sandbox.object_id().to_string(), + })), + ) + .await + .unwrap() + .into_inner(); + assert_eq!(built_config, fetched_config); + + let built_environment = build_provider_environment_snapshot(&state, &sandbox, true) + .await + .unwrap(); + let fetched_environment = handle_get_sandbox_provider_environment( + &state, + with_user(Request::new(GetSandboxProviderEnvironmentRequest { + sandbox_id: sandbox.object_id().to_string(), + supports_static_credential_bindings: true, + })), + ) + .await + .unwrap() + .into_inner(); + assert_eq!(built_environment, fetched_environment); + } + #[tokio::test] async fn provider_environment_resolution_is_unchanged_by_providers_v2_setting() { use openshell_core::proto::GetSandboxProviderEnvironmentRequest; diff --git a/crates/openshell-server/src/inference.rs b/crates/openshell-server/src/inference.rs index b83fd6be4f..9081d84cac 100644 --- a/crates/openshell-server/src/inference.rs +++ b/crates/openshell-server/src/inference.rs @@ -80,14 +80,9 @@ impl Inference for InferenceService { .await .map_err(|e| Status::internal(format!("fetch sandbox failed: {e}")))? .ok_or_else(|| Status::not_found(format!("sandbox '{sandbox_id}' not found")))?; - let workspace = sandbox.object_workspace(); - resolve_inference_bundle_with_credentials( - self.state.store.as_ref(), - workspace, - Some(&self.state.credentials), - ) - .await - .map(Response::new) + build_inference_bundle_snapshot(&self.state, &sandbox) + .await + .map(Response::new) } async fn set_inference_route( @@ -1099,6 +1094,22 @@ async fn resolve_inference_bundle_with_credentials( }) } +/// Build the resolved gateway-owned inference bundle for an authorized sandbox. +/// +/// The public fetch RPC and supervisor session delivery share this boundary so +/// route resolution, credential loading, and revision calculation cannot drift. +pub async fn build_inference_bundle_snapshot( + state: &ServerState, + sandbox: &Sandbox, +) -> Result { + resolve_inference_bundle_with_credentials( + state.store.as_ref(), + sandbox.object_workspace(), + Some(&state.credentials), + ) + .await +} + #[cfg(test)] async fn resolve_route_by_name( store: &Store, @@ -3766,6 +3777,64 @@ mod tests { ); } + #[tokio::test] + async fn inference_snapshot_builder_matches_fetch_rpc_payload() { + use crate::grpc::test_support::test_server_state; + use openshell_core::proto::SandboxSpec; + use openshell_core::proto::datamodel::v1::ObjectMeta; + + let state = test_server_state().await; + let provider = make_provider("openai-dev", "openai", "OPENAI_API_KEY", "sk-test"); + state + .store + .put_message(&provider) + .await + .expect("persist provider"); + upsert_inference_route( + state.store.as_ref(), + "default", + CLUSTER_INFERENCE_ROUTE_NAME, + "openai-dev", + "gpt-4", + 0, + false, + ) + .await + .expect("set inference route"); + let sandbox = Sandbox { + metadata: Some(ObjectMeta { + id: "sandbox-a".to_string(), + name: "sandbox-a".to_string(), + workspace: "default".to_string(), + ..Default::default() + }), + spec: Some(SandboxSpec::default()), + ..Default::default() + }; + state + .store + .put_message(&sandbox) + .await + .expect("persist sandbox"); + + let built = build_inference_bundle_snapshot(&state, &sandbox) + .await + .expect("build snapshot"); + let service = InferenceService::new(state); + let mut request = Request::new(GetInferenceBundleRequest {}); + request.extensions_mut().insert(test_sandbox_principal()); + let fetched = service + .get_inference_bundle(request) + .await + .expect("fetch bundle") + .into_inner(); + + assert_eq!(built.routes, fetched.routes); + assert_eq!(built.revision, fetched.revision); + assert!(built.generated_at_ms > 0); + assert!(fetched.generated_at_ms > 0); + } + /// Non-member callers must receive `PERMISSION_DENIED` — not `NOT_FOUND` — /// when targeting a workspace that does not exist. Returning `NOT_FOUND` /// would create a CWE-203 workspace-name oracle. diff --git a/crates/openshell-supervisor-network/src/inference_routes.rs b/crates/openshell-supervisor-network/src/inference_routes.rs index 22b406b8dd..75a26a2499 100644 --- a/crates/openshell-supervisor-network/src/inference_routes.rs +++ b/crates/openshell-supervisor-network/src/inference_routes.rs @@ -363,25 +363,8 @@ pub fn spawn_route_refresh( continue; } - let routes = bundle_to_resolved_routes(&bundle); - let (user_routes, system_routes) = partition_routes(routes); - ocsf_emit!(ConfigStateChangeBuilder::new(ocsf_ctx()) - .severity(SeverityId::Informational) - .status(StatusId::Success) - .state(StateId::Enabled, "updated") - .unmapped("user_route_count", serde_json::json!(user_routes.len())) - .unmapped("system_route_count", serde_json::json!(system_routes.len())) - .unmapped("revision", serde_json::json!(&bundle.revision)) - .message(format!( - "Inference routes updated [user_route_count:{} system_route_count:{} revision:{}]", - user_routes.len(), - system_routes.len(), - bundle.revision - )) - .build()); - current_revision = Some(bundle.revision); - *user_cache.write().await = user_routes; - *system_cache.write().await = system_routes; + current_revision = + Some(apply_inference_bundle(&user_cache, &system_cache, bundle).await); } Err(e) => { ocsf_emit!(ConfigStateChangeBuilder::new(ocsf_ctx()) @@ -399,6 +382,38 @@ pub fn spawn_route_refresh( }); } +/// Apply one complete inference bundle to the live user and system route caches. +/// +/// Fetching and revision comparison stay with the caller so polling and future +/// supervisor-session updates can share this cache-installation boundary. +pub async fn apply_inference_bundle( + user_cache: &tokio::sync::RwLock>, + system_cache: &tokio::sync::RwLock>, + bundle: openshell_core::proto::GetInferenceBundleResponse, +) -> String { + let routes = bundle_to_resolved_routes(&bundle); + let (user_routes, system_routes) = partition_routes(routes); + ocsf_emit!( + ConfigStateChangeBuilder::new(ocsf_ctx()) + .severity(SeverityId::Informational) + .status(StatusId::Success) + .state(StateId::Enabled, "updated") + .unmapped("user_route_count", serde_json::json!(user_routes.len())) + .unmapped("system_route_count", serde_json::json!(system_routes.len())) + .unmapped("revision", serde_json::json!(&bundle.revision)) + .message(format!( + "Inference routes updated [user_route_count:{} system_route_count:{} revision:{}]", + user_routes.len(), + system_routes.len(), + bundle.revision + )) + .build() + ); + *user_cache.write().await = user_routes; + *system_cache.write().await = system_routes; + bundle.revision +} + #[cfg(test)] #[allow( clippy::needless_raw_string_hashes, @@ -489,6 +504,36 @@ mod tests { assert!(routes.is_empty()); } + #[tokio::test] + async fn inference_bundle_apply_replaces_both_route_caches() { + let user_cache = tokio::sync::RwLock::new(Vec::new()); + let system_cache = tokio::sync::RwLock::new(Vec::new()); + let bundle = openshell_core::proto::GetInferenceBundleResponse { + routes: vec![ + openshell_core::proto::ResolvedRoute { + name: "inference.local".to_string(), + base_url: "http://local.test/v1".to_string(), + model_id: "local-model".to_string(), + ..Default::default() + }, + openshell_core::proto::ResolvedRoute { + name: SANDBOX_SYSTEM_ROUTE_NAME.to_string(), + base_url: "https://system.test/v1".to_string(), + model_id: "system-model".to_string(), + ..Default::default() + }, + ], + revision: "revision-7".to_string(), + generated_at_ms: 0, + }; + + let revision = apply_inference_bundle(&user_cache, &system_cache, bundle).await; + + assert_eq!(revision, "revision-7"); + assert_eq!(user_cache.read().await[0].model, "local-model"); + assert_eq!(system_cache.read().await[0].model, "system-model"); + } + #[test] fn bundle_to_resolved_routes_preserves_name_field() { let bundle = openshell_core::proto::GetInferenceBundleResponse { From 1ab15bff58078893e5f7e2d628e80a576b73b3fa Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Wed, 26 Aug 2026 17:18:57 -0700 Subject: [PATCH 2/2] feat(supervisor): push gateway desired state Signed-off-by: Piotr Mlocek --- architecture/compute-runtimes.md | 2 +- architecture/gateway.md | 8 +- architecture/google-vertex-ai-provider.md | 12 +- architecture/sandbox.md | 84 +- architecture/security-policy.md | 7 +- crates/openshell-cli/src/run.rs | 38 +- .../tests/ensure_providers_integration.rs | 27 +- .../openshell-cli/tests/mtls_integration.rs | 14 +- .../tests/provider_commands_integration.rs | 28 +- .../sandbox_create_lifecycle_integration.rs | 31 +- .../sandbox_name_fallback_integration.rs | 21 +- crates/openshell-core/src/grpc_client.rs | 104 +- crates/openshell-core/src/proposals.rs | 4 +- .../src/provider_credentials.rs | 7 +- crates/openshell-core/src/settings.rs | 3 +- crates/openshell-driver-kubernetes/README.md | 2 +- crates/openshell-extension-core/src/store.rs | 2 +- .../src/proto_json.rs | 5 +- crates/openshell-sandbox/src/lib.rs | 983 ++++++- crates/openshell-sandbox/src/main.rs | 2 +- crates/openshell-sdk/tests/client_mock.rs | 13 +- .../openshell-server/src/auth/method_authz.rs | 6 - .../src/auth/sandbox_methods.rs | 3 - crates/openshell-server/src/compute/mod.rs | 234 +- crates/openshell-server/src/grpc/mod.rs | 89 +- crates/openshell-server/src/grpc/policy.rs | 36 +- crates/openshell-server/src/inference.rs | 59 +- crates/openshell-server/src/multiplex.rs | 37 +- .../openshell-server/src/provider_refresh.rs | 17 +- crates/openshell-server/src/sandbox_watch.rs | 27 + .../src/supervisor_session.rs | 975 ++++++- crates/openshell-server/tests/common/mod.rs | 28 +- .../tests/supervisor_relay_integration.rs | 11 +- .../src/inference_routes.rs | 296 +- .../src/l7/relay.rs | 2 +- .../src/policy_local.rs | 2 +- .../openshell-supervisor-network/src/run.rs | 53 +- .../openshell-supervisor-process/src/run.rs | 83 +- .../src/supervisor_session.rs | 275 +- deploy/rpm/TROUBLESHOOTING.md | 5 + docs/about/installation.mdx | 5 + docs/get-started/tutorials/docker-compose.mdx | 5 + .../microsoft-graph-provider-refresh.mdx | 2 +- docs/kubernetes/topology.mdx | 3 +- docs/observability/logging.mdx | 3 +- docs/observability/ocsf-json-export.mdx | 2 +- docs/reference/gateway-auth.mdx | 2 +- docs/reference/sandbox-compute-drivers.mdx | 8 +- docs/reference/support-matrix.mdx | 23 + docs/sandboxes/policy-advisor.mdx | 2 +- docs/sandboxes/providers-v2.mdx | 2 +- e2e/python/test_sandbox_providers.py | 4 +- e2e/rust/tests/live_policy_update.rs | 2 +- proto/inference.proto | 13 +- proto/openshell.proto | 107 +- proto/sandbox.proto | 6 +- rfc/0011-multi-player-design/README.md | 29 +- sdk/go/openshell/v1/config_client_test.go | 10 +- .../v1/internal/converter/setting.go | 4 +- .../v1/internal/converter/setting_test.go | 8 +- sdk/go/proto/inferencev1/inference.pb.go | 57 +- sdk/go/proto/inferencev1/inference_grpc.pb.go | 40 - sdk/go/proto/openshellv1/openshell.pb.go | 2478 +++++++++++------ sdk/go/proto/openshellv1/openshell_grpc.pb.go | 190 +- sdk/go/proto/sandboxv1/sandbox.pb.go | 66 +- sdk/typescript/buf.gen.yaml | 6 +- sdk/typescript/src/client.ts | 4 +- tasks/scripts/generate_python_proto.py | 4 + 68 files changed, 4631 insertions(+), 2089 deletions(-) diff --git a/architecture/compute-runtimes.md b/architecture/compute-runtimes.md index 2a36073486..6792238768 100644 --- a/architecture/compute-runtimes.md +++ b/architecture/compute-runtimes.md @@ -333,7 +333,7 @@ process-supervision leaf and launches the user workload after the sidecar serves bootstrap state over a local control socket. The network sidecar owns gateway credentials and sends policy plus workload-facing provider environment state to the process leaf over that socket. It also streams provider -environment updates after settings polls so future process sessions see +environment updates received from the gateway so future process sessions see updated provider env without giving the process leaf gateway access. The pre-workload process supervisor is the only accepted control client: the network sidecar verifies its UID, GID, and PID with peer credentials, removes diff --git a/architecture/gateway.md b/architecture/gateway.md index f86f855845..852bbbae95 100644 --- a/architecture/gateway.md +++ b/architecture/gateway.md @@ -467,8 +467,8 @@ still referenced by a sandbox. Policy and runtime settings are delivered together through the effective sandbox config path. A gateway-global policy can override sandbox-scoped policy. The -sandbox supervisor polls for config revisions and hot-reloads dynamic policy -when the policy engine accepts the update. +gateway pushes changed snapshots over `ConnectSupervisor`, and the sandbox +supervisor hot-reloads dynamic policy when the policy engine accepts the update. External supervisor middleware registration is operator-owned configuration under `[[openshell.supervisor.middleware]]`. At startup the gateway connects to @@ -505,8 +505,8 @@ include profile endpoint and binding changes. Cluster inference routes store only `provider_name`, `model_id`, and optional timeout. The gateway resolves endpoint URLs, protocols, credentials, auth -style, and route-shaping metadata from the provider record when supervisors call -`GetInferenceBundle`. Supported provider types for cluster inference are +style, and route-shaping metadata from the provider record when the gateway +builds supervisor bootstrap and update snapshots. Supported provider types for cluster inference are `openai`, `anthropic`, `nvidia`, `deepinfra`, and `google-vertex-ai`. The bundle carries enough information for sandbox-local routers to construct diff --git a/architecture/google-vertex-ai-provider.md b/architecture/google-vertex-ai-provider.md index aac0c11c27..dc7c2c412c 100644 --- a/architecture/google-vertex-ai-provider.md +++ b/architecture/google-vertex-ai-provider.md @@ -65,8 +65,8 @@ Gateway (openshell-server) │ ├── infer_vertex_publisher() model → publisher │ └── vertex_location_and_host() region → Vertex API host │ - └── GetInferenceBundleRequest (from sandbox on connect) - └── resolve_route_by_name() re-resolves live route+credentials + └── ConfigBootstrap / ConfigUpdate snapshot builder + └── resolve_route_by_name() resolves live route+credentials Router (openshell-router) │ @@ -412,10 +412,10 @@ Provider type normalization for the `ProviderRegistry` (non-inference providers ## 9. Inference Routing in the Sandbox -When a sandbox agent connects to `https://inference.local`, the sandbox fetches the -inference bundle from the gateway (`GetInferenceBundleRequest`). The bundle contains one -or more `ResolvedRoute` proto messages built by `resolve_route_by_name`. For a Vertex AI -route the bundle contains: +Before a sandbox agent connects to `https://inference.local`, the supervisor applies the +inference snapshot delivered through `ConnectSupervisor`. The snapshot contains one or +more `ResolvedRoute` proto messages built by `resolve_route_by_name`. For a Vertex AI +route the snapshot contains: ``` ResolvedRoute { diff --git a/architecture/sandbox.md b/architecture/sandbox.md index 786ed5194d..a28e947504 100644 --- a/architecture/sandbox.md +++ b/architecture/sandbox.md @@ -10,7 +10,7 @@ Each sandbox workload has two trust levels: | Process | Role | |---|---| -| Supervisor | Starts as root inside the workload, prepares isolation, runs the proxy, fetches config, injects credentials, serves the relay socket, and launches child processes. | +| Supervisor | Starts as root inside the workload, applies desired state, prepares isolation, runs the proxy, injects credentials, serves the relay socket, and launches child processes. | | Agent child | Runs as an unprivileged user with filesystem, process, and network restrictions applied. | The supervisor keeps enough privilege to manage the sandbox, but the agent child @@ -25,15 +25,55 @@ only when the set is already empty; any other outcome fails the spawn. 1. The compute runtime starts the workload with sandbox identity, callback endpoint, TLS or secret material, image metadata, and initial command. -2. The supervisor loads policy and runtime settings from local files or the - gateway, depending on mode. -3. It prepares filesystem access, process restrictions, network namespace +2. In gateway-backed mode, the supervisor opens `ConnectSupervisor` before it + initializes gateway-owned runtime state. The gateway sends a complete + configuration, provider environment, and inference bootstrap. +3. The supervisor applies the bootstrap, or loads explicit local files in + standalone mode. A required configuration or policy failure aborts startup. +4. It prepares filesystem access, process restrictions, network namespace routing, trust stores, provider credential resolution, and inference routes. -4. It launches the persisted canonical main-process argv and retains its PTY +5. It launches the persisted canonical main-process argv and retains its PTY or pipes in the main-session multiplexer. -5. It starts the policy proxy and local SSH server. -6. It opens a supervisor session back to the gateway for connect, exec, file - sync, config polling, and log push. +6. It starts the policy proxy and local SSH server, then signals runtime-ready. +7. The gateway enables connect, exec, file sync, and other relay operations only + after both bootstrap initialization and runtime-ready are complete. + +## Desired-State Delivery + +`ConnectSupervisor` carries gateway-owned desired state. A fresh session always +starts with a complete `ConfigBootstrap`. Bootstrap success marks the session +initialized; `SupervisorRuntimeReady` separately proves that runtime-dependent +services are available. Bootstrap failure or a 120-second timeout moves the +sandbox to `Error`. Disconnect before initialization returns it to +`Provisioning`. + +After bootstrap, the gateway sends level-triggered `ConfigUpdate` messages for +one component at a time. Request IDs correlate results, component-local sequence +numbers reject stale delivery, and snapshot revisions remain equality-only +content fingerprints. The gateway keeps at most one update in flight for each +component and coalesces newer state. Failed live updates keep the sandbox +`Ready` and add a component-specific degraded condition. Policy and inference +retain their last-known-good runtime state. Invalid provider bindings fail +closed by revoking static credential material while retaining fetched dynamic +token grants. + +Committed sandbox changes notify the session owner immediately. Provider and +inference changes use a workspace-wide invalidation signal. A 30-second +jittered reconciliation pass rebuilds snapshots for active locally owned +sessions, repairing missed notifications and cross-replica writes. Reconnects +discard queued state and begin again with one complete bootstrap. + +Explicit local policy and inference files remain authoritative. Their component +results report `RETAINED_LOCAL_OVERRIDE`, while non-conflicting gateway settings +and provider state continue to update. A supervisor without a gateway does not +open or wait for a session. + +`GetSandboxConfig` remains a public read API. Policy status, policy analysis, +log upload, credential refresh, and relay RPCs remain independent of desired +state delivery. The old supervisor-only provider-environment and inference-bundle +fetch RPCs no longer exist. Snapshot builders and component apply routines are +shared implementation boundaries for a future direct transport; the runtime +does not add a transport abstraction before that transport exists. ## Isolation Layers @@ -195,10 +235,9 @@ the registry. Public custom-CA PEM travels with the stable registration. The slots live in a supervisor-owned `ExtensionCredentialStore` shared by every gateway connection the supervisor opens, so the registry's clients and the -polling loop that rotates them observe the same credentials. Configuration -polling runs far more frequently than credentials expire, so the loop rotates -only when a credential is missing or has passed four fifths of its lifetime, -and bounds its sleep by the soonest rotation deadline. +independent credential-refresh task observe the same credentials. The task +rotates only when a credential is missing or has passed four fifths of its +lifetime. Middleware cannot observe injected credentials or mutate supervisor-owned credential, routing, or framing headers. Body transformations are re-evaluated @@ -442,9 +481,9 @@ policy structure. This holds even when the initial policy is enriched with baseline paths during startup: the enriched revision the supervisor synced back to the gateway is the revision it acknowledges, so a successfully constructed initial policy never -remains `Pending`. If the first poll returns a different revision, the supervisor -processes it through the normal reload path instead of treating it as already -loaded. +remains `Pending`. If a subsequent pushed snapshot carries a different revision, +the supervisor processes it through the normal reload path instead of treating +it as already loaded. A newer sandbox-scoped revision can carry the same non-empty effective policy hash as the currently loaded revision, for example when provenance changes @@ -459,19 +498,22 @@ Policy status delivery uses a FIFO background worker. Retryable delivery failures retain the ordered update and retry with capped exponential backoff; terminal errors are logged and discarded. The outbox is nonblocking and does not discard updates because of a fixed queue capacity, so status endpoint -outages cannot block policy polling, enforcement, settings, or provider -refreshes and cannot permanently lose the initial acknowledgement. +outages cannot block desired-state application, enforcement, settings, or +provider refreshes and cannot permanently lose the initial acknowledgement. Only sandbox-scoped revisions (`PolicySource::Sandbox`, version greater than zero) are acknowledged. Global policies and local-file development policies do not use the sandbox revision API and produce no acknowledgement. When explicit -local Rego and data files are configured, the supervisor continues polling the -gateway for settings and provider refreshes but never replaces the local OPA -engine with a gateway policy revision. +local Rego and data files are configured, pushed gateway updates still apply +settings and provider changes but never replace the local OPA engine with a +gateway policy revision. ## Failure Behavior -- If gateway config polling fails, the sandbox keeps its last-known-good policy. +- If desired-state delivery or application fails after startup, the sandbox + remains ready and reports a degraded condition. Policy and inference retain + their last-known-good state; invalid provider bindings follow the fail-closed + revocation behavior above. - If a live policy or middleware-registry update is invalid, the supervisor rejects the combined update and keeps the current runtime pair. - If an operator-run middleware call fails, the selected config's `on_error` diff --git a/architecture/security-policy.md b/architecture/security-policy.md index 3f582eb25d..f0387536b2 100644 --- a/architecture/security-policy.md +++ b/architecture/security-policy.md @@ -166,9 +166,10 @@ detection finding at startup naming the inactive controls. The gateway stores sandbox-authored policy revisions separately from derived effective sandbox configuration. Effective configuration can include gateway-global policy overrides and provider-profile policy layers. The -supervisor polls for config revisions and attempts to load new dynamic policy -into the in-process OPA engine; CLI reads of the latest sandbox policy use the -same effective configuration path. +gateway pushes effective configuration revisions over the supervisor session. +The supervisor validates and loads changed dynamic policy into the in-process +OPA engine; CLI reads of the latest sandbox policy use the same effective +configuration path. The supervisor validates complete effective policy generations before activation. Overlapping endpoint selectors may contribute request allow and diff --git a/crates/openshell-cli/src/run.rs b/crates/openshell-cli/src/run.rs index 0a0b21a7f4..28d57e029c 100644 --- a/crates/openshell-cli/src/run.rs +++ b/crates/openshell-cli/src/run.rs @@ -42,15 +42,15 @@ use openshell_core::proto::{ ExposeServiceRequest, GetCurrentUserRequest, GetDraftHistoryRequest, GetDraftPolicyRequest, GetGatewayConfigRequest, GetInferenceRouteRequest, GetProviderProfileRequest, GetProviderRefreshStatusRequest, GetProviderRequest, GetSandboxConfigRequest, - GetSandboxConfigResponse, GetSandboxLogsRequest, GetSandboxPolicyStatusRequest, - GetSandboxRequest, GetServiceRequest, GpuResourceRequirements, ImportProviderProfilesRequest, - LintProviderProfilesRequest, ListProviderProfilesRequest, ListProvidersRequest, - ListSandboxPoliciesRequest, ListSandboxProvidersRequest, ListSandboxesRequest, - ListServicesRequest, PolicySource, PolicyStatus, Provider, - ProviderCredentialRefreshRecoveryAction, ProviderCredentialRefreshStatus, - ProviderCredentialRefreshStrategy, ProviderCredentialTokenGrantType, ProviderProfile, - ProviderProfileDiagnostic, ProviderProfileImportItem, RejectDraftChunkRequest, - ResourceRequirements, RevokeSshSessionRequest, RotateProviderCredentialRequest, Sandbox, + GetSandboxLogsRequest, GetSandboxPolicyStatusRequest, GetSandboxRequest, GetServiceRequest, + GpuResourceRequirements, ImportProviderProfilesRequest, LintProviderProfilesRequest, + ListProviderProfilesRequest, ListProvidersRequest, ListSandboxPoliciesRequest, + ListSandboxProvidersRequest, ListSandboxesRequest, ListServicesRequest, PolicySource, + PolicyStatus, Provider, ProviderCredentialRefreshRecoveryAction, + ProviderCredentialRefreshStatus, ProviderCredentialRefreshStrategy, + ProviderCredentialTokenGrantType, ProviderProfile, ProviderProfileDiagnostic, + ProviderProfileImportItem, RejectDraftChunkRequest, ResourceRequirements, + RevokeSshSessionRequest, RotateProviderCredentialRequest, Sandbox, SandboxConfigSnapshot, SandboxPhase, SandboxPolicy, SandboxSpec, SandboxTemplate, ServiceEndpointResponse, SetInferenceRouteRequest, SettingScope, StartSandboxRequest, StopSandboxRequest, TcpForwardFrame, TcpForwardInit, TcpRelayTarget, UpdateConfigRequest, @@ -2109,7 +2109,7 @@ fn sandbox_to_json(sandbox: &Sandbox) -> serde_json::Value { fn sandbox_detail_to_json( sandbox: &Sandbox, - config: &GetSandboxConfigResponse, + config: &SandboxConfigSnapshot, ) -> Result { let mut value = sandbox_to_json(sandbox); let obj = value @@ -5935,7 +5935,7 @@ pub async fn gateway_settings_get(server: &str, json: bool, tls: &TlsOptions) -> fn settings_to_json_sandbox( name: &str, workspace: &str, - response: &GetSandboxConfigResponse, + response: &SandboxConfigSnapshot, ) -> serde_json::Value { let policy_source = if response.policy_source == PolicySource::Global as i32 { "global" @@ -7429,12 +7429,12 @@ mod tests { PROGRESS_STEP_STARTING_SANDBOX, }; use openshell_core::proto::{ - GetSandboxConfigResponse, GpuResourceRequirements, PolicySource, PolicyStatus, Provider, - ProviderCredentialRefresh, ProviderCredentialRefreshRecoveryAction, - ProviderCredentialRefreshStatus, ProviderCredentialRefreshStrategy, - ProviderCredentialTokenGrant, ProviderProfile, ProviderProfileCredential, - ResourceRequirements, Sandbox, SandboxCondition, SandboxPhase, SandboxPolicyRevision, - SandboxStatus, datamodel::v1::ObjectMeta, + GpuResourceRequirements, PolicySource, PolicyStatus, Provider, ProviderCredentialRefresh, + ProviderCredentialRefreshRecoveryAction, ProviderCredentialRefreshStatus, + ProviderCredentialRefreshStrategy, ProviderCredentialTokenGrant, ProviderProfile, + ProviderProfileCredential, ResourceRequirements, Sandbox, SandboxCondition, + SandboxConfigSnapshot, SandboxPhase, SandboxPolicyRevision, SandboxStatus, + datamodel::v1::ObjectMeta, }; #[test] @@ -8789,7 +8789,7 @@ mod tests { sandbox.set_phase(SandboxPhase::Ready as i32); sandbox.set_current_policy_version(2); - let config = GetSandboxConfigResponse { + let config = SandboxConfigSnapshot { policy_source: PolicySource::Global as i32, global_policy_version: 3, ..Default::default() @@ -8815,7 +8815,7 @@ mod tests { }), ..Default::default() }; - let config = GetSandboxConfigResponse { + let config = SandboxConfigSnapshot { policy_source: PolicySource::Sandbox as i32, version: 0, ..Default::default() diff --git a/crates/openshell-cli/tests/ensure_providers_integration.rs b/crates/openshell-cli/tests/ensure_providers_integration.rs index 8192989375..356a8351b0 100644 --- a/crates/openshell-cli/tests/ensure_providers_integration.rs +++ b/crates/openshell-cli/tests/ensure_providers_integration.rs @@ -20,14 +20,12 @@ use openshell_core::proto::{ DetachSandboxProviderRequest, DetachSandboxProviderResponse, ExchangeProviderSubjectTokenRequest, ExchangeProviderSubjectTokenResponse, ExecSandboxEvent, ExecSandboxInput, ExecSandboxRequest, GatewayMessage, GetGatewayConfigRequest, - GetGatewayConfigResponse, GetProviderRequest, GetSandboxConfigRequest, - GetSandboxConfigResponse, GetSandboxProviderEnvironmentRequest, - GetSandboxProviderEnvironmentResponse, GetSandboxRequest, HealthRequest, HealthResponse, - ListProvidersRequest, ListProvidersResponse, ListSandboxProvidersRequest, - ListSandboxProvidersResponse, ListSandboxesRequest, ListSandboxesResponse, Provider, - ProviderResponse, RevokeSshSessionRequest, RevokeSshSessionResponse, SandboxResponse, - SandboxStreamEvent, ServiceStatus, SupervisorMessage, UpdateProviderRequest, - WatchSandboxRequest, + GetGatewayConfigResponse, GetProviderRequest, GetSandboxConfigRequest, GetSandboxRequest, + HealthRequest, HealthResponse, ListProvidersRequest, ListProvidersResponse, + ListSandboxProvidersRequest, ListSandboxProvidersResponse, ListSandboxesRequest, + ListSandboxesResponse, Provider, ProviderResponse, RevokeSshSessionRequest, + RevokeSshSessionResponse, SandboxConfigSnapshot, SandboxResponse, SandboxStreamEvent, + ServiceStatus, SupervisorMessage, UpdateProviderRequest, WatchSandboxRequest, }; use openshell_core::{ObjectId, ObjectName}; use std::collections::HashMap; @@ -178,8 +176,8 @@ impl OpenShell for TestOpenShell { async fn get_sandbox_config( &self, _request: tonic::Request, - ) -> Result, Status> { - Ok(Response::new(GetSandboxConfigResponse::default())) + ) -> Result, Status> { + Ok(Response::new(SandboxConfigSnapshot::default())) } async fn get_gateway_config( @@ -189,15 +187,6 @@ impl OpenShell for TestOpenShell { Ok(Response::new(GetGatewayConfigResponse::default())) } - async fn get_sandbox_provider_environment( - &self, - _request: tonic::Request, - ) -> Result, Status> { - Ok(Response::new( - GetSandboxProviderEnvironmentResponse::default(), - )) - } - async fn create_ssh_session( &self, _request: tonic::Request, diff --git a/crates/openshell-cli/tests/mtls_integration.rs b/crates/openshell-cli/tests/mtls_integration.rs index 91d520d1af..19ac17e19f 100644 --- a/crates/openshell-cli/tests/mtls_integration.rs +++ b/crates/openshell-cli/tests/mtls_integration.rs @@ -145,9 +145,9 @@ impl OpenShell for TestOpenShell { async fn get_sandbox_config( &self, _request: tonic::Request, - ) -> Result, Status> { + ) -> Result, Status> { Ok(Response::new( - openshell_core::proto::GetSandboxConfigResponse::default(), + openshell_core::proto::SandboxConfigSnapshot::default(), )) } @@ -160,16 +160,6 @@ impl OpenShell for TestOpenShell { )) } - async fn get_sandbox_provider_environment( - &self, - _request: tonic::Request, - ) -> Result, Status> - { - Ok(Response::new( - openshell_core::proto::GetSandboxProviderEnvironmentResponse::default(), - )) - } - async fn create_ssh_session( &self, _request: tonic::Request, diff --git a/crates/openshell-cli/tests/provider_commands_integration.rs b/crates/openshell-cli/tests/provider_commands_integration.rs index 2c71e0b39f..25b3f1b475 100644 --- a/crates/openshell-cli/tests/provider_commands_integration.rs +++ b/crates/openshell-cli/tests/provider_commands_integration.rs @@ -18,14 +18,13 @@ use openshell_core::proto::{ ExchangeProviderSubjectTokenRequest, ExchangeProviderSubjectTokenResponse, ExecSandboxEvent, ExecSandboxInput, ExecSandboxRequest, GatewayMessage, GetGatewayConfigRequest, GetGatewayConfigResponse, GetProviderRefreshStatusRequest, GetProviderRefreshStatusResponse, - GetProviderRequest, GetSandboxConfigRequest, GetSandboxConfigResponse, - GetSandboxProviderEnvironmentRequest, GetSandboxProviderEnvironmentResponse, GetSandboxRequest, - HealthRequest, HealthResponse, ListProvidersRequest, ListProvidersResponse, - ListSandboxProvidersRequest, ListSandboxProvidersResponse, ListSandboxesRequest, - ListSandboxesResponse, Provider, ProviderCredentialRefresh, ProviderCredentialRefreshStatus, - ProviderCredentialRefreshStrategy, ProviderProfile, ProviderProfileCredential, - ProviderProfileDiscovery, ProviderResponse, RevokeSshSessionRequest, RevokeSshSessionResponse, - RotateProviderCredentialRequest, RotateProviderCredentialResponse, Sandbox, SandboxResponse, + GetProviderRequest, GetSandboxConfigRequest, GetSandboxRequest, HealthRequest, HealthResponse, + ListProvidersRequest, ListProvidersResponse, ListSandboxProvidersRequest, + ListSandboxProvidersResponse, ListSandboxesRequest, ListSandboxesResponse, Provider, + ProviderCredentialRefresh, ProviderCredentialRefreshStatus, ProviderCredentialRefreshStrategy, + ProviderProfile, ProviderProfileCredential, ProviderProfileDiscovery, ProviderResponse, + RevokeSshSessionRequest, RevokeSshSessionResponse, RotateProviderCredentialRequest, + RotateProviderCredentialResponse, Sandbox, SandboxConfigSnapshot, SandboxResponse, SandboxStreamEvent, ServiceStatus, SettingValue, SupervisorMessage, UpdateProviderRequest, WatchSandboxRequest, setting_value, }; @@ -306,8 +305,8 @@ impl OpenShell for TestOpenShell { async fn get_sandbox_config( &self, _request: tonic::Request, - ) -> Result, Status> { - Ok(Response::new(GetSandboxConfigResponse::default())) + ) -> Result, Status> { + Ok(Response::new(SandboxConfigSnapshot::default())) } async fn get_gateway_config( @@ -320,15 +319,6 @@ impl OpenShell for TestOpenShell { })) } - async fn get_sandbox_provider_environment( - &self, - _request: tonic::Request, - ) -> Result, Status> { - Ok(Response::new( - GetSandboxProviderEnvironmentResponse::default(), - )) - } - async fn create_ssh_session( &self, _request: tonic::Request, diff --git a/crates/openshell-cli/tests/sandbox_create_lifecycle_integration.rs b/crates/openshell-cli/tests/sandbox_create_lifecycle_integration.rs index bcc07619ee..6252bad8ce 100644 --- a/crates/openshell-cli/tests/sandbox_create_lifecycle_integration.rs +++ b/crates/openshell-cli/tests/sandbox_create_lifecycle_integration.rs @@ -19,16 +19,14 @@ use openshell_core::proto::{ DetachSandboxProviderRequest, DetachSandboxProviderResponse, ExchangeProviderSubjectTokenRequest, ExchangeProviderSubjectTokenResponse, ExecSandboxEvent, ExecSandboxInput, ExecSandboxRequest, GatewayMessage, GetGatewayConfigRequest, - GetGatewayConfigResponse, GetProviderRequest, GetSandboxConfigRequest, - GetSandboxConfigResponse, GetSandboxProviderEnvironmentRequest, - GetSandboxProviderEnvironmentResponse, GetSandboxRequest, GpuResourceRequirements, - HealthRequest, HealthResponse, ListProvidersRequest, ListProvidersResponse, - ListSandboxProvidersRequest, ListSandboxProvidersResponse, ListSandboxesRequest, - ListSandboxesResponse, PlatformEvent, ProviderResponse, RevokeSshSessionRequest, - RevokeSshSessionResponse, Sandbox, SandboxCondition, SandboxLogLine, SandboxPhase, - SandboxResponse, SandboxStatus, SandboxStreamEvent, ServiceStatus, SettingValue, - SupervisorMessage, UpdateProviderRequest, WatchSandboxRequest, sandbox_stream_event, - setting_value, + GetGatewayConfigResponse, GetProviderRequest, GetSandboxConfigRequest, GetSandboxRequest, + GpuResourceRequirements, HealthRequest, HealthResponse, ListProvidersRequest, + ListProvidersResponse, ListSandboxProvidersRequest, ListSandboxProvidersResponse, + ListSandboxesRequest, ListSandboxesResponse, PlatformEvent, ProviderResponse, + RevokeSshSessionRequest, RevokeSshSessionResponse, Sandbox, SandboxCondition, + SandboxConfigSnapshot, SandboxLogLine, SandboxPhase, SandboxResponse, SandboxStatus, + SandboxStreamEvent, ServiceStatus, SettingValue, SupervisorMessage, UpdateProviderRequest, + WatchSandboxRequest, sandbox_stream_event, setting_value, }; use std::collections::HashMap; use std::fs; @@ -206,8 +204,8 @@ impl OpenShell for TestOpenShell { async fn get_sandbox_config( &self, _request: tonic::Request, - ) -> Result, Status> { - Ok(Response::new(GetSandboxConfigResponse::default())) + ) -> Result, Status> { + Ok(Response::new(SandboxConfigSnapshot::default())) } async fn get_gateway_config( @@ -223,15 +221,6 @@ impl OpenShell for TestOpenShell { })) } - async fn get_sandbox_provider_environment( - &self, - _request: tonic::Request, - ) -> Result, Status> { - Ok(Response::new( - GetSandboxProviderEnvironmentResponse::default(), - )) - } - async fn create_ssh_session( &self, request: tonic::Request, diff --git a/crates/openshell-cli/tests/sandbox_name_fallback_integration.rs b/crates/openshell-cli/tests/sandbox_name_fallback_integration.rs index 7e2cf74f50..803b094d49 100644 --- a/crates/openshell-cli/tests/sandbox_name_fallback_integration.rs +++ b/crates/openshell-cli/tests/sandbox_name_fallback_integration.rs @@ -18,13 +18,13 @@ use openshell_core::proto::{ ExchangeProviderSubjectTokenRequest, ExchangeProviderSubjectTokenResponse, ExecSandboxEvent, ExecSandboxInput, ExecSandboxRequest, GatewayMessage, GetGatewayConfigRequest, GetGatewayConfigResponse, GetProviderRequest, GetSandboxConfigRequest, - GetSandboxConfigResponse, GetSandboxPolicyStatusRequest, GetSandboxPolicyStatusResponse, - GetSandboxProviderEnvironmentRequest, GetSandboxProviderEnvironmentResponse, GetSandboxRequest, + GetSandboxPolicyStatusRequest, GetSandboxPolicyStatusResponse, GetSandboxRequest, HealthRequest, HealthResponse, ListProvidersRequest, ListProvidersResponse, ListSandboxProvidersRequest, ListSandboxProvidersResponse, ListSandboxesRequest, ListSandboxesResponse, NetworkEndpoint, NetworkPolicyRule, PolicyStatus, ProviderResponse, - Sandbox, SandboxPolicy, SandboxPolicyRevision, SandboxResponse, SandboxStreamEvent, - ServiceStatus, SupervisorMessage, UpdateProviderRequest, WatchSandboxRequest, + Sandbox, SandboxConfigSnapshot, SandboxPolicy, SandboxPolicyRevision, SandboxResponse, + SandboxStreamEvent, ServiceStatus, SupervisorMessage, UpdateProviderRequest, + WatchSandboxRequest, }; use std::sync::Arc; use tempfile::TempDir; @@ -162,13 +162,13 @@ impl OpenShell for TestOpenShell { async fn get_sandbox_config( &self, request: tonic::Request, - ) -> Result, Status> { + ) -> Result, Status> { let req = request.into_inner(); assert_eq!( req.sandbox_id, "test-id", "GetSandboxConfig should pass the id from GetSandbox" ); - Ok(Response::new(GetSandboxConfigResponse { + Ok(Response::new(SandboxConfigSnapshot { policy: Some(SandboxPolicy { version: 9, network_policies: [ @@ -222,15 +222,6 @@ impl OpenShell for TestOpenShell { Ok(Response::new(GetGatewayConfigResponse::default())) } - async fn get_sandbox_provider_environment( - &self, - _request: tonic::Request, - ) -> Result, Status> { - Ok(Response::new( - GetSandboxProviderEnvironmentResponse::default(), - )) - } - async fn create_ssh_session( &self, _request: tonic::Request, diff --git a/crates/openshell-core/src/grpc_client.rs b/crates/openshell-core/src/grpc_client.rs index 54f0db6902..282d30beda 100644 --- a/crates/openshell-core/src/grpc_client.rs +++ b/crates/openshell-core/src/grpc_client.rs @@ -24,11 +24,10 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use crate::proto::{ DenialSummary, ExchangeProviderSubjectTokenRequest, GetDraftPolicyRequest, - GetInferenceBundleRequest, GetInferenceBundleResponse, GetSandboxConfigRequest, - GetSandboxProviderEnvironmentRequest, IssueSandboxTokenRequest, NetworkActivitySummary, - PolicyChunk, PolicySource, PolicyStatus, RefreshSandboxTokenRequest, ReportPolicyStatusRequest, + GetSandboxConfigRequest, IssueSandboxTokenRequest, NetworkActivitySummary, PolicyChunk, + PolicySource, PolicyStatus, RefreshSandboxTokenRequest, ReportPolicyStatusRequest, SandboxPolicy as ProtoSandboxPolicy, SubmitPolicyAnalysisRequest, SubmitPolicyAnalysisResponse, - UpdateConfigRequest, inference_client::InferenceClient, open_shell_client::OpenShellClient, + UpdateConfigRequest, open_shell_client::OpenShellClient, }; use crate::sandbox_env; use miette::{IntoDiagnostic, Result, WrapErr}; @@ -681,12 +680,6 @@ async fn connect(endpoint: &str) -> Result> { Ok(OpenShellClient::new(channel)) } -/// Connect to the inference service. -async fn connect_inference(endpoint: &str) -> Result> { - let channel = connect_channel(endpoint).await?; - Ok(InferenceClient::new(channel)) -} - /// Fetch sandbox policy from `OpenShell` server via gRPC. /// /// Returns `Ok(Some(policy))` when the server has a policy configured, @@ -828,38 +821,6 @@ pub async fn sync_policy_and_fetch_snapshot( fetch_settings_snapshot_with_client(&mut client, sandbox_id).await } -/// Fetch provider environment variables for a sandbox from `OpenShell` server via gRPC. -/// -/// Returns a map of environment variable names to values derived from provider -/// credentials configured on the sandbox. Returns an empty map if the sandbox -/// has no providers or the call fails. -pub async fn fetch_provider_environment( - endpoint: &str, - sandbox_id: &str, -) -> Result { - debug!(endpoint = %endpoint, sandbox_id = %sandbox_id, "Fetching provider environment"); - - let mut client = connect(endpoint).await?; - - let response = client - .get_sandbox_provider_environment(GetSandboxProviderEnvironmentRequest { - sandbox_id: sandbox_id.to_string(), - supports_static_credential_bindings: true, - }) - .await - .into_diagnostic()?; - - let inner = response.into_inner(); - Ok(ProviderEnvironmentResult { - environment: inner.environment, - provider_env_revision: inner.provider_env_revision, - credential_expires_at_ms: inner.credential_expires_at_ms, - dynamic_credentials: inner.dynamic_credentials, - static_credential_bindings: inner.static_credential_bindings, - non_secret_environment_keys: inner.non_secret_environment_keys, - }) -} - pub async fn exchange_provider_subject_token( endpoint: &str, sandbox_id: &str, @@ -911,19 +872,20 @@ fn provider_subject_token_exchange_status(status: Status) -> miette::Report { /// A reusable gRPC client for the `OpenShell` service. /// -/// Wraps a tonic channel connected once and reused for policy polling -/// and status reporting, avoiding per-request TLS handshake overhead. +/// Wraps a tonic channel connected once and reused for supervisor-triggered +/// status, analysis, credential refresh, and configuration-read RPCs. #[derive(Clone)] pub struct CachedOpenShellClient { client: OpenShellClient, workspace: Arc>, /// Extension credentials for this supervisor. Cloning the client shares - /// the store, so the middleware registry and the polling loop that rotates - /// it observe the same slots. + /// the store, so the middleware registry and credential refresh task + /// observe the same slots. extension_credentials: ExtensionCredentialStore, } -/// Settings poll result returned by [`CachedOpenShellClient::poll_settings`]. +/// Effective configuration representation shared by public reads and the +/// supervisor desired-state apply path. #[derive(Clone, Debug)] pub struct SettingsPollResult { pub policy: Option, @@ -945,7 +907,7 @@ pub struct SettingsPollResult { pub extension_authentication_enabled: bool, } -fn settings_poll_result(inner: crate::proto::GetSandboxConfigResponse) -> SettingsPollResult { +pub fn settings_poll_result(inner: crate::proto::SandboxConfigSnapshot) -> SettingsPollResult { SettingsPollResult { policy: inner.policy, version: inner.version, @@ -970,11 +932,11 @@ fn settings_poll_result(inner: crate::proto::GetSandboxConfigResponse) -> Settin mod settings_poll_tests { use super::settings_poll_result; use crate::PolicyValidationFailureMode; - use crate::proto::GetSandboxConfigResponse; + use crate::proto::SandboxConfigSnapshot; #[test] fn validation_failure_mode_round_trips_from_gateway_config() { - let result = settings_poll_result(GetSandboxConfigResponse { + let result = settings_poll_result(SandboxConfigSnapshot { policy_validation_failure_mode: "retain_last_valid".to_string(), ..Default::default() }); @@ -986,7 +948,7 @@ mod settings_poll_tests { #[test] fn unknown_validation_failure_mode_fails_closed() { - let result = settings_poll_result(GetSandboxConfigResponse { + let result = settings_poll_result(SandboxConfigSnapshot { policy_validation_failure_mode: "future_mode".to_string(), ..Default::default() }); @@ -998,26 +960,17 @@ mod settings_poll_tests { #[test] fn extension_authentication_capability_round_trips_and_defaults_disabled() { - let enabled = settings_poll_result(GetSandboxConfigResponse { + let enabled = settings_poll_result(SandboxConfigSnapshot { extension_authentication_enabled: true, ..Default::default() }); assert!(enabled.extension_authentication_enabled); - let legacy = settings_poll_result(GetSandboxConfigResponse::default()); + let legacy = settings_poll_result(SandboxConfigSnapshot::default()); assert!(!legacy.extension_authentication_enabled); } } -pub struct ProviderEnvironmentResult { - pub environment: HashMap, - pub provider_env_revision: u64, - pub credential_expires_at_ms: HashMap, - pub dynamic_credentials: HashMap, - pub static_credential_bindings: HashMap, - pub non_secret_environment_keys: Vec, -} - pub struct ProviderSubjectTokenExchangeResult { pub access_token: String, pub expires_in: i64, @@ -1031,15 +984,13 @@ impl CachedOpenShellClient { /// Connect while sharing an existing credential store. /// - /// The supervisor opens the gateway channel more than once (policy load, - /// then the polling loop). Both must observe the same slots, otherwise the - /// credentials handed to the middleware registry are not the ones the loop - /// rotates. + /// Configuration application and the independent credential refresh task + /// must observe the same slots as the installed middleware registry. pub async fn connect_with_credentials( endpoint: &str, extension_credentials: ExtensionCredentialStore, ) -> Result { - debug!(endpoint = %endpoint, "Connecting openshell gRPC client for policy polling"); + debug!(endpoint = %endpoint, "Connecting cached openshell gRPC client"); let client = connect(endpoint).await?; Ok(Self { client, @@ -1130,13 +1081,12 @@ impl CachedOpenShellClient { .map(drop) } - /// Returns the workspace learned from the server, or empty if not yet polled. + /// Returns the workspace learned from a configuration snapshot. pub fn workspace(&self) -> String { self.workspace.get().cloned().unwrap_or_default() } - /// Pre-seed the workspace without polling. The value is ignored if the - /// workspace was already learned from `poll_settings`. + /// Pre-seed the workspace from a session-delivered configuration snapshot. pub fn set_workspace(&self, workspace: String) { let _ = self.workspace.set(workspace); } @@ -1222,17 +1172,3 @@ impl CachedOpenShellClient { Ok(()) } } - -/// Fetch the resolved inference route bundle from the server. -pub async fn fetch_inference_bundle(endpoint: &str) -> Result { - debug!(endpoint = %endpoint, "Fetching inference route bundle"); - - let mut client = connect_inference(endpoint).await?; - - let response = client - .get_inference_bundle(GetInferenceBundleRequest {}) - .await - .into_diagnostic()?; - - Ok(response.into_inner()) -} diff --git a/crates/openshell-core/src/proposals.rs b/crates/openshell-core/src/proposals.rs index 53586629ca..e6d8171992 100644 --- a/crates/openshell-core/src/proposals.rs +++ b/crates/openshell-core/src/proposals.rs @@ -4,7 +4,7 @@ //! Shared state controlling agent-driven policy proposals. //! //! Initialised once during sandbox start from the `agent_policy_proposals_enabled` -//! setting and updated by the policy poll loop or authoritative sidecar control +//! setting and updated by desired-state delivery or authoritative sidecar control //! when the setting changes. Read by the `policy.local` route handler and by //! the skills installer to gate the agent-controlled mutation surface. @@ -15,7 +15,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; /// /// Clones point at the same atomic value, so the sandbox orchestrator can pass /// this into the process and network supervisors and then update it from the -/// settings poll loop or sidecar control. +/// desired-state handler or sidecar control. #[derive(Clone, Debug)] pub struct AgentProposals { enabled: Arc, diff --git a/crates/openshell-core/src/provider_credentials.rs b/crates/openshell-core/src/provider_credentials.rs index 2b1537a21b..f8c49cf901 100644 --- a/crates/openshell-core/src/provider_credentials.rs +++ b/crates/openshell-core/src/provider_credentials.rs @@ -500,9 +500,10 @@ impl ProviderCredentialState { /// Install one gateway provider-environment snapshot. /// /// Callers must serialize this operation with other bound-environment - /// installs and revocations. The sandbox settings refresh loop is the sole - /// writer today. The internal lock makes each mutation memory-safe, but it - /// does not establish revision ordering between concurrent snapshots. + /// installs and revocations. The sandbox's provider desired-state handler + /// is the sole writer today. The internal lock makes each mutation + /// memory-safe, but it does not establish revision ordering between + /// concurrent snapshots. pub fn install_bound_environment( &self, revision: u64, diff --git a/crates/openshell-core/src/settings.rs b/crates/openshell-core/src/settings.rs index 156e4c3845..30881673c9 100644 --- a/crates/openshell-core/src/settings.rs +++ b/crates/openshell-core/src/settings.rs @@ -69,7 +69,8 @@ impl RegisteredSetting { /// (supervisor). No database migration is needed -- new keys are stored in /// the existing settings JSON blob. /// 3. Add sandbox-side consumption in `openshell-sandbox` to read and act on -/// the new key from the poll loop's `SettingsPollResult::settings` map. +/// the new key from `SandboxConfigSnapshot.settings` during desired-state +/// application. /// 4. The key will automatically appear in `settings get` (CLI/TUI) and be /// settable via `settings set`. The server validates that only registered /// keys are accepted. diff --git a/crates/openshell-driver-kubernetes/README.md b/crates/openshell-driver-kubernetes/README.md index b352f6efe9..c0c3ef3431 100644 --- a/crates/openshell-driver-kubernetes/README.md +++ b/crates/openshell-driver-kubernetes/README.md @@ -45,7 +45,7 @@ not a tenant isolation boundary. The gateway stores platform state and delegates sandbox workload creation to this driver. Kubernetes owns scheduling and pod lifecycle. The `openshell-sandbox` supervisor inside each workload owns agent isolation, -credential injection, policy polling, logs, and the gateway relay. +credential injection, desired-state application, logs, and the gateway relay. ## Sandbox Resource diff --git a/crates/openshell-extension-core/src/store.rs b/crates/openshell-extension-core/src/store.rs index 2e6b94be25..10c78d6085 100644 --- a/crates/openshell-extension-core/src/store.rs +++ b/crates/openshell-extension-core/src/store.rs @@ -26,7 +26,7 @@ struct Entry { /// Per-service extension credentials held by one supervisor. /// /// Cloning shares the underlying map, so the registry's middleware clients and -/// the polling loop that rotates them observe the same slots. Ownership is +/// the credential refresh task that rotates them observe the same slots. Ownership is /// explicit rather than process-global: tests construct independent stores and /// cannot interfere with each other. #[derive(Clone, Default)] diff --git a/crates/openshell-gateway-interceptors/src/proto_json.rs b/crates/openshell-gateway-interceptors/src/proto_json.rs index f6aecbcf67..25ffb6d244 100644 --- a/crates/openshell-gateway-interceptors/src/proto_json.rs +++ b/crates/openshell-gateway-interceptors/src/proto_json.rs @@ -381,10 +381,7 @@ mod tests { "material", ), ("openshell.v1.ConfigureProviderRefreshRequest", "material"), - ( - "openshell.v1.GetSandboxProviderEnvironmentResponse", - "environment", - ), + ("openshell.v1.ProviderEnvironmentSnapshot", "environment"), ] { let message = codec.message_descriptor(message_name).unwrap(); let field = message.get_field_by_name(field_name).unwrap(); diff --git a/crates/openshell-sandbox/src/lib.rs b/crates/openshell-sandbox/src/lib.rs index 100b3d2c77..7942cb8216 100644 --- a/crates/openshell-sandbox/src/lib.rs +++ b/crates/openshell-sandbox/src/lib.rs @@ -18,9 +18,7 @@ use miette::{IntoDiagnostic, Result, WrapErr}; use std::future::Future; use std::pin::Pin; use std::sync::Arc; -#[cfg(target_os = "linux")] -use std::sync::atomic::Ordering; -use std::sync::atomic::{AtomicBool, AtomicU32}; +use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}; use std::time::Duration; use tracing::{debug, info, warn}; @@ -68,13 +66,127 @@ use openshell_supervisor_process::process::ProcessEnforcementMode; pub use openshell_supervisor_process::process::{ProcessHandle, ProcessStatus}; use openshell_supervisor_process::skills; use tokio::sync::mpsc::UnboundedSender; -#[cfg(any(test, target_os = "linux"))] use tokio::time::timeout; const SIDECAR_NETWORK_ENFORCEMENT_MODE: &str = "sidecar-nftables"; const SIDECAR_TLS_DIR: &str = openshell_core::container_paths::SIDECAR_TLS_DIR; const SIDECAR_CA_CERT: &str = "openshell-ca.pem"; const SIDECAR_CA_BUNDLE: &str = "ca-bundle.pem"; +const GATEWAY_BOOTSTRAP_WAIT: Duration = Duration::from_secs(120); + +struct GatewaySession { + requests: Option< + tokio::sync::mpsc::Receiver< + openshell_supervisor_process::supervisor_session::DesiredStateRequest, + >, + >, + runtime_ready: openshell_supervisor_process::supervisor_session::RuntimeReadySender, + task: tokio::task::JoinHandle<()>, + terminating: Arc, +} + +impl Drop for GatewaySession { + fn drop(&mut self) { + self.terminating.store(true, Ordering::Release); + self.task.abort(); + } +} + +struct BootstrapReply { + sender: Option>, +} + +impl BootstrapReply { + fn ready( + mut self, + sandbox_config_outcome: openshell_core::proto::ConfigApplyOutcome, + provider_environment_outcome: openshell_core::proto::ConfigApplyOutcome, + inference_bundle_outcome: openshell_core::proto::ConfigApplyOutcome, + ) { + if let Some(sender) = self.sender.take() { + let _ = sender.send(openshell_core::proto::ConfigBootstrapResult { + status: openshell_core::proto::ConfigBootstrapStatus::Ready as i32, + error: String::new(), + sandbox_config_outcome: sandbox_config_outcome as i32, + provider_environment_outcome: provider_environment_outcome as i32, + inference_bundle_outcome: inference_bundle_outcome as i32, + }); + } + } +} + +impl Drop for BootstrapReply { + fn drop(&mut self) { + if let Some(sender) = self.sender.take() { + let _ = sender.send(openshell_core::proto::ConfigBootstrapResult { + status: openshell_core::proto::ConfigBootstrapStatus::Failed as i32, + error: "supervisor bootstrap aborted during runtime initialization".to_string(), + sandbox_config_outcome: openshell_core::proto::ConfigApplyOutcome::Failed as i32, + provider_environment_outcome: openshell_core::proto::ConfigApplyOutcome::Failed + as i32, + inference_bundle_outcome: openshell_core::proto::ConfigApplyOutcome::Failed as i32, + }); + } + } +} + +async fn start_gateway_session( + endpoint: String, + sandbox_id: String, +) -> Result<( + GatewaySession, + openshell_core::proto::ConfigBootstrap, + BootstrapReply, +)> { + use openshell_supervisor_process::supervisor_session::DesiredStateRequest; + + let terminating = Arc::new(AtomicBool::new(false)); + let (desired_state, mut requests) = tokio::sync::mpsc::channel(8); + let (runtime_ready, runtime_ready_rx) = tokio::sync::watch::channel(None); + let task = openshell_supervisor_process::supervisor_session::spawn( + endpoint, + sandbox_id, + Arc::clone(&terminating), + desired_state, + runtime_ready_rx, + ); + let request = match timeout(GATEWAY_BOOTSTRAP_WAIT, requests.recv()).await { + Ok(Some(request)) => request, + Ok(None) => { + terminating.store(true, Ordering::Release); + task.abort(); + return Err(miette::miette!( + "supervisor session ended before configuration bootstrap" + )); + } + Err(_) => { + terminating.store(true, Ordering::Release); + task.abort(); + return Err(miette::miette!( + "timed out waiting for gateway configuration bootstrap" + )); + } + }; + let DesiredStateRequest::Bootstrap { bootstrap, result } = request else { + terminating.store(true, Ordering::Release); + task.abort(); + return Err(miette::miette!( + "supervisor session sent a config update before bootstrap" + )); + }; + Ok(( + GatewaySession { + requests: Some(requests), + runtime_ready, + task, + terminating, + }, + *bootstrap, + BootstrapReply { + sender: Some(result), + }, + )) +} #[cfg(any(test, target_os = "linux"))] fn has_network_runtime_capability(capabilities: Option<&str>, required: &str) -> bool { @@ -167,9 +279,30 @@ pub async fn run_sandbox( None }; + let (mut gateway_session, gateway_bootstrap, mut bootstrap_reply) = + if process_uses_sidecar_control { + (None, None, None) + } else if let (Some(endpoint), Some(id)) = + (openshell_endpoint.as_ref(), sandbox_id.as_ref()) + { + let (session, bootstrap, reply) = + start_gateway_session(endpoint.clone(), id.clone()).await?; + if bootstrap.sandbox_config.is_none() + || bootstrap.provider_environment.is_none() + || bootstrap.inference_bundle.is_none() + { + return Err(miette::miette!( + "incompatible gateway: ConfigBootstrap is incomplete" + )); + } + (Some(session), Some(bootstrap), Some(reply)) + } else { + (None, None, None) + }; + // Extension credentials are owned by this supervisor and shared by every // gateway connection it opens, so the middleware registry's bearer slots - // and the policy poll loop that rotates them stay the same objects. + // and the credential refresh task stay the same objects. let extension_credentials = openshell_extension_core::ExtensionCredentialStore::new(); // Load policy and initialize OPA engine @@ -203,9 +336,14 @@ pub async fn run_sandbox( policy_rules, policy_data, &extension_credentials, + gateway_bootstrap + .as_ref() + .and_then(|bootstrap| bootstrap.sandbox_config.clone()), ) .await? }; + #[cfg(not(test))] + let _ = initial_extension_authentication_enabled; // Normalize the active driver's identity contract once, while both the // policy and launched image filesystem are available. Kubernetes and @@ -236,6 +374,7 @@ pub async fn run_sandbox( openshell_supervisor_process::process::ResolvedWorkspace::new(workdir.clone(), false), ); + let mut provider_bootstrap_degraded = false; #[cfg_attr(not(target_os = "linux"), allow(unused_mut))] let (provider_credentials, mut provider_env) = if let Some(bootstrap) = sidecar_bootstrap.as_ref() @@ -256,50 +395,22 @@ pub async fn run_sandbox( dynamic_credentials, static_credential_bindings, non_secret_environment_keys, - ) = if let (Some(id), Some(endpoint)) = (&sandbox_id, &openshell_endpoint) { - match openshell_core::grpc_client::fetch_provider_environment(endpoint, id).await { - Ok(result) => { - ocsf_emit!( - ConfigStateChangeBuilder::new(ocsf_ctx()) - .severity(SeverityId::Informational) - .status(StatusId::Success) - .state(StateId::Enabled, "loaded") - .message(format!( - "Fetched provider environment [env_count:{}]", - result.environment.len() - )) - .build() - ); - ( - result.provider_env_revision, - result.environment, - result.credential_expires_at_ms, - result.dynamic_credentials, - result.static_credential_bindings, - result.non_secret_environment_keys, - ) - } - Err(e) => { - ocsf_emit!( - ConfigStateChangeBuilder::new(ocsf_ctx()) - .severity(SeverityId::High) - .status(StatusId::Failure) - .state(StateId::Disabled, "fail_closed") - .message(format!( - "Failed to fetch provider environment; no provider credentials are active: {e}" - )) - .build() - ); - ( - 0, - std::collections::HashMap::new(), - std::collections::HashMap::new(), - std::collections::HashMap::new(), - std::collections::HashMap::new(), - Vec::new(), - ) - } - } + ) = if let Some(snapshot) = gateway_bootstrap + .as_ref() + .and_then(|bootstrap| bootstrap.provider_environment.clone()) + { + ( + snapshot.provider_env_revision, + snapshot.environment, + snapshot.credential_expires_at_ms, + snapshot.dynamic_credentials, + snapshot.static_credential_bindings, + snapshot.non_secret_environment_keys, + ) + } else if sandbox_id.is_some() && openshell_endpoint.is_some() { + return Err(miette::miette!( + "gateway-backed supervisor startup is missing its provider bootstrap snapshot" + )); } else { ( 0, @@ -322,6 +433,7 @@ pub async fn run_sandbox( ) { Ok(credentials) => credentials, Err(error) => { + provider_bootstrap_degraded = true; ocsf_emit!( ConfigStateChangeBuilder::new(ocsf_ctx()) .severity(SeverityId::High) @@ -358,7 +470,7 @@ pub async fn run_sandbox( // Shared agent-proposals feature flag. Seed from the same initial settings // snapshot that produced the policy so networking and process setup agree - // before the poll loop starts reconciling later changes. + // before later desired-state updates arrive. let agent_proposals = AgentProposals::new(initial_agent_proposals_enabled); let process_control_writer = process_control_connection @@ -511,8 +623,8 @@ pub async fn run_sandbox( #[cfg(not(target_os = "linux"))] drop(bypass_activity_tx); - // Workspace watch: the policy poll loop learns the workspace from - // GetSandboxConfig and broadcasts it. Flush tasks and the policy.local + // Workspace watch: the session-delivered config supplies the workspace. + // Flush tasks and the policy.local // API read the current value so proposals target the correct workspace. let (workspace_tx, workspace_rx) = tokio::sync::watch::channel(String::new()); @@ -537,6 +649,9 @@ pub async fn run_sandbox( sandbox_name_for_agg.as_deref(), openshell_endpoint_for_proxy.as_deref(), inference_routes.as_deref(), + gateway_bootstrap + .as_ref() + .and_then(|bootstrap| bootstrap.inference_bundle.clone()), denial_tx, activity_tx, agent_proposals.clone(), @@ -619,6 +734,9 @@ pub async fn run_sandbox( sandbox_id: sandbox_id.clone(), trusted_ssh_socket_path: std::path::PathBuf::from(trusted_ssh_socket_path), control_publisher: sidecar_control_publisher.clone(), + runtime_ready: gateway_session + .as_ref() + .map(|session| session.runtime_ready.clone()), }, ); } @@ -721,40 +839,53 @@ pub async fn run_sandbox( }); } - // Spawn background policy poll task (gRPC mode only). - if !process_uses_sidecar_control - && let (Some(id), Some(endpoint), Some(engine)) = ( - sandbox_id.as_deref(), - openshell_endpoint.as_deref(), - opa_engine.as_ref(), + if let Some(session) = gateway_session.as_mut() { + let config = gateway_bootstrap + .as_ref() + .and_then(|bootstrap| bootstrap.sandbox_config.as_ref()) + .ok_or_else(|| miette::miette!("gateway bootstrap is missing sandbox config"))?; + let inference_revision = gateway_bootstrap + .as_ref() + .and_then(|bootstrap| bootstrap.inference_bundle.as_ref()) + .map(|bundle| bundle.revision.clone()) + .ok_or_else(|| miette::miette!("gateway bootstrap is missing inference bundle"))?; + let engine = opa_engine + .as_ref() + .ok_or_else(|| miette::miette!("gateway config did not initialize an OPA engine"))? + .clone(); + let endpoint = openshell_endpoint + .as_deref() + .ok_or_else(|| miette::miette!("gateway session is missing its endpoint"))?; + let id = sandbox_id + .as_deref() + .ok_or_else(|| miette::miette!("gateway session is missing its sandbox id"))?; + let client = openshell_core::grpc_client::CachedOpenShellClient::connect_with_credentials( + endpoint, + extension_credentials.clone(), ) - { - let poll_id = id.to_string(); - let poll_endpoint = endpoint.to_string(); - let poll_engine = engine.clone(); - let poll_ocsf_enabled = ocsf_enabled.clone(); - let poll_pid = entrypoint_pid.clone(); - let poll_provider_credentials = provider_credentials.clone(); - let poll_policy_local = networking.as_ref().map(|n| n.policy_local_ctx.clone()); - let poll_interval_secs: u64 = std::env::var("OPENSHELL_POLICY_POLL_INTERVAL_SECS") - .ok() - .and_then(|v| v.parse().ok()) - .unwrap_or(10); - let poll_ctx = PolicyPollLoopContext { - endpoint: poll_endpoint, - sandbox_id: poll_id, - opa_engine: poll_engine, + .await?; + let requests = session + .requests + .take() + .ok_or_else(|| miette::miette!("desired-state request loop already started"))?; + let local_policy_override = !loaded_policy_origin.allows_gateway_policy_reload(); + let local_inference_override = inference_routes.is_some(); + let ctx = DesiredStateContext { + sandbox_id: id.to_string(), + opa_engine: engine, loaded_policy_origin, - entrypoint_pid: poll_pid, - interval_secs: poll_interval_secs, - ocsf_enabled: poll_ocsf_enabled, - provider_credentials: poll_provider_credentials, - policy_local_ctx: poll_policy_local, + entrypoint_pid: entrypoint_pid.clone(), + #[cfg(test)] + interval_secs: 0, + ocsf_enabled: ocsf_enabled.clone(), + provider_credentials: provider_credentials.clone(), + policy_local_ctx: networking.as_ref().map(|n| n.policy_local_ctx.clone()), agent_proposals: agent_proposals.clone(), middleware_registry_status, sidecar_control_publisher: sidecar_control_publisher.clone(), workspace_tx, extension_credentials: extension_credentials.clone(), + #[cfg(test)] extension_authentication_enabled: initial_extension_authentication_enabled, middleware_connector: default_middleware_connector(), transparent_tcp: TransparentTcpReloadState { @@ -762,19 +893,42 @@ pub async fn run_sandbox( substrate_ready: transparent_tcp_substrate_ready, }, }; + let runtime = DesiredStateRuntime::new( + ctx, + client, + config, + inference_revision, + networking + .as_ref() + .and_then(|networking| networking.inference_context.clone()), + local_inference_override, + ); + tokio::spawn(run_desired_state_loop(requests, runtime)); - tokio::spawn(async move { - if let Err(e) = run_policy_poll_loop(poll_ctx).await { - ocsf_emit!( - AppLifecycleBuilder::new(ocsf_ctx()) - .activity(ActivityId::Fail) - .severity(SeverityId::Medium) - .status(StatusId::Failure) - .message(format!("Policy poll loop exited with error: {e}")) - .build() - ); - } - }); + let config_outcome = if local_policy_override { + openshell_core::proto::ConfigApplyOutcome::RetainedLocalOverride + } else { + openshell_core::proto::ConfigApplyOutcome::Applied + }; + let provider_outcome = if provider_bootstrap_degraded { + openshell_core::proto::ConfigApplyOutcome::Degraded + } else { + openshell_core::proto::ConfigApplyOutcome::Applied + }; + let inference_outcome = if networking + .as_ref() + .is_some_and(|networking| networking.inference_degraded) + { + openshell_core::proto::ConfigApplyOutcome::Degraded + } else if local_inference_override { + openshell_core::proto::ConfigApplyOutcome::RetainedLocalOverride + } else { + openshell_core::proto::ConfigApplyOutcome::Applied + }; + bootstrap_reply + .take() + .ok_or_else(|| miette::miette!("gateway bootstrap result already sent"))? + .ready(config_outcome, provider_outcome, inference_outcome); } // Start GCE metadata loopback server inside the network namespace so @@ -940,6 +1094,9 @@ pub async fn run_sandbox( main_env, ca_file_paths, agent_proposals.clone(), + gateway_session + .as_ref() + .map(|session| session.runtime_ready.clone()), #[cfg(target_os = "linux")] netns.as_ref(), #[cfg(target_os = "linux")] @@ -1295,6 +1452,7 @@ struct SidecarEntrypointHandler { sandbox_id: Option, trusted_ssh_socket_path: std::path::PathBuf, control_publisher: Option, + runtime_ready: Option, } #[cfg(target_os = "linux")] @@ -1311,13 +1469,15 @@ fn spawn_sidecar_entrypoint_handler( sandbox_id, trusted_ssh_socket_path, control_publisher, + runtime_ready, } = handler; - let mut session_started = false; + let mut runtime_ready_sent = false; let mut trusted_supervisor_pid = None; - let terminating = Arc::new(AtomicBool::new(false)); while let Some(started) = entrypoint_rx.recv().await { if let Some(exit_code) = started.exit_code { - terminating.store(true, Ordering::Release); + if let Some(runtime_ready) = runtime_ready.as_ref() { + let _ = runtime_ready.send(None); + } if let (Some(endpoint), Some(id)) = (openshell_endpoint.as_ref(), sandbox_id.as_ref()) { @@ -1374,11 +1534,7 @@ fn spawn_sidecar_entrypoint_handler( } } - if started.start_session - && !session_started - && let (Some(endpoint), Some(id)) = - (openshell_endpoint.as_ref(), sandbox_id.as_ref()) - { + if started.start_session && !runtime_ready_sent { let Some(supervisor_pid) = trusted_supervisor_pid else { warn!( pid = started.pid, @@ -1386,20 +1542,24 @@ fn spawn_sidecar_entrypoint_handler( ); continue; }; - openshell_supervisor_process::supervisor_session::spawn( - endpoint.clone(), - id.clone(), - trusted_ssh_socket_path.clone(), - None, - Some(supervisor_pid), - Arc::clone(&terminating), - started.instance_id.clone(), - ); - session_started = true; - info!("sidecar supervisor session task spawned"); + if let Some(runtime_ready) = runtime_ready.as_ref() + && runtime_ready + .send(Some( + openshell_supervisor_process::supervisor_session::RuntimeReadyState { + instance_id: started.instance_id.clone(), + ssh_socket_path: trusted_ssh_socket_path.clone(), + netns_fd: None, + expected_ssh_peer_pid: Some(supervisor_pid), + }, + )) + .is_err() + { + warn!("supervisor session runtime-ready channel closed"); + } + runtime_ready_sent = true; + info!("sidecar supervisor runtime marked ready"); } } - terminating.store(true, Ordering::Release); }); } @@ -2246,6 +2406,7 @@ async fn load_policy( policy_rules: Option, policy_data: Option, extension_credentials: &openshell_extension_core::ExtensionCredentialStore, + gateway_snapshot: Option, ) -> Result<( SandboxPolicy, Option>, @@ -2314,10 +2475,14 @@ async fn load_policy( endpoint = %endpoint, "Fetching sandbox policy via gRPC" ); - let mut snapshot = grpc_retry("Policy fetch", || { - openshell_core::grpc_client::fetch_settings_snapshot(endpoint, id) - }) - .await?; + let mut snapshot = if let Some(snapshot) = gateway_snapshot { + openshell_core::grpc_client::settings_poll_result(snapshot) + } else { + grpc_retry("Policy fetch", || { + openshell_core::grpc_client::fetch_settings_snapshot(endpoint, id) + }) + .await? + }; let mut proto_policy = if let Some(p) = snapshot.policy.clone() { p @@ -2460,8 +2625,8 @@ async fn load_policy( // Connect operator-registered middleware services. A connect/describe // failure keeps the built-in registry active so each request's - // `on_error` policy governs matched traffic. The policy poll loop - // retries the install without waiting for a config change. + // `on_error` policy governs matched traffic. Desired-state + // reconciliation retries the same config snapshot. let middleware_services = snapshot.supervisor_middleware_services.clone(); let middleware_registry_status = if middleware_services.is_empty() { MiddlewareRegistryStatus::Synchronized @@ -2472,7 +2637,7 @@ async fn load_policy( async move { let credentials = if extension_authentication_enabled { // Share the supervisor's store so the slots installed here - // are the ones the policy poll loop later rotates in place. + // are the ones the credential refresh task rotates in place. openshell_core::grpc_client::CachedOpenShellClient::connect_with_credentials( endpoint, extension_credentials, @@ -2667,6 +2832,7 @@ enum GatewayRuntimeReloadError { MiddlewareRegistry(miette::Report), } +#[cfg(test)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum GatewayRuntimeFailureClass { PolicyValidation, @@ -2675,6 +2841,7 @@ enum GatewayRuntimeFailureClass { } impl GatewayRuntimeReloadError { + #[cfg(test)] fn class(&self) -> GatewayRuntimeFailureClass { match self { Self::PolicyValidation(_) => GatewayRuntimeFailureClass::PolicyValidation, @@ -2684,8 +2851,17 @@ impl GatewayRuntimeReloadError { Self::MiddlewareRegistry(_) => GatewayRuntimeFailureClass::MiddlewareRegistry, } } + + fn message(&self) -> String { + match self { + Self::PolicyValidation(error) + | Self::TransparentTcpPrerequisite(error) + | Self::MiddlewareRegistry(error) => error.to_string(), + } + } } +#[cfg(test)] #[derive(Debug, PartialEq, Eq)] struct FailedRuntimeRevision { config_revision: u64, @@ -2693,6 +2869,7 @@ struct FailedRuntimeRevision { failure_class: GatewayRuntimeFailureClass, } +#[cfg(test)] impl FailedRuntimeRevision { fn new(config_revision: u64, policy_hash: &str, failure: &GatewayRuntimeReloadError) -> Self { Self { @@ -2789,6 +2966,7 @@ fn middleware_registry_needs_rebuild( || current_services != desired_services } +#[cfg(test)] fn gateway_policy_runtime_needs_reconciliation( reloads_gateway_policy: bool, current_policy_hash: &str, @@ -2819,10 +2997,9 @@ struct LoadedPolicyRevision { /// /// A missing gateway revision means the policy was loaded from the gateway but /// could not be bound to an authoritative snapshot (for example, enrichment -/// sync failed). That state must reconcile on the first successful poll. A +/// sync failed). That state must reconcile on the next delivered snapshot. A /// local-file override is different: gateway policy revisions are observed for -/// settings/provider refreshes but must never replace the explicit local OPA -/// policy. +/// reconciliation but must never replace the explicit local OPA policy. #[derive(Clone, Debug, PartialEq, Eq)] enum LoadedPolicyOrigin { LocalOverride, @@ -2837,6 +3014,7 @@ impl LoadedPolicyOrigin { matches!(self, Self::Gateway { .. }) } + #[cfg(test)] fn has_last_valid_policy(&self) -> bool { match self { Self::LocalOverride => true, @@ -2861,6 +3039,7 @@ impl LoadedPolicyRevision { /// A sandbox-scoped policy revision that was constructed successfully at /// startup and must be acknowledged to the gateway exactly once. +#[cfg(test)] #[derive(Clone, Debug, PartialEq, Eq)] struct InitialPolicyAck { version: u32, @@ -2868,6 +3047,7 @@ struct InitialPolicyAck { config_revision: u64, } +#[cfg(test)] #[derive(Clone, Debug, PartialEq, Eq)] struct PolicyStatusUpdate { version: u32, @@ -2876,12 +3056,14 @@ struct PolicyStatusUpdate { success_event: Option, } +#[cfg(test)] #[derive(Clone, Debug, PartialEq, Eq)] enum PolicyStatusSuccessEvent { InitialAcknowledgement { policy_hash: String }, UnchangedAcknowledgement { policy_hash: String }, } +#[cfg(test)] impl PolicyStatusUpdate { fn initial_loaded(ack: &InitialPolicyAck) -> Self { Self { @@ -2922,6 +3104,7 @@ impl PolicyStatusUpdate { } } +#[cfg(test)] #[derive(Clone, Debug, PartialEq, Eq)] enum InitialPollDisposition { Acknowledge(InitialPolicyAck), @@ -2937,6 +3120,7 @@ enum InitialPollDisposition { /// policies, local-file development policies, version zero, and changed /// identities yield `None`, so those paths never emit a sandbox-revision /// acknowledgement. +#[cfg(test)] fn initial_policy_ack_candidate( loaded: Option<&LoadedPolicyRevision>, canonical: &openshell_core::grpc_client::SettingsPollResult, @@ -2963,6 +3147,7 @@ fn initial_policy_ack_candidate( }) } +#[cfg(test)] fn initial_poll_disposition( origin: &LoadedPolicyOrigin, canonical: &openshell_core::grpc_client::SettingsPollResult, @@ -2978,6 +3163,7 @@ fn initial_poll_disposition( } } +#[cfg(test)] fn unchanged_policy_revision_candidate( reloads_gateway_policy: bool, recovering_rejected_policy: bool, @@ -2994,6 +3180,7 @@ fn unchanged_policy_revision_candidate( .then_some(result.version) } +#[cfg(test)] fn unchanged_policy_revision_ready_to_ack( candidate: Option, policy_runtime_changed: bool, @@ -3064,6 +3251,7 @@ fn report_credential_gating_unavailable() { /// The channel is FIFO, so a delayed older status can never arrive after a /// newer status and move the gateway's active version backward. Delivery uses /// the existing bounded retry, but failures never delay policy enforcement. +#[cfg(test)] #[tonic::async_trait] trait PolicyGatewayClient: Clone + Send + Sync + 'static { async fn poll_settings( @@ -3079,6 +3267,13 @@ trait PolicyGatewayClient: Clone + Send + Sync + 'static { error: &str, ) -> Result<()>; + async fn provider_environment( + &self, + _sandbox_id: &str, + ) -> Result { + Ok(openshell_core::proto::ProviderEnvironmentSnapshot::default()) + } + async fn refresh_installed_extension_credentials(&self) -> Result<()> { Ok(()) } @@ -3093,6 +3288,7 @@ trait PolicyGatewayClient: Clone + Send + Sync + 'static { fn workspace(&self) -> String; } +#[cfg(test)] #[tonic::async_trait] impl PolicyGatewayClient for openshell_core::grpc_client::CachedOpenShellClient { async fn poll_settings( @@ -3129,6 +3325,7 @@ impl PolicyGatewayClient for openshell_core::grpc_client::CachedOpenShellClient } } +#[cfg(test)] async fn run_policy_status_reporter( client: C, sandbox_id: String, @@ -3209,6 +3406,7 @@ async fn run_policy_status_reporter( } } +#[cfg(test)] fn enqueue_policy_status(sender: &UnboundedSender, update: PolicyStatusUpdate) { let version = update.version; if let Err(error) = sender.send(update) { @@ -3261,23 +3459,16 @@ async fn report_initial_policy_failure( } } -/// Background loop that polls the server for policy updates. -/// -/// When a new version is detected, attempts to reload the OPA engine via -/// `reload_from_proto_with_pid()`. Reports load success/failure back to the -/// server. On failure, the previous engine is untouched (LKG behavior). -/// -/// When the entrypoint PID is available, policy reloads include symlink -/// resolution for binary paths via the container filesystem. -struct PolicyPollLoopContext { - endpoint: String, +/// Shared inputs used to apply desired-state snapshots to the live runtime. +struct DesiredStateContext { sandbox_id: String, opa_engine: Arc, /// Source of the policy currently loaded into OPA. This distinguishes an /// explicit local-file override from an unbound gateway revision so the - /// former is never replaced by policy polling. + /// former is never replaced by a gateway-delivered policy snapshot. loaded_policy_origin: LoadedPolicyOrigin, entrypoint_pid: Arc, + #[cfg(test)] interval_secs: u64, ocsf_enabled: Arc, provider_credentials: ProviderCredentialState, @@ -3287,12 +3478,508 @@ struct PolicyPollLoopContext { sidecar_control_publisher: Option, workspace_tx: tokio::sync::watch::Sender, extension_credentials: openshell_extension_core::ExtensionCredentialStore, + #[cfg(test)] extension_authentication_enabled: bool, middleware_connector: MiddlewareConnector, /// Immutable driver capability and startup substrate state. transparent_tcp: TransparentTcpReloadState, } +struct DesiredStateRuntime { + ctx: DesiredStateContext, + client: openshell_core::grpc_client::CachedOpenShellClient, + config: tokio::sync::Mutex, + provider: tokio::sync::Mutex, + inference: tokio::sync::Mutex, + component_sequences: [AtomicU64; 3], +} + +struct ConfigDesiredState { + current_config_revision: u64, + current_policy_version: u32, + current_policy_hash: String, + current_middleware_services: Vec, + current_extension_authentication_enabled: bool, + current_settings: std::collections::HashMap, + middleware_registry_status: MiddlewareRegistryStatus, +} + +struct ProviderDesiredState { + current_revision: u64, +} + +struct InferenceDesiredState { + inference_context: Option>, + local_inference_override: bool, + current_revision: String, +} + +impl DesiredStateRuntime { + fn new( + ctx: DesiredStateContext, + client: openshell_core::grpc_client::CachedOpenShellClient, + config: &openshell_core::proto::SandboxConfigSnapshot, + inference_revision: String, + inference_context: Option>, + local_inference_override: bool, + ) -> Self { + let middleware_registry_status = ctx.middleware_registry_status; + client.set_workspace(config.workspace.clone()); + Self { + config: tokio::sync::Mutex::new(ConfigDesiredState { + current_config_revision: config.config_revision, + current_policy_version: config.version, + current_policy_hash: config.policy_hash.clone(), + current_middleware_services: config.supervisor_middleware_services.clone(), + current_extension_authentication_enabled: config.extension_authentication_enabled, + current_settings: config.settings.clone(), + middleware_registry_status, + }), + provider: tokio::sync::Mutex::new(ProviderDesiredState { + current_revision: ctx.provider_credentials.snapshot().revision, + }), + inference: tokio::sync::Mutex::new(InferenceDesiredState { + current_revision: inference_revision, + inference_context, + local_inference_override, + }), + component_sequences: std::array::from_fn(|_| AtomicU64::new(0)), + ctx, + client, + } + } + + async fn apply_config( + &self, + snapshot: openshell_core::proto::SandboxConfigSnapshot, + ) -> std::result::Result { + use openshell_core::proto::{ConfigApplyOutcome, PolicySource}; + use std::sync::atomic::Ordering; + + let mut state = self.config.lock().await; + + if snapshot.config_revision == state.current_config_revision { + return Ok(ConfigApplyOutcome::IgnoredDuplicate); + } + + let result = openshell_core::grpc_client::settings_poll_result(snapshot); + let middleware_credentials = if result.extension_authentication_enabled { + self.client + .extension_credentials_for(&result.supervisor_middleware_services) + .await + .map_err(|error| error.to_string())? + } else { + std::collections::HashMap::new() + }; + let extension_authentication_changed = state.current_extension_authentication_enabled + != result.extension_authentication_enabled; + let middleware_registry_changed = extension_authentication_changed + || middleware_registry_needs_rebuild( + state.middleware_registry_status, + &state.current_middleware_services, + &result.supervisor_middleware_services, + ); + let local_override = !self.ctx.loaded_policy_origin.allows_gateway_policy_reload(); + + if local_override { + let ConfigDesiredState { + current_middleware_services, + middleware_registry_status, + .. + } = &mut *state; + reconcile_middleware_registry( + &self.ctx.opa_engine, + &self.ctx.middleware_connector, + MiddlewareRegistryReconciliation { + desired_services: &result.supervisor_middleware_services, + authentication: MiddlewareAuthentication { + credentials: middleware_credentials, + enabled: result.extension_authentication_enabled, + }, + registry_changed: middleware_registry_changed, + extension_credentials: &self.ctx.extension_credentials, + current_services: current_middleware_services, + status: middleware_registry_status, + }, + ) + .await; + if state.middleware_registry_status != MiddlewareRegistryStatus::Synchronized { + return Err("supervisor middleware registry update failed".to_string()); + } + } else { + let policy_changed = result.policy_hash != state.current_policy_hash; + if policy_changed || middleware_registry_changed { + let pid = self.ctx.entrypoint_pid.load(Ordering::Acquire); + if let Err(failure) = reload_gateway_policy_runtime( + &self.ctx.opa_engine, + result.policy.as_ref(), + pid, + MiddlewareReloadContext { + desired_services: &result.supervisor_middleware_services, + authentication: &MiddlewareAuthentication { + credentials: middleware_credentials, + enabled: result.extension_authentication_enabled, + }, + registry_changed: middleware_registry_changed, + connector: &self.ctx.middleware_connector, + }, + self.ctx.transparent_tcp, + ) + .await + { + let failure_message = failure.message(); + match apply_gateway_runtime_reload_failure( + &self.ctx.opa_engine, + failure, + result.policy_validation_failure_mode, + true, + result.version, + ) + .map_err(|error| error.to_string())? + { + GatewayRuntimeFailureDisposition::PolicyRejected { error, disposition } => { + emit_policy_validation_failure( + &disposition, + result.version, + &result.policy_hash, + &error, + ); + } + GatewayRuntimeFailureDisposition::TransparentTcpExpansionRejected { + error, + active_generation, + } => emit_transparent_tcp_expansion_rejection( + result.version, + &result.policy_hash, + active_generation, + &error, + ), + GatewayRuntimeFailureDisposition::MiddlewareUnavailable { error } => { + warn!(error = %error, "Supervisor middleware update failed; retaining the active registry"); + } + } + if result.version > 0 && result.policy_source == PolicySource::Sandbox { + let _ = self + .client + .report_policy_status( + &self.ctx.sandbox_id, + result.version, + false, + &failure_message, + ) + .await; + } + return Err(failure_message); + } + + if let Some(policy) = result.policy.as_ref() { + if let Some(policy_local_ctx) = self.ctx.policy_local_ctx.as_ref() { + policy_local_ctx.set_current_policy(policy.clone()).await; + } + if let Some(publisher) = self.ctx.sidecar_control_publisher.as_ref() { + publisher.publish_policy( + policy.clone(), + result.policy_hash.clone(), + result.config_revision, + ); + } + } + retain_extension_credentials( + &self.ctx.extension_credentials, + &result.supervisor_middleware_services, + result.extension_authentication_enabled, + ); + state.middleware_registry_status = MiddlewareRegistryStatus::Synchronized; + } + + if result.version > 0 && result.policy_source == PolicySource::Sandbox { + if let Err(error) = self + .client + .report_policy_status(&self.ctx.sandbox_id, result.version, true, "") + .await + { + warn!(error = %error, version = result.version, "Failed to report applied policy update"); + } + state.current_policy_version = result.version; + } + state.current_policy_hash.clone_from(&result.policy_hash); + state + .current_middleware_services + .clone_from(&result.supervisor_middleware_services); + } + + log_setting_changes(&state.current_settings, &result.settings); + apply_ocsf_json_setting(&self.ctx.ocsf_enabled, &result.settings); + apply_agent_proposals_enabled( + &self.ctx.agent_proposals, + agent_proposals_enabled_from_settings(&result.settings), + "desired-state update", + Some(result.config_revision), + self.ctx.sidecar_control_publisher.as_ref(), + skills::install_static_skills, + ); + let _ = self.ctx.workspace_tx.send(result.workspace); + state.current_config_revision = result.config_revision; + state.current_extension_authentication_enabled = result.extension_authentication_enabled; + state.current_settings = result.settings; + + Ok(if local_override { + ConfigApplyOutcome::RetainedLocalOverride + } else { + ConfigApplyOutcome::Applied + }) + } + + async fn apply_provider( + &self, + snapshot: openshell_core::proto::ProviderEnvironmentSnapshot, + ) -> std::result::Result { + use openshell_core::proto::ConfigApplyOutcome; + + let mut state = self.provider.lock().await; + if snapshot.provider_env_revision == state.current_revision { + return Ok(ConfigApplyOutcome::IgnoredDuplicate); + } + let revision = snapshot.provider_env_revision; + if !apply_provider_environment_snapshot( + &self.ctx.provider_credentials, + snapshot, + self.ctx.sidecar_control_publisher.as_ref(), + ) { + return Err("provider environment snapshot was rejected".to_string()); + } + state.current_revision = revision; + Ok(ConfigApplyOutcome::Applied) + } + + async fn apply_inference( + &self, + snapshot: openshell_core::proto::InferenceBundleSnapshot, + ) -> std::result::Result { + use openshell_core::proto::ConfigApplyOutcome; + + let mut state = self.inference.lock().await; + if state.local_inference_override { + return Ok(ConfigApplyOutcome::RetainedLocalOverride); + } + if snapshot.revision == state.current_revision { + return Ok(ConfigApplyOutcome::IgnoredDuplicate); + } + let revision = snapshot.revision.clone(); + if let Some(context) = state.inference_context.as_ref() { + openshell_supervisor_network::inference_routes::apply_inference_bundle( + &context.route_cache(), + &context.system_route_cache(), + snapshot, + ) + .await; + } + state.current_revision = revision; + Ok(ConfigApplyOutcome::Applied) + } + + async fn refresh_extension_credentials(&self) { + if let Err(error) = self.client.refresh_installed_extension_credentials().await { + warn!(error = %error, "Failed to refresh installed supervisor middleware credentials"); + } + } + + async fn apply_bootstrap( + &self, + bootstrap: openshell_core::proto::ConfigBootstrap, + ) -> openshell_core::proto::ConfigBootstrapResult { + use openshell_core::proto::{ConfigApplyOutcome, ConfigBootstrapStatus}; + + for sequence in &self.component_sequences { + sequence.store(0, Ordering::Release); + } + let config = async { + match bootstrap.sandbox_config { + Some(snapshot) => self.apply_config(snapshot).await, + None => Err("sandbox_config: snapshot is missing".to_string()), + } + }; + let provider = async { + match bootstrap.provider_environment { + Some(snapshot) => self.apply_provider(snapshot).await, + None => Err("provider_environment: snapshot is missing".to_string()), + } + }; + let inference = async { + match bootstrap.inference_bundle { + Some(snapshot) => self.apply_inference(snapshot).await, + None => Err("inference_bundle: snapshot is missing".to_string()), + } + }; + let (config, provider, inference) = tokio::join!(config, provider, inference); + let required_error = config.as_ref().err().cloned(); + let provider_error = provider.as_ref().err().cloned(); + let inference_error = inference.as_ref().err().cloned(); + let status = if required_error.is_some() { + ConfigBootstrapStatus::Failed + } else { + ConfigBootstrapStatus::Ready + }; + let error = [required_error, provider_error, inference_error] + .into_iter() + .flatten() + .collect::>() + .join("; "); + openshell_core::proto::ConfigBootstrapResult { + status: status as i32, + error: sanitize_desired_state_error(&error), + sandbox_config_outcome: config.unwrap_or(ConfigApplyOutcome::Failed) as i32, + provider_environment_outcome: provider.unwrap_or(ConfigApplyOutcome::Degraded) as i32, + inference_bundle_outcome: inference.unwrap_or(ConfigApplyOutcome::Degraded) as i32, + } + } + + async fn apply_update( + &self, + update: openshell_core::proto::ConfigUpdate, + ) -> openshell_core::proto::ConfigUpdateResult { + use openshell_core::proto::{ConfigApplyOutcome, config_update}; + + let request_id = update.request_id.clone(); + let component_sequence = update.component_sequence; + if request_id.is_empty() || component_sequence == 0 { + return config_update_result( + request_id, + component_sequence, + ConfigApplyOutcome::Failed, + "config update requires non-empty request_id and non-zero component_sequence", + ); + } + let index = match update.component.as_ref() { + Some(config_update::Component::SandboxConfig(_)) => 0, + Some(config_update::Component::ProviderEnvironment(_)) => 1, + Some(config_update::Component::InferenceBundle(_)) => 2, + None => { + return config_update_result( + request_id, + component_sequence, + ConfigApplyOutcome::Unsupported, + "config update component is unset or unknown", + ); + } + }; + let previous_sequence = + self.component_sequences[index].fetch_max(component_sequence, Ordering::AcqRel); + if component_sequence <= previous_sequence { + return config_update_result( + request_id, + component_sequence, + ConfigApplyOutcome::IgnoredStale, + "", + ); + } + let application = match update.component { + Some(config_update::Component::SandboxConfig(update)) => match update.snapshot { + Some(snapshot) => self.apply_config(snapshot).await, + None => Err("sandbox config update is missing its snapshot".to_string()), + }, + Some(config_update::Component::ProviderEnvironment(update)) => match update.snapshot { + Some(snapshot) => self.apply_provider(snapshot).await, + None => Err("provider environment update is missing its snapshot".to_string()), + }, + Some(config_update::Component::InferenceBundle(update)) => match update.snapshot { + Some(snapshot) => self.apply_inference(snapshot).await, + None => Err("inference bundle update is missing its snapshot".to_string()), + }, + None => unreachable!("component presence checked above"), + }; + match application { + Ok(outcome) => config_update_result(request_id, component_sequence, outcome, ""), + Err(error) => config_update_result( + request_id, + component_sequence, + ConfigApplyOutcome::Failed, + &error, + ), + } + } +} + +fn sanitize_desired_state_error(error: &str) -> String { + const MAX_ERROR_BYTES: usize = 1024; + let mut sanitized: String = error + .chars() + .filter(|character| !character.is_control()) + .collect(); + if sanitized.len() > MAX_ERROR_BYTES { + let mut boundary = MAX_ERROR_BYTES; + while !sanitized.is_char_boundary(boundary) { + boundary -= 1; + } + sanitized.truncate(boundary); + } + sanitized +} + +fn config_update_result( + request_id: String, + component_sequence: u64, + outcome: openshell_core::proto::ConfigApplyOutcome, + error: &str, +) -> openshell_core::proto::ConfigUpdateResult { + openshell_core::proto::ConfigUpdateResult { + request_id, + component_sequence, + outcome: outcome as i32, + error: sanitize_desired_state_error(error), + } +} + +async fn run_desired_state_loop( + mut requests: tokio::sync::mpsc::Receiver< + openshell_supervisor_process::supervisor_session::DesiredStateRequest, + >, + runtime: DesiredStateRuntime, +) { + use openshell_supervisor_process::supervisor_session::DesiredStateRequest; + + let runtime = Arc::new(runtime); + let mut updates = tokio::task::JoinSet::new(); + let mut credential_refresh = tokio::time::interval(Duration::from_secs(10)); + credential_refresh.tick().await; + loop { + tokio::select! { + request = requests.recv() => { + let Some(request) = request else { + break; + }; + match request { + DesiredStateRequest::Bootstrap { bootstrap, result } => { + // A reconnect bootstrap supersedes every request from + // the previous session. Cancel and drain those tasks + // before installing the new complete snapshot so a + // late old update cannot overwrite fresh state. + updates.abort_all(); + while updates.join_next().await.is_some() {} + let _ = result.send(runtime.apply_bootstrap(*bootstrap).await); + } + DesiredStateRequest::Update { update, result } => { + let runtime = Arc::clone(&runtime); + updates.spawn(async move { + let _ = result.send(runtime.apply_update(*update).await); + }); + } + } + } + Some(completion) = updates.join_next(), if !updates.is_empty() => { + if let Err(error) = completion + && !error.is_cancelled() + { + warn!(error = %error, "Desired-state component task failed"); + } + } + _ = credential_refresh.tick() => { + runtime.refresh_extension_credentials().await; + } + } + } +} + type MiddlewareConnector = Arc< dyn Fn( Vec, @@ -3350,6 +4037,7 @@ async fn install_builtin_middleware_registry(opa_engine: &OpaEngine) -> Result<( /// Wait the configured poll interval, but never past the point at which an /// installed extension credential must be rotated. +#[cfg(test)] fn next_poll_delay( store: &openshell_extension_core::ExtensionCredentialStore, interval: Duration, @@ -3462,6 +4150,7 @@ struct PolicyValidationFailureDisposition { active_generation: u64, } +#[cfg(test)] struct RejectedPolicyGeneration { version: u32, policy_hash: String, @@ -3669,17 +4358,9 @@ fn emit_policy_validation_failure( } } -async fn run_policy_poll_loop(ctx: PolicyPollLoopContext) -> Result<()> { - let client = openshell_core::grpc_client::CachedOpenShellClient::connect_with_credentials( - &ctx.endpoint, - ctx.extension_credentials.clone(), - ) - .await?; - run_policy_poll_loop_with_client(ctx, client).await -} - +#[cfg(test)] async fn run_policy_poll_loop_with_client( - ctx: PolicyPollLoopContext, + ctx: DesiredStateContext, client: C, ) -> Result<()> { use openshell_core::proto::PolicySource; @@ -3940,12 +4621,7 @@ async fn run_policy_poll_loop_with_client( } if provider_env_changed { - match openshell_core::grpc_client::fetch_provider_environment( - &ctx.endpoint, - &ctx.sandbox_id, - ) - .await - { + match client.provider_environment(&ctx.sandbox_id).await { Ok(env_result) => { let provider_env_revision = env_result.provider_env_revision; if apply_provider_environment_snapshot( @@ -4231,10 +4907,10 @@ async fn run_policy_poll_loop_with_client( /// /// The caller remains responsible for serializing snapshots and deciding whether /// a failed application should be retried. Keeping transport outside this helper -/// lets polling and supervisor-session updates share the same installation path. +/// lets bootstrap and live supervisor-session updates share the same installation path. fn apply_provider_environment_snapshot( provider_credentials: &ProviderCredentialState, - snapshot: openshell_core::grpc_client::ProviderEnvironmentResult, + snapshot: openshell_core::proto::ProviderEnvironmentSnapshot, sidecar_control_publisher: Option<&sidecar_control::Publisher>, ) -> bool { let provider_env_revision = snapshot.provider_env_revision; @@ -4524,7 +5200,7 @@ mod tests { ProviderCredentialState::from_child_env_snapshot(1, std::collections::HashMap::new()); let applied = apply_provider_environment_snapshot( &provider_credentials, - openshell_core::grpc_client::ProviderEnvironmentResult { + openshell_core::proto::ProviderEnvironmentSnapshot { environment: std::collections::HashMap::from([( "API_BASE".to_string(), "https://example.test".to_string(), @@ -5025,10 +5701,9 @@ network_policies: opa_engine: Arc, loaded_policy_origin: LoadedPolicyOrigin, middleware_connector: MiddlewareConnector, - ) -> PolicyPollLoopContext { + ) -> DesiredStateContext { let (workspace_tx, _workspace_rx) = tokio::sync::watch::channel(String::new()); - PolicyPollLoopContext { - endpoint: String::new(), + DesiredStateContext { sandbox_id: "sandbox-test".to_string(), opa_engine, loaded_policy_origin, diff --git a/crates/openshell-sandbox/src/main.rs b/crates/openshell-sandbox/src/main.rs index 6d244fb6bc..dbb8398e18 100644 --- a/crates/openshell-sandbox/src/main.rs +++ b/crates/openshell-sandbox/src/main.rs @@ -596,7 +596,7 @@ fn main() -> Result<()> { let push_layer = log_push_state.as_ref().map(|(layer, _)| layer.clone()); let _log_push_handle = log_push_state.map(|(_, handle)| handle); - // Shared flag: the sandbox poll loop toggles this when the + // Shared flag: desired-state delivery toggles this when the // `ocsf_json_enabled` setting changes. The JSONL layer checks it // on each event and short-circuits when false. let ocsf_enabled = Arc::new(AtomicBool::new(false)); diff --git a/crates/openshell-sdk/tests/client_mock.rs b/crates/openshell-sdk/tests/client_mock.rs index 4b06b9c1e5..327c60db7c 100644 --- a/crates/openshell-sdk/tests/client_mock.rs +++ b/crates/openshell-sdk/tests/client_mock.rs @@ -488,8 +488,8 @@ impl OpenShell for TestOpenShell { async fn get_sandbox_config( &self, _: tonic::Request, - ) -> Result, Status> { - Ok(Response::new(proto::GetSandboxConfigResponse::default())) + ) -> Result, Status> { + Ok(Response::new(proto::SandboxConfigSnapshot::default())) } async fn get_gateway_config( @@ -536,15 +536,6 @@ impl OpenShell for TestOpenShell { Err(Status::unimplemented("unused")) } - async fn get_sandbox_provider_environment( - &self, - _: tonic::Request, - ) -> Result, Status> { - Ok(Response::new( - proto::GetSandboxProviderEnvironmentResponse::default(), - )) - } - async fn get_sandbox_logs( &self, _: tonic::Request, diff --git a/crates/openshell-server/src/auth/method_authz.rs b/crates/openshell-server/src/auth/method_authz.rs index 71eb7acac6..92beaa8468 100644 --- a/crates/openshell-server/src/auth/method_authz.rs +++ b/crates/openshell-server/src/auth/method_authz.rs @@ -129,9 +129,6 @@ mod tests { "/openshell.v1.OpenShell/ReportPolicyStatus" )); assert!(!is_user_callable("/openshell.v1.OpenShell/PushSandboxLogs")); - assert!(!is_user_callable( - "/openshell.v1.OpenShell/GetSandboxProviderEnvironment" - )); assert!(!is_user_callable( "/openshell.v1.OpenShell/SubmitPolicyAnalysis" )); @@ -139,9 +136,6 @@ mod tests { "/openshell.v1.OpenShell/ConnectSupervisor" )); assert!(!is_user_callable("/openshell.v1.OpenShell/RelayStream")); - assert!(!is_user_callable( - "/openshell.inference.v1.Inference/GetInferenceBundle" - )); // Unauthenticated methods are not "user callable" — they're // intercepted before principal evaluation. assert!(!is_user_callable("/openshell.v1.OpenShell/Health")); diff --git a/crates/openshell-server/src/auth/sandbox_methods.rs b/crates/openshell-server/src/auth/sandbox_methods.rs index 89f34d1253..4d60442859 100644 --- a/crates/openshell-server/src/auth/sandbox_methods.rs +++ b/crates/openshell-server/src/auth/sandbox_methods.rs @@ -29,9 +29,6 @@ mod tests { assert!(is_sandbox_callable( "/openshell.v1.OpenShell/GetSandboxConfig" )); - assert!(is_sandbox_callable( - "/openshell.inference.v1.Inference/GetInferenceBundle" - )); assert!(is_sandbox_callable( "/openshell.v1.OpenShell/ExchangeProviderSubjectToken" )); diff --git a/crates/openshell-server/src/compute/mod.rs b/crates/openshell-server/src/compute/mod.rs index 65411d28f1..73d6b40086 100644 --- a/crates/openshell-server/src/compute/mod.rs +++ b/crates/openshell-server/src/compute/mod.rs @@ -1349,7 +1349,7 @@ impl ComputeRuntime { ) -> Option { let sandbox_id = transition.object_id().to_string(); let expected_resource_version = sandbox_resource_version(transition); - let session_connected = self.supervisor_sessions.has_session(&sandbox_id); + let session_connected = self.supervisor_sessions.has_ready_session(&sandbox_id); match self .store .update_message_cas::(&sandbox_id, expected_resource_version, |sandbox| { @@ -1796,7 +1796,7 @@ impl ComputeRuntime { match observed { Ok(Some(snapshot)) if snapshot.id == sandbox_id && snapshot.status.is_some() => { - let session_connected = self.supervisor_sessions.has_session(sandbox_id); + let session_connected = self.supervisor_sessions.has_ready_session(sandbox_id); self.write_delete_recovery_with_retry( sandbox_id, deleting_resource_version, @@ -2766,7 +2766,7 @@ impl ComputeRuntime { expected_resource_version: u64, existing_phase: SandboxPhase, ) -> Result<(), String> { - let session_connected = self.supervisor_sessions.has_session(&incoming.id); + let session_connected = self.supervisor_sessions.has_ready_session(&incoming.id); let sandbox = self .store .update_message_cas::( @@ -2810,6 +2810,58 @@ impl ComputeRuntime { .await } + pub async fn supervisor_bootstrap_failed( + &self, + sandbox_id: &str, + message: &str, + ) -> Result<(), String> { + let Some(sandbox) = self + .store + .get_message::(sandbox_id) + .await + .map_err(|error| error.to_string())? + else { + return Ok(()); + }; + self.mark_sandbox_error(&sandbox, "SupervisorBootstrapFailed", message) + .await; + Ok(()) + } + + pub async fn supervisor_config_update_result( + &self, + sandbox_id: &str, + component: &str, + applied: bool, + reason: &str, + message: &str, + ) -> Result<(), String> { + let _guard = self.sync_lock.lock().await; + let condition_type = format!("DesiredState{component}"); + let reason = reason.to_string(); + let message = message.to_string(); + let updated = self + .store + .update_message_cas::(sandbox_id, 0, |sandbox| { + let sandbox_name = sandbox.object_name().to_string(); + upsert_sandbox_condition( + &mut sandbox.status, + &sandbox_name, + SandboxCondition { + r#type: condition_type.clone(), + status: if applied { "True" } else { "False" }.to_string(), + reason: reason.clone(), + message: message.clone(), + last_transition_time: String::new(), + }, + ); + }) + .await + .map_err(|error| error.to_string())?; + self.sandbox_index.update_from_sandbox(&updated); + Ok(()) + } + async fn set_supervisor_session_state( &self, sandbox_id: &str, @@ -2837,7 +2889,7 @@ impl ComputeRuntime { ) { return Ok(()); } - if !connected && current_phase != SandboxPhase::Ready { + if !connected && current_phase == SandboxPhase::Provisioning { return Ok(()); } let expected_resource_version = sandbox_resource_version(&existing); @@ -3780,6 +3832,13 @@ fn apply_driver_snapshot(sandbox: &mut Sandbox, incoming: &DriverSandbox, sessio .main_process_instance_id .clone_from(¤t_status.main_process_instance_id); status.exit_code = current_status.exit_code; + for condition in current_status + .conditions + .iter() + .filter(|condition| condition.r#type.starts_with("DesiredState")) + { + upsert_sandbox_condition_value(status, condition.clone()); + } } if old_phase != phase { info!( @@ -3942,10 +4001,26 @@ fn upsert_ready_condition( ..Default::default() }); + upsert_sandbox_condition_value(status, condition); +} + +fn upsert_sandbox_condition( + status: &mut Option, + sandbox_name: &str, + condition: SandboxCondition, +) { + let status = status.get_or_insert_with(|| SandboxStatus { + sandbox_name: sandbox_name.to_string(), + ..Default::default() + }); + upsert_sandbox_condition_value(status, condition); +} + +fn upsert_sandbox_condition_value(status: &mut SandboxStatus, condition: SandboxCondition) { if let Some(existing) = status .conditions .iter_mut() - .find(|existing| existing.r#type == "Ready") + .find(|existing| existing.r#type == condition.r#type) { *existing = condition; } else { @@ -4963,6 +5038,16 @@ mod tests { tx, shutdown_tx, ); + assert!( + !runtime + .supervisor_sessions + .mark_initialized(sandbox_id, "session-1") + ); + assert!( + runtime + .supervisor_sessions + .mark_runtime_ready(sandbox_id, "session-1") + ); } fn sandbox_record(id: &str, name: &str, phase: SandboxPhase) -> Sandbox { @@ -5977,7 +6062,11 @@ mod tests { .unwrap(); assert_eq!(stopped.phase(), SandboxPhase::Stopped as i32); assert_eq!(driver.stop_calls(), 1); - assert!(!runtime.supervisor_sessions.has_session(sandbox.object_id())); + assert!( + !runtime + .supervisor_sessions + .has_ready_session(sandbox.object_id()) + ); assert!( runtime .store @@ -6086,7 +6175,11 @@ mod tests { assert_eq!(stopped.phase(), SandboxPhase::Stopped as i32); assert_eq!(driver.stop_calls(), 0); - assert!(!runtime.supervisor_sessions.has_session(sandbox.object_id())); + assert!( + !runtime + .supervisor_sessions + .has_ready_session(sandbox.object_id()) + ); assert!( runtime .store @@ -6124,7 +6217,11 @@ mod tests { .unwrap() .unwrap(); assert_eq!(stored.phase(), SandboxPhase::Stopped as i32); - assert!(!runtime.supervisor_sessions.has_session(sandbox.object_id())); + assert!( + !runtime + .supervisor_sessions + .has_ready_session(sandbox.object_id()) + ); assert!( runtime .store @@ -6247,7 +6344,11 @@ mod tests { .unwrap() .unwrap(); assert_eq!(stored.phase(), SandboxPhase::Stopped as i32); - assert!(!runtime.supervisor_sessions.has_session(sandbox.object_id())); + assert!( + !runtime + .supervisor_sessions + .has_ready_session(sandbox.object_id()) + ); assert!( runtime .store @@ -6304,7 +6405,11 @@ mod tests { .unwrap() .unwrap(); assert_eq!(stored.phase(), SandboxPhase::Stopping as i32); - assert!(runtime.supervisor_sessions.has_session(sandbox.object_id())); + assert!( + runtime + .supervisor_sessions + .has_ready_session(sandbox.object_id()) + ); progressing.status.as_mut().unwrap().conditions[0].status = "True".to_string(); progressing.status.as_mut().unwrap().conditions[0].reason = "PodTerminated".to_string(); @@ -6317,7 +6422,11 @@ mod tests { .unwrap() .unwrap(); assert_eq!(stored.phase(), SandboxPhase::Stopped as i32); - assert!(!runtime.supervisor_sessions.has_session(sandbox.object_id())); + assert!( + !runtime + .supervisor_sessions + .has_ready_session(sandbox.object_id()) + ); assert!( runtime .store @@ -6422,7 +6531,11 @@ mod tests { ))); runtime.apply_sandbox_update(stopped.clone()).await.unwrap(); - assert!(!runtime.supervisor_sessions.has_session(sandbox.object_id())); + assert!( + !runtime + .supervisor_sessions + .has_ready_session(sandbox.object_id()) + ); assert!( runtime .store @@ -6438,7 +6551,11 @@ mod tests { register_test_supervisor_session(&runtime, sandbox.object_id()); runtime.apply_sandbox_update(stopped).await.unwrap(); - assert!(runtime.supervisor_sessions.has_session(sandbox.object_id())); + assert!( + runtime + .supervisor_sessions + .has_ready_session(sandbox.object_id()) + ); assert!( runtime .store @@ -7946,6 +8063,97 @@ mod tests { assert_eq!(ready.message, "Supervisor session disconnected"); } + #[tokio::test] + async fn supervisor_disconnect_before_bootstrap_returns_starting_to_provisioning() { + let runtime = test_runtime(Arc::new(TestDriver::default())).await; + let sandbox = sandbox_record("sb-1", "sandbox-a", SandboxPhase::Starting); + runtime.store.put_message(&sandbox).await.unwrap(); + + runtime + .supervisor_session_disconnected("sb-1") + .await + .unwrap(); + + let stored = runtime + .store + .get_message::("sb-1") + .await + .unwrap() + .unwrap(); + assert_eq!( + SandboxPhase::try_from(stored.phase()).unwrap(), + SandboxPhase::Provisioning + ); + let ready = ready_condition(&stored).unwrap(); + assert_eq!(ready.reason, "DependenciesNotReady"); + } + + #[tokio::test] + async fn live_config_update_failure_preserves_ready_with_degraded_condition() { + let runtime = test_runtime(Arc::new(TestDriver::default())).await; + let sandbox = sandbox_record("sb-1", "sandbox-a", SandboxPhase::Ready); + runtime.store.put_message(&sandbox).await.unwrap(); + + runtime + .supervisor_config_update_result( + "sb-1", + "ProviderEnvironment", + false, + "DesiredStateApplyFailed", + "provider snapshot rejected", + ) + .await + .unwrap(); + + let stored = runtime + .store + .get_message::("sb-1") + .await + .unwrap() + .unwrap(); + assert_eq!( + SandboxPhase::try_from(stored.phase()).unwrap(), + SandboxPhase::Ready + ); + let condition = stored + .status + .as_ref() + .unwrap() + .conditions + .iter() + .find(|condition| condition.r#type == "DesiredStateProviderEnvironment") + .unwrap(); + assert_eq!(condition.status, "False"); + assert_eq!(condition.reason, "DesiredStateApplyFailed"); + + runtime + .supervisor_config_update_result( + "sb-1", + "ProviderEnvironment", + true, + "DesiredStateApplied", + "Latest provider environment desired state applied", + ) + .await + .unwrap(); + let stored = runtime + .store + .get_message::("sb-1") + .await + .unwrap() + .unwrap(); + let condition = stored + .status + .as_ref() + .unwrap() + .conditions + .iter() + .find(|condition| condition.r#type == "DesiredStateProviderEnvironment") + .unwrap(); + assert_eq!(condition.status, "True"); + assert_eq!(condition.reason, "DesiredStateApplied"); + } + // --- Composition rule tests --- fn make_ready_driver_status() -> DriverSandboxStatus { diff --git a/crates/openshell-server/src/grpc/mod.rs b/crates/openshell-server/src/grpc/mod.rs index f502369bcf..71ced87312 100644 --- a/crates/openshell-server/src/grpc/mod.rs +++ b/crates/openshell-server/src/grpc/mod.rs @@ -30,29 +30,27 @@ use openshell_core::proto::{ GetGatewayConfigRequest, GetGatewayConfigResponse, GetGatewayInfoRequest, GetGatewayInfoResponse, GetProviderProfileRequest, GetProviderRefreshStatusRequest, GetProviderRefreshStatusResponse, GetProviderRequest, GetSandboxConfigRequest, - GetSandboxConfigResponse, GetSandboxLogsRequest, GetSandboxLogsResponse, - GetSandboxPolicyStatusRequest, GetSandboxPolicyStatusResponse, - GetSandboxProviderEnvironmentRequest, GetSandboxProviderEnvironmentResponse, GetSandboxRequest, - GetServiceRequest, GetWorkspaceRequest, GetWorkspaceResponse, HealthRequest, HealthResponse, - ImportProviderProfilesRequest, ImportProviderProfilesResponse, IssueSandboxTokenRequest, - IssueSandboxTokenResponse, LintProviderProfilesRequest, LintProviderProfilesResponse, - ListProviderProfilesRequest, ListProviderProfilesResponse, ListProvidersRequest, - ListProvidersResponse, ListSandboxPoliciesRequest, ListSandboxPoliciesResponse, - ListSandboxProvidersRequest, ListSandboxProvidersResponse, ListSandboxesRequest, - ListSandboxesResponse, ListServicesRequest, ListServicesResponse, ListWorkspaceMembersRequest, - ListWorkspaceMembersResponse, ListWorkspacesRequest, ListWorkspacesResponse, - ProviderProfileResponse, ProviderResponse, PushSandboxLogsRequest, PushSandboxLogsResponse, - RefreshSandboxTokenRequest, RefreshSandboxTokenResponse, RejectDraftChunkRequest, - RejectDraftChunkResponse, RelayFrame, RemoveWorkspaceMemberRequest, - RemoveWorkspaceMemberResponse, ReportMainProcessExitRequest, ReportMainProcessExitResponse, - ReportPolicyStatusRequest, ReportPolicyStatusResponse, RevokeSshSessionRequest, - RevokeSshSessionResponse, RotateProviderCredentialRequest, RotateProviderCredentialResponse, - SandboxResponse, ServiceEndpointResponse, ServiceStatus, StartSandboxRequest, - StopSandboxRequest, SubmitPolicyAnalysisRequest, SubmitPolicyAnalysisResponse, - SupervisorMessage, TcpForwardFrame, UndoDraftChunkRequest, UndoDraftChunkResponse, - UpdateConfigRequest, UpdateConfigResponse, UpdateProviderProfilesRequest, - UpdateProviderProfilesResponse, UpdateProviderRequest, WatchSandboxRequest, - open_shell_server::OpenShell, + GetSandboxLogsRequest, GetSandboxLogsResponse, GetSandboxPolicyStatusRequest, + GetSandboxPolicyStatusResponse, GetSandboxRequest, GetServiceRequest, GetWorkspaceRequest, + GetWorkspaceResponse, HealthRequest, HealthResponse, ImportProviderProfilesRequest, + ImportProviderProfilesResponse, IssueSandboxTokenRequest, IssueSandboxTokenResponse, + LintProviderProfilesRequest, LintProviderProfilesResponse, ListProviderProfilesRequest, + ListProviderProfilesResponse, ListProvidersRequest, ListProvidersResponse, + ListSandboxPoliciesRequest, ListSandboxPoliciesResponse, ListSandboxProvidersRequest, + ListSandboxProvidersResponse, ListSandboxesRequest, ListSandboxesResponse, ListServicesRequest, + ListServicesResponse, ListWorkspaceMembersRequest, ListWorkspaceMembersResponse, + ListWorkspacesRequest, ListWorkspacesResponse, ProviderProfileResponse, ProviderResponse, + PushSandboxLogsRequest, PushSandboxLogsResponse, RefreshSandboxTokenRequest, + RefreshSandboxTokenResponse, RejectDraftChunkRequest, RejectDraftChunkResponse, RelayFrame, + RemoveWorkspaceMemberRequest, RemoveWorkspaceMemberResponse, ReportMainProcessExitRequest, + ReportMainProcessExitResponse, ReportPolicyStatusRequest, ReportPolicyStatusResponse, + RevokeSshSessionRequest, RevokeSshSessionResponse, RotateProviderCredentialRequest, + RotateProviderCredentialResponse, SandboxConfigSnapshot, SandboxResponse, + ServiceEndpointResponse, ServiceStatus, StartSandboxRequest, StopSandboxRequest, + SubmitPolicyAnalysisRequest, SubmitPolicyAnalysisResponse, SupervisorMessage, TcpForwardFrame, + UndoDraftChunkRequest, UndoDraftChunkResponse, UpdateConfigRequest, UpdateConfigResponse, + UpdateProviderProfilesRequest, UpdateProviderProfilesResponse, UpdateProviderRequest, + WatchSandboxRequest, open_shell_server::OpenShell, }; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -420,7 +418,9 @@ impl OpenShell for OpenShellService { &self, request: Request, ) -> Result, Status> { - provider::handle_create_provider(&self.state, request).await + let response = provider::handle_create_provider(&self.state, request).await?; + self.state.sandbox_watch_bus.notify_all(); + Ok(response) } async fn get_provider( @@ -455,14 +455,18 @@ impl OpenShell for OpenShellService { &self, request: Request, ) -> Result, Status> { - provider::handle_import_provider_profiles(&self.state, request).await + let response = provider::handle_import_provider_profiles(&self.state, request).await?; + self.state.sandbox_watch_bus.notify_all(); + Ok(response) } async fn update_provider_profiles( &self, request: Request, ) -> Result, Status> { - provider::handle_update_provider_profiles(&self.state, request).await + let response = provider::handle_update_provider_profiles(&self.state, request).await?; + self.state.sandbox_watch_bus.notify_all(); + Ok(response) } async fn lint_provider_profiles( @@ -476,7 +480,9 @@ impl OpenShell for OpenShellService { &self, request: Request, ) -> Result, Status> { - provider::handle_update_provider(&self.state, request).await + let response = provider::handle_update_provider(&self.state, request).await?; + self.state.sandbox_watch_bus.notify_all(); + Ok(response) } async fn get_provider_refresh_status( @@ -490,35 +496,45 @@ impl OpenShell for OpenShellService { &self, request: Request, ) -> Result, Status> { - provider::handle_configure_provider_refresh(&self.state, request).await + let response = provider::handle_configure_provider_refresh(&self.state, request).await?; + self.state.sandbox_watch_bus.notify_all(); + Ok(response) } async fn rotate_provider_credential( &self, request: Request, ) -> Result, Status> { - provider::handle_rotate_provider_credential(&self.state, request).await + let response = provider::handle_rotate_provider_credential(&self.state, request).await?; + self.state.sandbox_watch_bus.notify_all(); + Ok(response) } async fn delete_provider_refresh( &self, request: Request, ) -> Result, Status> { - provider::handle_delete_provider_refresh(&self.state, request).await + let response = provider::handle_delete_provider_refresh(&self.state, request).await?; + self.state.sandbox_watch_bus.notify_all(); + Ok(response) } async fn delete_provider( &self, request: Request, ) -> Result, Status> { - provider::handle_delete_provider(&self.state, request).await + let response = provider::handle_delete_provider(&self.state, request).await?; + self.state.sandbox_watch_bus.notify_all(); + Ok(response) } async fn delete_provider_profile( &self, request: Request, ) -> Result, Status> { - provider::handle_delete_provider_profile(&self.state, request).await + let response = provider::handle_delete_provider_profile(&self.state, request).await?; + self.state.sandbox_watch_bus.notify_all(); + Ok(response) } // --- Config / Policy --- @@ -526,7 +542,7 @@ impl OpenShell for OpenShellService { async fn get_sandbox_config( &self, request: Request, - ) -> Result, Status> { + ) -> Result, Status> { policy::handle_get_sandbox_config(&self.state, request).await } @@ -537,13 +553,6 @@ impl OpenShell for OpenShellService { policy::handle_get_gateway_config(&self.state, request).await } - async fn get_sandbox_provider_environment( - &self, - request: Request, - ) -> Result, Status> { - policy::handle_get_sandbox_provider_environment(&self.state, request).await - } - async fn exchange_provider_subject_token( &self, request: Request, diff --git a/crates/openshell-server/src/grpc/policy.rs b/crates/openshell-server/src/grpc/policy.rs index 975e608f1f..37a52f12fa 100644 --- a/crates/openshell-server/src/grpc/policy.rs +++ b/crates/openshell-server/src/grpc/policy.rs @@ -23,6 +23,8 @@ use crate::provider_profile_sources::EffectiveProviderProfileCatalog; #[cfg(test)] use crate::provider_profile_sources::ProviderProfileSources; use openshell_core::net::{is_always_blocked_ip, is_internal_ip}; +#[cfg(test)] +use openshell_core::proto::GetSandboxProviderEnvironmentRequest; use openshell_core::proto::policy_merge_operation; use openshell_core::proto::setting_value; use openshell_core::proto::{ @@ -32,13 +34,12 @@ use openshell_core::proto::{ DraftHistoryEntry, EditDraftChunkRequest, EditDraftChunkResponse, EffectiveSetting, GetDraftHistoryRequest, GetDraftHistoryResponse, GetDraftPolicyRequest, GetDraftPolicyResponse, GetGatewayConfigRequest, GetGatewayConfigResponse, GetSandboxConfigRequest, - GetSandboxConfigResponse, GetSandboxLogsRequest, GetSandboxLogsResponse, - GetSandboxPolicyStatusRequest, GetSandboxPolicyStatusResponse, - GetSandboxProviderEnvironmentRequest, GetSandboxProviderEnvironmentResponse, - ListSandboxPoliciesRequest, ListSandboxPoliciesResponse, PolicyChunk, PolicyMergeOperation, - PolicySource, PolicyStatus, PushSandboxLogsRequest, PushSandboxLogsResponse, - RejectDraftChunkRequest, RejectDraftChunkResponse, ReportPolicyStatusRequest, - ReportPolicyStatusResponse, SandboxLogLine, SandboxPolicyRevision, SettingScope, SettingValue, + GetSandboxLogsRequest, GetSandboxLogsResponse, GetSandboxPolicyStatusRequest, + GetSandboxPolicyStatusResponse, ListSandboxPoliciesRequest, ListSandboxPoliciesResponse, + PolicyChunk, PolicyMergeOperation, PolicySource, PolicyStatus, ProviderEnvironmentSnapshot, + PushSandboxLogsRequest, PushSandboxLogsResponse, RejectDraftChunkRequest, + RejectDraftChunkResponse, ReportPolicyStatusRequest, ReportPolicyStatusResponse, + SandboxConfigSnapshot, SandboxLogLine, SandboxPolicyRevision, SettingScope, SettingValue, SubmitPolicyAnalysisRequest, SubmitPolicyAnalysisResponse, UndoDraftChunkRequest, UndoDraftChunkResponse, UpdateConfigRequest, UpdateConfigResponse, }; @@ -2313,7 +2314,7 @@ async fn resolve_sandbox_by_name_for_principal( pub(super) async fn handle_get_sandbox_config( state: &Arc, request: Request, -) -> Result, Status> { +) -> Result, Status> { let principal = super::extract_principal(&request)?; let sandbox_id = request.get_ref().sandbox_id.clone(); crate::auth::guard::enforce_sandbox_scope(&request, &sandbox_id)?; @@ -2334,7 +2335,7 @@ pub(super) async fn handle_get_sandbox_config( pub async fn build_sandbox_config_snapshot( state: &ServerState, sandbox: &Sandbox, -) -> Result { +) -> Result { let sandbox_id = sandbox.object_id().to_string(); let workspace = sandbox.object_workspace().to_string(); let sandbox_provider_names = sandbox @@ -2530,7 +2531,7 @@ pub async fn build_sandbox_config_snapshot( ) .await?; - Ok(GetSandboxConfigResponse { + Ok(SandboxConfigSnapshot { policy, version, policy_hash, @@ -3042,10 +3043,11 @@ pub(super) async fn handle_get_gateway_config( })) } +#[cfg(test)] pub(super) async fn handle_get_sandbox_provider_environment( state: &Arc, request: Request, -) -> Result, Status> { +) -> Result, Status> { let sandbox_id = request.get_ref().sandbox_id.clone(); let supports_static_credential_bindings = request.get_ref().supports_static_credential_bindings; crate::auth::guard::enforce_sandbox_scope(&request, &sandbox_id)?; @@ -3065,14 +3067,14 @@ pub(super) async fn handle_get_sandbox_provider_environment( /// Build the gateway-owned provider environment for an authorized sandbox. /// -/// `supports_static_credential_bindings` preserves the existing fetch RPC's -/// compatibility behavior. The required session protocol introduced by -/// #1731 will call this builder with binding support enabled. +/// Session delivery always enables static credential bindings. The capability +/// argument remains explicit so builder tests can cover fail-closed behavior +/// for consumers without binding support. pub async fn build_provider_environment_snapshot( state: &ServerState, sandbox: &Sandbox, supports_static_credential_bindings: bool, -) -> Result { +) -> Result { let sandbox_id = sandbox.object_id().to_string(); let workspace = sandbox.object_workspace().to_string(); @@ -3158,7 +3160,7 @@ pub async fn build_provider_environment_snapshot( provider_count = provider_names.len(), env_count = provider_environment.environment.len(), provider_env_revision, - "GetSandboxProviderEnvironment request completed successfully" + "Provider environment snapshot built successfully" ); let non_secret_environment_keys = provider_environment @@ -3168,7 +3170,7 @@ pub async fn build_provider_environment_snapshot( .cloned() .collect(); - Ok(GetSandboxProviderEnvironmentResponse { + Ok(ProviderEnvironmentSnapshot { environment: provider_environment.environment, provider_env_revision, credential_expires_at_ms: provider_environment.credential_expires_at_ms, diff --git a/crates/openshell-server/src/inference.rs b/crates/openshell-server/src/inference.rs index 9081d84cac..061e8bd54c 100644 --- a/crates/openshell-server/src/inference.rs +++ b/crates/openshell-server/src/inference.rs @@ -7,11 +7,10 @@ use openshell_core::inference::{ VERTEX_AI_PROJECT_ID_KEY, VERTEX_AI_PUBLISHER_KEY, VERTEX_AI_REGION_KEY, }; use openshell_core::proto::{ - DeleteInferenceRouteRequest, DeleteInferenceRouteResponse, GetInferenceBundleRequest, - GetInferenceBundleResponse, GetInferenceRouteRequest, GetInferenceRouteResponse, - InferenceRoute, InferenceRouteConfig, Provider, ResolvedRoute, Sandbox, - SetInferenceRouteRequest, SetInferenceRouteResponse, ValidatedEndpoint, - inference_server::Inference, + DeleteInferenceRouteRequest, DeleteInferenceRouteResponse, GetInferenceRouteRequest, + GetInferenceRouteResponse, InferenceBundleSnapshot, InferenceRoute, InferenceRouteConfig, + Provider, ResolvedRoute, Sandbox, SetInferenceRouteRequest, SetInferenceRouteResponse, + ValidatedEndpoint, inference_server::Inference, }; use openshell_core::{ObjectId, ObjectLabels, ObjectWorkspace}; use openshell_providers::normalize_provider_type; @@ -64,27 +63,6 @@ impl ObjectType for InferenceRoute { #[tonic::async_trait] impl Inference for InferenceService { - async fn get_inference_bundle( - &self, - request: Request, - ) -> Result, Status> { - let sandbox_id = authorize_inference_bundle( - request - .extensions() - .get::(), - )?; - let sandbox: Sandbox = self - .state - .store - .get_message::(&sandbox_id) - .await - .map_err(|e| Status::internal(format!("fetch sandbox failed: {e}")))? - .ok_or_else(|| Status::not_found(format!("sandbox '{sandbox_id}' not found")))?; - build_inference_bundle_snapshot(&self.state, &sandbox) - .await - .map(Response::new) - } - async fn set_inference_route( &self, request: Request, @@ -116,6 +94,7 @@ impl Inference for InferenceService { verify, ) .await?; + self.state.sandbox_watch_bus.notify_all(); let config = route .route @@ -212,6 +191,9 @@ impl Inference for InferenceService { .delete_by_name(InferenceRoute::object_type(), &workspace, route_name) .await .map_err(|e| Status::internal(format!("delete route failed: {e}")))?; + if deleted { + self.state.sandbox_watch_bus.notify_all(); + } Ok(Response::new(DeleteInferenceRouteResponse { deleted })) } } @@ -1011,6 +993,7 @@ fn find_provider_config_value(provider: &Provider, preferred_keys: &[&str]) -> O None } +#[cfg(test)] fn authorize_inference_bundle( principal: Option<&crate::auth::principal::Principal>, ) -> Result { @@ -1030,7 +1013,7 @@ fn authorize_inference_bundle( async fn resolve_inference_bundle( store: &Store, workspace: &str, -) -> Result { +) -> Result { resolve_inference_bundle_with_credentials(store, workspace, None).await } @@ -1038,7 +1021,7 @@ async fn resolve_inference_bundle_with_credentials( store: &Store, workspace: &str, credentials: Option<&crate::credentials::CredentialRuntime>, -) -> Result { +) -> Result { let mut routes = Vec::new(); if let Some(r) = resolve_route_by_name_with_credentials( store, @@ -1087,7 +1070,7 @@ async fn resolve_inference_bundle_with_credentials( format!("{:016x}", hasher.finish()) }; - Ok(GetInferenceBundleResponse { + Ok(InferenceBundleSnapshot { routes, revision, generated_at_ms: now_ms, @@ -1101,7 +1084,7 @@ async fn resolve_inference_bundle_with_credentials( pub async fn build_inference_bundle_snapshot( state: &ServerState, sandbox: &Sandbox, -) -> Result { +) -> Result { resolve_inference_bundle_with_credentials( state.store.as_ref(), sandbox.object_workspace(), @@ -3778,7 +3761,7 @@ mod tests { } #[tokio::test] - async fn inference_snapshot_builder_matches_fetch_rpc_payload() { + async fn inference_snapshot_builder_resolves_complete_payload() { use crate::grpc::test_support::test_server_state; use openshell_core::proto::SandboxSpec; use openshell_core::proto::datamodel::v1::ObjectMeta; @@ -3820,19 +3803,9 @@ mod tests { let built = build_inference_bundle_snapshot(&state, &sandbox) .await .expect("build snapshot"); - let service = InferenceService::new(state); - let mut request = Request::new(GetInferenceBundleRequest {}); - request.extensions_mut().insert(test_sandbox_principal()); - let fetched = service - .get_inference_bundle(request) - .await - .expect("fetch bundle") - .into_inner(); - - assert_eq!(built.routes, fetched.routes); - assert_eq!(built.revision, fetched.revision); + assert_eq!(built.routes.len(), 1); + assert!(!built.revision.is_empty()); assert!(built.generated_at_ms > 0); - assert!(fetched.generated_at_ms > 0); } /// Non-member callers must receive `PERMISSION_DENIED` — not `NOT_FOUND` — diff --git a/crates/openshell-server/src/multiplex.rs b/crates/openshell-server/src/multiplex.rs index 7a7125dcc3..d334c82b7c 100644 --- a/crates/openshell-server/src/multiplex.rs +++ b/crates/openshell-server/src/multiplex.rs @@ -1364,10 +1364,8 @@ mod tests { "/openshell.v1.OpenShell/GetSandboxConfig", "/openshell.v1.OpenShell/ReportPolicyStatus", "/openshell.v1.OpenShell/PushSandboxLogs", - "/openshell.v1.OpenShell/GetSandboxProviderEnvironment", "/openshell.v1.OpenShell/SubmitPolicyAnalysis", "/openshell.v1.OpenShell/RefreshSandboxToken", - "/openshell.inference.v1.Inference/GetInferenceBundle", ]; for path in callback_paths { @@ -2320,7 +2318,7 @@ mod tests { "/openshell.v1.OpenShell/CreateSandbox", "/openshell.v1.OpenShell/ListSandboxes", "/openshell.v1.OpenShell/DeleteSandbox", - "/openshell.inference.v1.Inference/GetInferenceBundle", + "/openshell.inference.v1.Inference/GetInferenceRoute", "/metrics", ]; @@ -2343,7 +2341,7 @@ mod tests { let expected = [ "GET", - "openshell.inference.v1.Inference/GetInferenceBundle", + "openshell.inference.v1.Inference/GetInferenceRoute", "openshell.v1.OpenShell/CreateSandbox", "openshell.v1.OpenShell/DeleteSandbox", "openshell.v1.OpenShell/ListSandboxes", @@ -2373,9 +2371,9 @@ mod tests { assert_eq!( otel_span_name( &http::Method::POST, - "/openshell.inference.v1.Inference/GetInferenceBundle" + "/openshell.inference.v1.Inference/GetInferenceRoute" ), - "openshell.inference.v1.Inference/GetInferenceBundle" + "openshell.inference.v1.Inference/GetInferenceRoute" ); } @@ -2406,8 +2404,8 @@ mod tests { #[test] fn grpc_method_extracts_inference_service() { assert_eq!( - grpc_method_from_path("/openshell.inference.v1.Inference/GetInferenceBundle"), - "GetInferenceBundle" + grpc_method_from_path("/openshell.inference.v1.Inference/GetInferenceRoute"), + "GetInferenceRoute" ); } @@ -2726,27 +2724,6 @@ mod tests { )); } - #[tokio::test] - async fn sandbox_principal_can_fetch_inference_bundle() { - let mock = Arc::new(MockAuthenticator::returning(Ok(Some(sandbox_principal())))); - let chain = AuthenticatorChain::new(vec![mock]); - let (recorder, seen) = PrincipalRecorder::new(); - let mut router = AuthGrpcRouter::new(recorder, Some(chain), None); - - let res = router - .call(empty_request( - "/openshell.inference.v1.Inference/GetInferenceBundle", - )) - .await - .unwrap(); - - assert_eq!(res.status(), 200); - assert!(matches!( - seen.lock().unwrap().as_ref(), - Some(Principal::Sandbox(_)) - )); - } - /// A user principal — even one carrying `openshell:all` and the /// admin role — must not reach a `sandbox`-annotated method. The /// router enforces this from the per-handler auth-mode declarations @@ -2775,12 +2752,10 @@ mod tests { "/openshell.v1.OpenShell/ReportPolicyStatus", "/openshell.v1.OpenShell/PushSandboxLogs", "/openshell.v1.OpenShell/SubmitPolicyAnalysis", - "/openshell.v1.OpenShell/GetSandboxProviderEnvironment", "/openshell.v1.OpenShell/ConnectSupervisor", "/openshell.v1.OpenShell/RelayStream", "/openshell.v1.OpenShell/IssueSandboxToken", "/openshell.v1.OpenShell/RefreshSandboxToken", - "/openshell.inference.v1.Inference/GetInferenceBundle", ] { let mock = Arc::new(MockAuthenticator::returning(Ok(Some(admin_user())))); let chain = AuthenticatorChain::new(vec![mock]); diff --git a/crates/openshell-server/src/provider_refresh.rs b/crates/openshell-server/src/provider_refresh.rs index 176f86ae22..32a68d4ab2 100644 --- a/crates/openshell-server/src/provider_refresh.rs +++ b/crates/openshell-server/src/provider_refresh.rs @@ -1880,14 +1880,18 @@ pub fn spawn_refresh_worker(state: std::sync::Arc, interval: ticker.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay); loop { ticker.tick().await; - if let Err(err) = run_refresh_worker_tick( + match run_refresh_worker_tick( state.store.as_ref(), Some(&state.credentials), Some(&state.compute), ) .await { - warn!(error = %err, "provider credential refresh worker tick failed"); + Ok(true) => state.sandbox_watch_bus.notify_all(), + Ok(false) => {} + Err(err) => { + warn!(error = %err, "provider credential refresh worker tick failed"); + } } } }); @@ -1906,7 +1910,7 @@ async fn run_refresh_worker_tick( store: &Store, credentials: Option<&crate::credentials::CredentialRuntime>, compute: Option<&crate::compute::ComputeRuntime>, -) -> Result<(), Status> { +) -> Result { let now_ms = current_time_ms(); let states = list_all_refresh_states(store).await.inspect_err(|_| { crate::otel_tracing::mark_error(&tracing::Span::current()); @@ -1927,6 +1931,7 @@ async fn run_refresh_worker_tick( watched_count, due_count, rotation_requested_count, "provider credential refresh worker sweep" ); + let mut desired_state_changed = false; for state in states { if state .metadata @@ -1956,6 +1961,8 @@ async fn run_refresh_worker_tick( error = %err, "failed to finalize tombstoned provider refresh; retrying on the next sweep" ); + } else { + desired_state_changed = true; } continue; } @@ -2039,9 +2046,11 @@ async fn run_refresh_worker_tick( error = %err, "provider credential refresh failed" ); + } else { + desired_state_changed = true; } } - Ok(()) + Ok(desired_state_changed) } #[cfg(test)] diff --git a/crates/openshell-server/src/sandbox_watch.rs b/crates/openshell-server/src/sandbox_watch.rs index ac38eba8db..d5dc93fd2d 100644 --- a/crates/openshell-server/src/sandbox_watch.rs +++ b/crates/openshell-server/src/sandbox_watch.rs @@ -16,13 +16,16 @@ use tonic::Status; #[derive(Debug, Clone)] pub struct SandboxWatchBus { inner: Arc>>>, + global: broadcast::Sender<()>, } impl SandboxWatchBus { #[must_use] pub fn new() -> Self { + let (global, _rx) = broadcast::channel(128); Self { inner: Arc::new(Mutex::new(HashMap::new())), + global, } } @@ -49,6 +52,16 @@ impl SandboxWatchBus { self.sender_for(sandbox_id).subscribe() } + /// Notify consumers whose desired state can depend on workspace-wide data. + pub fn notify_all(&self) { + let _ = self.global.send(()); + } + + /// Subscribe to workspace-wide desired-state invalidations. + pub fn subscribe_all(&self) -> broadcast::Receiver<()> { + self.global.subscribe() + } + /// Remove the bus entry for the given sandbox id. /// /// This drops the broadcast sender, closing any active receivers with @@ -114,4 +127,18 @@ mod tests { // Should not panic bus.remove("nonexistent"); } + + #[test] + fn global_notifications_are_independent_from_sandbox_notifications() { + let bus = SandboxWatchBus::new(); + let mut sandbox_rx = bus.subscribe("sb-1"); + let mut global_rx = bus.subscribe_all(); + + bus.notify_all(); + assert!(global_rx.try_recv().is_ok()); + assert!(matches!( + sandbox_rx.try_recv(), + Err(broadcast::error::TryRecvError::Empty) + )); + } } diff --git a/crates/openshell-server/src/supervisor_session.rs b/crates/openshell-server/src/supervisor_session.rs index fbff0e276c..0f887a2404 100644 --- a/crates/openshell-server/src/supervisor_session.rs +++ b/crates/openshell-server/src/supervisor_session.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use std::collections::HashMap; +use std::hash::{DefaultHasher, Hash, Hasher}; use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; @@ -13,9 +14,12 @@ use tracing::{debug, info, warn}; use uuid::Uuid; use openshell_core::proto::{ - GatewayMessage, RelayFrame, RelayInit, RelayOpen, ReportMainProcessExitRequest, - ReportMainProcessExitResponse, Sandbox, SandboxPhase, SessionAccepted, SshRelayTarget, - SupervisorMessage, gateway_message, relay_open, supervisor_message, + ConfigApplyOutcome, ConfigBootstrap, ConfigBootstrapResult, ConfigBootstrapStatus, + ConfigUpdate, ConfigUpdateResult, GatewayMessage, InferenceBundleUpdate, + ProviderEnvironmentUpdate, RelayFrame, RelayInit, RelayOpen, ReportMainProcessExitRequest, + ReportMainProcessExitResponse, Sandbox, SandboxConfigUpdate, SandboxPhase, SessionAccepted, + SshRelayTarget, SupervisorMessage, config_update, gateway_message, relay_open, + supervisor_message, }; use openshell_core::transport_errors::is_expected_transport_close_status; @@ -23,6 +27,8 @@ use crate::ServerState; use crate::auth::principal::Principal; const HEARTBEAT_INTERVAL_SECS: u32 = 15; +const BOOTSTRAP_TIMEOUT: Duration = Duration::from_secs(120); +const CONFIG_UPDATE_TIMEOUT: Duration = Duration::from_secs(60); const RELAY_PENDING_TIMEOUT: Duration = Duration::from_secs(10); /// Initial backoff between session-availability polls in `wait_for_session`. const SESSION_WAIT_INITIAL_BACKOFF: Duration = Duration::from_millis(100); @@ -39,6 +45,164 @@ const MAX_PENDING_RELAYS: usize = 256; /// cap (20) so tunnel-specific limits still fire first for that caller. const MAX_PENDING_RELAYS_PER_SANDBOX: usize = 32; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum DesiredStateComponent { + SandboxConfig, + ProviderEnvironment, + InferenceBundle, +} + +#[derive(Debug)] +struct InFlightUpdate { + request_id: String, + component_sequence: u64, + revision: String, + sent_at: Instant, +} + +#[derive(Debug)] +struct ComponentDeliveryState { + applied_revision: String, + next_sequence: u64, + in_flight: Option, + pending_revision: Option, +} + +impl ComponentDeliveryState { + fn new(applied_revision: impl ToString) -> Self { + Self { + applied_revision: applied_revision.to_string(), + next_sequence: 1, + in_flight: None, + pending_revision: None, + } + } +} + +#[derive(Debug)] +struct DesiredStateDelivery { + sandbox_config: ComponentDeliveryState, + provider_environment: ComponentDeliveryState, + inference_bundle: ComponentDeliveryState, +} + +#[derive(Debug)] +struct AppliedUpdateResult { + component: DesiredStateComponent, + outcome: ConfigApplyOutcome, +} + +impl DesiredStateDelivery { + fn new(bootstrap: &ConfigBootstrap) -> Result { + let sandbox_config = bootstrap + .sandbox_config + .as_ref() + .ok_or_else(|| Status::internal("bootstrap sandbox config is missing"))?; + let provider_environment = bootstrap + .provider_environment + .as_ref() + .ok_or_else(|| Status::internal("bootstrap provider environment is missing"))?; + let inference_bundle = bootstrap + .inference_bundle + .as_ref() + .ok_or_else(|| Status::internal("bootstrap inference bundle is missing"))?; + Ok(Self { + sandbox_config: ComponentDeliveryState::new(sandbox_config.config_revision), + provider_environment: ComponentDeliveryState::new( + provider_environment.provider_env_revision, + ), + inference_bundle: ComponentDeliveryState::new(&inference_bundle.revision), + }) + } + + fn state_mut(&mut self, component: DesiredStateComponent) -> &mut ComponentDeliveryState { + match component { + DesiredStateComponent::SandboxConfig => &mut self.sandbox_config, + DesiredStateComponent::ProviderEnvironment => &mut self.provider_environment, + DesiredStateComponent::InferenceBundle => &mut self.inference_bundle, + } + } + + fn has_pending_update(&self) -> bool { + self.sandbox_config.pending_revision.is_some() + || self.provider_environment.pending_revision.is_some() + || self.inference_bundle.pending_revision.is_some() + } + + fn expire_timed_out(&mut self) -> Vec { + let mut expired = Vec::new(); + for (component, state) in [ + ( + DesiredStateComponent::SandboxConfig, + &mut self.sandbox_config, + ), + ( + DesiredStateComponent::ProviderEnvironment, + &mut self.provider_environment, + ), + ( + DesiredStateComponent::InferenceBundle, + &mut self.inference_bundle, + ), + ] { + if state + .in_flight + .as_ref() + .is_some_and(|update| update.sent_at.elapsed() >= CONFIG_UPDATE_TIMEOUT) + { + state.in_flight = None; + expired.push(component); + } + } + expired + } + + fn handle_result( + &mut self, + result: &ConfigUpdateResult, + ) -> Result, String> { + if result.request_id.is_empty() { + return Err("config update result has an empty request_id".to_string()); + } + let matches = [ + DesiredStateComponent::SandboxConfig, + DesiredStateComponent::ProviderEnvironment, + DesiredStateComponent::InferenceBundle, + ] + .into_iter() + .find(|component| { + self.state_mut(*component) + .in_flight + .as_ref() + .is_some_and(|update| update.request_id == result.request_id) + }); + let Some(component) = matches else { + return Ok(None); + }; + let state = self.state_mut(component); + let Some(update) = state.in_flight.take() else { + return Ok(None); + }; + if update.component_sequence != result.component_sequence { + state.in_flight = Some(update); + return Err("config update result component_sequence mismatch".to_string()); + } + let outcome = ConfigApplyOutcome::try_from(result.outcome) + .map_err(|_| "config update result has an unknown outcome".to_string())?; + let terminal_success = matches!( + outcome, + ConfigApplyOutcome::Applied + | ConfigApplyOutcome::IgnoredDuplicate + | ConfigApplyOutcome::RetainedLocalOverride + | ConfigApplyOutcome::Degraded + ); + if terminal_success { + state.applied_revision = update.revision; + } + Ok(Some(AppliedUpdateResult { component, outcome })) + } +} + // --------------------------------------------------------------------------- // Session registry // --------------------------------------------------------------------------- @@ -59,6 +223,8 @@ struct LiveSession { shutdown: oneshot::Sender<()>, #[allow(dead_code)] connected_at: Instant, + initialized: bool, + runtime_ready: bool, } /// Holds a oneshot sender that will deliver the upgraded relay stream or a @@ -125,6 +291,8 @@ impl SupervisorSessionRegistry { tx, shutdown, connected_at: Instant::now(), + initialized: false, + runtime_ready: false, }, ); match previous { @@ -202,13 +370,56 @@ impl SupervisorSessionRegistry { .lock() .unwrap() .get(sandbox_id) - .map(|s| s.tx.clone()) + .filter(|session| session.initialized && session.runtime_ready) + .map(|session| session.tx.clone()) } pub fn has_session(&self, sandbox_id: &str) -> bool { self.sessions.lock().unwrap().contains_key(sandbox_id) } + pub fn has_ready_session(&self, sandbox_id: &str) -> bool { + self.sessions + .lock() + .unwrap() + .get(sandbox_id) + .is_some_and(|session| session.initialized && session.runtime_ready) + } + + fn is_initialized(&self, sandbox_id: &str, session_id: &str) -> bool { + self.sessions + .lock() + .unwrap() + .get(sandbox_id) + .is_some_and(|session| session.session_id == session_id && session.initialized) + } + + /// Mark bootstrap complete. Returns whether runtime-ready was already set. + pub fn mark_initialized(&self, sandbox_id: &str, session_id: &str) -> bool { + let mut sessions = self.sessions.lock().unwrap(); + let Some(session) = sessions.get_mut(sandbox_id) else { + return false; + }; + if session.session_id != session_id { + return false; + } + session.initialized = true; + session.runtime_ready + } + + /// Mark runtime services ready. Returns whether bootstrap is also complete. + pub fn mark_runtime_ready(&self, sandbox_id: &str, session_id: &str) -> bool { + let mut sessions = self.sessions.lock().unwrap(); + let Some(session) = sessions.get_mut(sandbox_id) else { + return false; + }; + if session.session_id != session_id { + return false; + } + session.runtime_ready = true; + session.initialized + } + pub fn is_current_session(&self, sandbox_id: &str, session_id: &str) -> bool { self.sessions .lock() @@ -456,16 +667,155 @@ pub fn spawn_relay_reaper(state: Arc, interval: Duration) { async fn require_persisted_sandbox( store: &Arc, sandbox_id: &str, -) -> Result<(), Status> { +) -> Result { let sandbox = store .get_message::(sandbox_id) .await .map_err(|err| Status::internal(format!("failed to load sandbox: {err}")))?; - if sandbox.is_none() { - return Err(Status::not_found("sandbox not found")); + sandbox.ok_or_else(|| Status::not_found("sandbox not found")) +} + +fn reconciliation_interval(sandbox_id: &str) -> Duration { + let mut hasher = DefaultHasher::new(); + sandbox_id.hash(&mut hasher); + Duration::from_secs(24 + hasher.finish() % 13) +} + +async fn reconcile_desired_state( + state: &Arc, + sandbox_id: &str, + tx: &mpsc::Sender, + delivery: &mut DesiredStateDelivery, +) -> Result<(), Status> { + for component in delivery.expire_timed_out() { + let (condition_component, component_label) = match component { + DesiredStateComponent::SandboxConfig => ("SandboxConfig", "sandbox config"), + DesiredStateComponent::ProviderEnvironment => { + ("ProviderEnvironment", "provider environment") + } + DesiredStateComponent::InferenceBundle => ("InferenceBundle", "inference bundle"), + }; + if let Err(error) = state + .compute + .supervisor_config_update_result( + sandbox_id, + condition_component, + false, + "DesiredStateApplyTimedOut", + &format!("Latest {component_label} desired-state update timed out"), + ) + .await + { + warn!(sandbox_id = %sandbox_id, error = %error, "failed to persist desired-state update timeout"); + } + } + let sandbox = require_persisted_sandbox(&state.store, sandbox_id).await?; + let (sandbox_config, provider_environment, inference_bundle) = tokio::join!( + crate::grpc::policy::build_sandbox_config_snapshot(state, &sandbox), + crate::grpc::policy::build_provider_environment_snapshot(state, &sandbox, true), + crate::inference::build_inference_bundle_snapshot(state, &sandbox), + ); + + let mut errors = Vec::new(); + match sandbox_config { + Ok(snapshot) => { + if let Err(error) = send_component_update( + tx, + DesiredStateComponent::SandboxConfig, + snapshot.config_revision.to_string(), + config_update::Component::SandboxConfig(SandboxConfigUpdate { + snapshot: Some(snapshot), + }), + delivery, + ) + .await + { + errors.push(format!("sandbox config delivery: {error}")); + } + } + Err(error) => errors.push(format!("sandbox config snapshot: {error}")), + } + match provider_environment { + Ok(snapshot) => { + if let Err(error) = send_component_update( + tx, + DesiredStateComponent::ProviderEnvironment, + snapshot.provider_env_revision.to_string(), + config_update::Component::ProviderEnvironment(ProviderEnvironmentUpdate { + snapshot: Some(snapshot), + }), + delivery, + ) + .await + { + errors.push(format!("provider environment delivery: {error}")); + } + } + Err(error) => errors.push(format!("provider environment snapshot: {error}")), + } + match inference_bundle { + Ok(snapshot) => { + if let Err(error) = send_component_update( + tx, + DesiredStateComponent::InferenceBundle, + snapshot.revision.clone(), + config_update::Component::InferenceBundle(InferenceBundleUpdate { + snapshot: Some(snapshot), + }), + delivery, + ) + .await + { + errors.push(format!("inference bundle delivery: {error}")); + } + } + Err(error) => errors.push(format!("inference bundle snapshot: {error}")), + } + + if errors.is_empty() { + Ok(()) + } else { + Err(Status::internal(errors.join("; "))) } +} +async fn send_component_update( + tx: &mpsc::Sender, + component: DesiredStateComponent, + revision: String, + payload: config_update::Component, + delivery: &mut DesiredStateDelivery, +) -> Result<(), Status> { + let state = delivery.state_mut(component); + if revision == state.applied_revision { + state.pending_revision = None; + return Ok(()); + } + if let Some(in_flight) = state.in_flight.as_ref() { + state.pending_revision = (revision != in_flight.revision).then_some(revision); + return Ok(()); + } + let request_id = Uuid::new_v4().to_string(); + let component_sequence = state.next_sequence; + let message = GatewayMessage { + payload: Some(gateway_message::Payload::ConfigUpdate(ConfigUpdate { + request_id: request_id.clone(), + component: Some(payload), + component_sequence, + })), + }; + tx.send(message) + .await + .map_err(|_| Status::unavailable("supervisor session outbound queue closed"))?; + state.next_sequence = state.next_sequence.saturating_add(1); + state.in_flight = Some(InFlightUpdate { + request_id, + component_sequence, + revision, + sent_at: Instant::now(), + }); + state.pending_revision = None; Ok(()) } @@ -704,7 +1054,19 @@ pub async fn handle_connect_supervisor( if let Some(principal) = principal.as_ref() { crate::auth::guard::ensure_sandbox_principal_scope(principal, &sandbox_id)?; } - require_persisted_sandbox(&state.store, &sandbox_id).await?; + let sandbox = require_persisted_sandbox(&state.store, &sandbox_id).await?; + + let (sandbox_config, provider_environment, inference_bundle) = tokio::try_join!( + crate::grpc::policy::build_sandbox_config_snapshot(state, &sandbox), + crate::grpc::policy::build_provider_environment_snapshot(state, &sandbox, true), + crate::inference::build_inference_bundle_snapshot(state, &sandbox), + )?; + let bootstrap = ConfigBootstrap { + sandbox_config: Some(sandbox_config), + provider_environment: Some(provider_environment), + inference_bundle: Some(inference_bundle), + }; + let delivery = DesiredStateDelivery::new(&bootstrap)?; let session_id = Uuid::new_v4().to_string(); info!( @@ -736,6 +1098,7 @@ pub async fn handle_connect_supervisor( payload: Some(gateway_message::Payload::SessionAccepted(SessionAccepted { session_id: session_id.clone(), heartbeat_interval_secs: HEARTBEAT_INTERVAL_SECS, + bootstrap: Some(bootstrap), })), }; if tx.send(accepted).await.is_err() { @@ -754,21 +1117,6 @@ pub async fn handle_connect_supervisor( .await; } - if let Err(err) = state - .compute - .supervisor_session_connected(&sandbox_id, &hello.instance_id) - .await - { - warn!( - sandbox_id = %sandbox_id, - session_id = %session_id, - error = %err, - "supervisor session: failed to mark sandbox ready" - ); - } else { - state.telemetry.sandbox_session_connected(&sandbox_id); - } - // Step 4: Spawn the session loop that reads inbound messages. let state_clone = Arc::clone(state); let sandbox_id_clone = sandbox_id.clone(); @@ -778,6 +1126,7 @@ pub async fn handle_connect_supervisor( &sandbox_id_clone, &session_id, &tx, + delivery, &mut inbound, shutdown_rx, ) @@ -844,6 +1193,7 @@ async fn run_session_loop( sandbox_id: &str, session_id: &str, tx: &mpsc::Sender, + mut delivery: DesiredStateDelivery, inbound: &mut tonic::Streaming, mut shutdown_rx: oneshot::Receiver<()>, ) { @@ -851,6 +1201,12 @@ async fn run_session_loop( let mut heartbeat_timer = tokio::time::interval(heartbeat_interval); // Skip the first immediate tick. heartbeat_timer.tick().await; + let bootstrap_timeout = tokio::time::sleep(BOOTSTRAP_TIMEOUT); + tokio::pin!(bootstrap_timeout); + let mut sandbox_updates = state.sandbox_watch_bus.subscribe(sandbox_id); + let mut global_updates = state.sandbox_watch_bus.subscribe_all(); + let mut reconciliation_timer = tokio::time::interval(reconciliation_interval(sandbox_id)); + reconciliation_timer.tick().await; loop { tokio::select! { @@ -861,7 +1217,38 @@ async fn run_session_loop( msg = inbound.message() => { match msg { Ok(Some(msg)) => { - handle_supervisor_message(state, sandbox_id, session_id, msg); + let is_update_result = matches!( + &msg.payload, + Some(supervisor_message::Payload::ConfigUpdateResult(_)) + ); + if !handle_supervisor_message( + state, + sandbox_id, + session_id, + msg, + &mut delivery, + ) + .await + { + break; + } + if is_update_result + && delivery.has_pending_update() + && let Err(error) = reconcile_desired_state( + state, + sandbox_id, + tx, + &mut delivery, + ) + .await + { + warn!( + sandbox_id = %sandbox_id, + session_id = %session_id, + error = %error, + "supervisor session: desired-state reconciliation failed" + ); + } } Ok(None) => { info!(sandbox_id = %sandbox_id, session_id = %session_id, "supervisor session: stream closed by supervisor"); @@ -889,6 +1276,85 @@ async fn run_session_loop( } } } + update = sandbox_updates.recv(), + if state.supervisor_sessions.is_initialized(sandbox_id, session_id) => + { + if update.is_ok() + && let Err(error) = reconcile_desired_state( + state, + sandbox_id, + tx, + &mut delivery, + ) + .await + { + warn!( + sandbox_id = %sandbox_id, + session_id = %session_id, + error = %error, + "supervisor session: desired-state notification failed" + ); + } + } + update = global_updates.recv(), + if state.supervisor_sessions.is_initialized(sandbox_id, session_id) => + { + if update.is_ok() + && let Err(error) = reconcile_desired_state( + state, + sandbox_id, + tx, + &mut delivery, + ) + .await + { + warn!( + sandbox_id = %sandbox_id, + session_id = %session_id, + error = %error, + "supervisor session: global desired-state notification failed" + ); + } + } + _ = reconciliation_timer.tick(), + if state.supervisor_sessions.is_initialized(sandbox_id, session_id) => + { + if let Err(error) = reconcile_desired_state( + state, + sandbox_id, + tx, + &mut delivery, + ) + .await + { + warn!( + sandbox_id = %sandbox_id, + session_id = %session_id, + error = %error, + "supervisor session: periodic desired-state reconciliation failed" + ); + } + } + () = &mut bootstrap_timeout, + if !state.supervisor_sessions.is_initialized(sandbox_id, session_id) => + { + warn!( + sandbox_id = %sandbox_id, + session_id = %session_id, + "supervisor session: bootstrap result timed out" + ); + if let Err(error) = state + .compute + .supervisor_bootstrap_failed( + sandbox_id, + "Supervisor configuration bootstrap timed out", + ) + .await + { + warn!(sandbox_id = %sandbox_id, session_id = %session_id, error = %error, "supervisor session: failed to persist bootstrap timeout"); + } + break; + } _ = heartbeat_timer.tick() => { let hb = GatewayMessage { payload: Some(gateway_message::Payload::Heartbeat( @@ -904,16 +1370,159 @@ async fn run_session_loop( } } -fn handle_supervisor_message( +async fn handle_supervisor_message( state: &Arc, sandbox_id: &str, session_id: &str, msg: SupervisorMessage, -) { + delivery: &mut DesiredStateDelivery, +) -> bool { match msg.payload { Some(supervisor_message::Payload::Heartbeat(_)) => { // Heartbeat received — nothing to do for now. } + Some(supervisor_message::Payload::BootstrapResult(result)) => { + if let Err(error) = validate_bootstrap_result(&result) { + warn!( + sandbox_id = %sandbox_id, + session_id = %session_id, + error = %error, + "supervisor session: bootstrap failed" + ); + if let Err(persist_error) = state + .compute + .supervisor_bootstrap_failed(sandbox_id, &error) + .await + { + warn!(sandbox_id = %sandbox_id, session_id = %session_id, error = %persist_error, "supervisor session: failed to persist bootstrap failure"); + } + return false; + } + for (component, component_label, outcome) in [ + ( + "ProviderEnvironment", + "provider environment", + result.provider_environment_outcome, + ), + ( + "InferenceBundle", + "inference bundle", + result.inference_bundle_outcome, + ), + ] { + if ConfigApplyOutcome::try_from(outcome).ok() == Some(ConfigApplyOutcome::Degraded) + { + let message = sanitize_update_error(&result.error, component_label); + if let Err(error) = state + .compute + .supervisor_config_update_result( + sandbox_id, + component, + false, + "DesiredStateBootstrapDegraded", + &message, + ) + .await + { + warn!(sandbox_id = %sandbox_id, session_id = %session_id, error = %error, "supervisor session: failed to persist degraded bootstrap component"); + } + } + } + state + .supervisor_sessions + .mark_initialized(sandbox_id, session_id); + info!( + sandbox_id = %sandbox_id, + session_id = %session_id, + "supervisor session: bootstrap applied" + ); + } + Some(supervisor_message::Payload::RuntimeReady(ready)) => { + if ready.instance_id.is_empty() { + warn!( + sandbox_id = %sandbox_id, + session_id = %session_id, + "supervisor session: runtime-ready signal omitted instance_id" + ); + return false; + } + if !state + .supervisor_sessions + .is_initialized(sandbox_id, session_id) + { + warn!( + sandbox_id = %sandbox_id, + session_id = %session_id, + "supervisor session: runtime-ready arrived before bootstrap result" + ); + return false; + } + let became_ready = state + .supervisor_sessions + .mark_runtime_ready(sandbox_id, session_id); + if became_ready { + mark_supervisor_ready(state, sandbox_id, session_id, &ready.instance_id).await; + } + } + Some(supervisor_message::Payload::ConfigUpdateResult(result)) => { + match delivery.handle_result(&result) { + Ok(Some(applied)) => { + let (component, component_label) = match applied.component { + DesiredStateComponent::SandboxConfig => ("SandboxConfig", "sandbox config"), + DesiredStateComponent::ProviderEnvironment => { + ("ProviderEnvironment", "provider environment") + } + DesiredStateComponent::InferenceBundle => { + ("InferenceBundle", "inference bundle") + } + }; + let healthy = matches!( + applied.outcome, + ConfigApplyOutcome::Applied + | ConfigApplyOutcome::IgnoredDuplicate + | ConfigApplyOutcome::RetainedLocalOverride + ); + let reason = if healthy { + "DesiredStateApplied" + } else if applied.outcome == ConfigApplyOutcome::Degraded { + "DesiredStateDegraded" + } else { + "DesiredStateApplyFailed" + }; + let message = if healthy { + format!("Latest {component_label} desired state applied") + } else { + sanitize_update_error(&result.error, component_label) + }; + if let Err(error) = state + .compute + .supervisor_config_update_result( + sandbox_id, component, healthy, reason, &message, + ) + .await + { + warn!( + sandbox_id = %sandbox_id, + session_id = %session_id, + request_id = %result.request_id, + error = %error, + "supervisor session: failed to persist config update result" + ); + } + } + Ok(None) => {} + Err(error) => { + warn!( + sandbox_id = %sandbox_id, + session_id = %session_id, + request_id = %result.request_id, + error = %error, + "supervisor session: invalid config update result" + ); + return false; + } + } + } Some(supervisor_message::Payload::RelayOpenResult(result)) => { if result.success { info!( @@ -953,6 +1562,92 @@ fn handle_supervisor_message( ); } } + true +} + +fn validate_bootstrap_result(result: &ConfigBootstrapResult) -> Result<(), String> { + if ConfigBootstrapStatus::try_from(result.status).ok() != Some(ConfigBootstrapStatus::Ready) { + return Err(sanitize_session_error(&result.error)); + } + let config_outcome = ConfigApplyOutcome::try_from(result.sandbox_config_outcome).ok(); + if !matches!( + config_outcome, + Some( + ConfigApplyOutcome::Applied + | ConfigApplyOutcome::IgnoredDuplicate + | ConfigApplyOutcome::RetainedLocalOverride + ) + ) { + return Err("sandbox_config: invalid required bootstrap outcome".to_string()); + } + for (component, outcome) in [ + ("provider_environment", result.provider_environment_outcome), + ("inference_bundle", result.inference_bundle_outcome), + ] { + if !matches!( + ConfigApplyOutcome::try_from(outcome).ok(), + Some( + ConfigApplyOutcome::Applied + | ConfigApplyOutcome::IgnoredDuplicate + | ConfigApplyOutcome::RetainedLocalOverride + | ConfigApplyOutcome::Degraded + ) + ) { + return Err(format!("{component}: invalid bootstrap outcome")); + } + } + Ok(()) +} + +fn sanitize_session_error(error: &str) -> String { + const MAX_ERROR_BYTES: usize = 1024; + let mut sanitized: String = error + .chars() + .filter(|character| !character.is_control()) + .collect(); + if sanitized.len() > MAX_ERROR_BYTES { + let mut boundary = MAX_ERROR_BYTES; + while !sanitized.is_char_boundary(boundary) { + boundary -= 1; + } + sanitized.truncate(boundary); + } + if sanitized.is_empty() { + "bootstrap rejected without an error".to_string() + } else { + sanitized + } +} + +fn sanitize_update_error(error: &str, component: &str) -> String { + let sanitized = sanitize_session_error(error); + if error.is_empty() { + format!("Latest {component} desired state was not applied") + } else { + sanitized + } +} + +async fn mark_supervisor_ready( + state: &Arc, + sandbox_id: &str, + session_id: &str, + instance_id: &str, +) { + if let Err(err) = state + .compute + .supervisor_session_connected(sandbox_id, instance_id) + .await + { + warn!( + sandbox_id = %sandbox_id, + session_id = %session_id, + error = %err, + "supervisor session: failed to mark sandbox ready" + ); + } else { + state.telemetry.sandbox_session_connected(sandbox_id); + } } // --------------------------------------------------------------------------- @@ -1033,6 +1728,224 @@ mod tests { }) } + fn desired_state_bootstrap() -> ConfigBootstrap { + ConfigBootstrap { + sandbox_config: Some(openshell_core::proto::SandboxConfigSnapshot { + config_revision: 11, + ..Default::default() + }), + provider_environment: Some(openshell_core::proto::ProviderEnvironmentSnapshot { + provider_env_revision: 22, + ..Default::default() + }), + inference_bundle: Some(openshell_core::proto::InferenceBundleSnapshot { + revision: "inference-33".to_string(), + ..Default::default() + }), + } + } + + fn mark_session_ready( + registry: &SupervisorSessionRegistry, + sandbox_id: &str, + session_id: &str, + ) { + assert!(!registry.mark_initialized(sandbox_id, session_id)); + assert!(registry.mark_runtime_ready(sandbox_id, session_id)); + } + + #[test] + fn readiness_requires_initialized_and_runtime_ready() { + let registry = SupervisorSessionRegistry::new(); + let (tx, _rx) = mpsc::channel(1); + registry.register( + "sbx".to_string(), + "session".to_string(), + tx, + make_shutdown(), + ); + + assert!(!registry.has_ready_session("sbx")); + assert!(!registry.mark_initialized("sbx", "session")); + assert!(!registry.has_ready_session("sbx")); + assert!(registry.mark_runtime_ready("sbx", "session")); + assert!(registry.has_ready_session("sbx")); + } + + #[test] + fn desired_state_delivery_uses_bootstrap_fingerprints() { + let delivery = DesiredStateDelivery::new(&desired_state_bootstrap()).unwrap(); + + assert_eq!(delivery.sandbox_config.applied_revision, "11"); + assert_eq!(delivery.provider_environment.applied_revision, "22"); + assert_eq!(delivery.inference_bundle.applied_revision, "inference-33"); + } + + #[tokio::test] + async fn component_delivery_coalesces_while_one_update_is_in_flight() { + let mut delivery = DesiredStateDelivery::new(&desired_state_bootstrap()).unwrap(); + let (tx, mut rx) = mpsc::channel(4); + let payload = || { + config_update::Component::SandboxConfig(SandboxConfigUpdate { + snapshot: Some(openshell_core::proto::SandboxConfigSnapshot::default()), + }) + }; + + send_component_update( + &tx, + DesiredStateComponent::SandboxConfig, + "12".to_string(), + payload(), + &mut delivery, + ) + .await + .unwrap(); + send_component_update( + &tx, + DesiredStateComponent::SandboxConfig, + "13".to_string(), + payload(), + &mut delivery, + ) + .await + .unwrap(); + + let first = rx.recv().await.unwrap(); + assert!(rx.try_recv().is_err()); + assert!(delivery.has_pending_update()); + let gateway_message::Payload::ConfigUpdate(first) = first.payload.unwrap() else { + panic!("expected config update"); + }; + assert_eq!(first.component_sequence, 1); + let applied = delivery + .handle_result(&ConfigUpdateResult { + request_id: first.request_id, + component_sequence: first.component_sequence, + outcome: ConfigApplyOutcome::Applied as i32, + error: String::new(), + }) + .unwrap() + .unwrap(); + assert_eq!(applied.component, DesiredStateComponent::SandboxConfig); + assert!(delivery.has_pending_update()); + + send_component_update( + &tx, + DesiredStateComponent::SandboxConfig, + "13".to_string(), + payload(), + &mut delivery, + ) + .await + .unwrap(); + let second = rx.recv().await.unwrap(); + let gateway_message::Payload::ConfigUpdate(second) = second.payload.unwrap() else { + panic!("expected config update"); + }; + assert_eq!(second.component_sequence, 2); + assert!(!delivery.has_pending_update()); + } + + #[tokio::test] + async fn failed_update_waits_for_reconciliation_before_retrying() { + let mut delivery = DesiredStateDelivery::new(&desired_state_bootstrap()).unwrap(); + let (tx, mut rx) = mpsc::channel(2); + let payload = || { + config_update::Component::SandboxConfig(SandboxConfigUpdate { + snapshot: Some(openshell_core::proto::SandboxConfigSnapshot::default()), + }) + }; + + send_component_update( + &tx, + DesiredStateComponent::SandboxConfig, + "12".to_string(), + payload(), + &mut delivery, + ) + .await + .unwrap(); + let first = rx.recv().await.unwrap(); + let gateway_message::Payload::ConfigUpdate(first) = first.payload.unwrap() else { + panic!("expected config update"); + }; + delivery + .handle_result(&ConfigUpdateResult { + request_id: first.request_id, + component_sequence: first.component_sequence, + outcome: ConfigApplyOutcome::Failed as i32, + error: "rejected".to_string(), + }) + .unwrap(); + + assert!(!delivery.has_pending_update()); + assert_eq!(delivery.sandbox_config.applied_revision, "11"); + assert!(rx.try_recv().is_err()); + + send_component_update( + &tx, + DesiredStateComponent::SandboxConfig, + "12".to_string(), + payload(), + &mut delivery, + ) + .await + .unwrap(); + let retry = rx.recv().await.unwrap(); + let gateway_message::Payload::ConfigUpdate(retry) = retry.payload.unwrap() else { + panic!("expected retry update"); + }; + assert_eq!(retry.component_sequence, 2); + } + + #[test] + fn mismatched_component_sequence_is_a_protocol_error() { + let mut delivery = DesiredStateDelivery::new(&desired_state_bootstrap()).unwrap(); + delivery.sandbox_config.in_flight = Some(InFlightUpdate { + request_id: "request".to_string(), + component_sequence: 4, + revision: "12".to_string(), + sent_at: Instant::now(), + }); + + let error = delivery + .handle_result(&ConfigUpdateResult { + request_id: "request".to_string(), + component_sequence: 5, + outcome: ConfigApplyOutcome::Applied as i32, + error: String::new(), + }) + .unwrap_err(); + assert!(error.contains("component_sequence mismatch")); + assert!(delivery.sandbox_config.in_flight.is_some()); + } + + #[test] + fn bootstrap_allows_degraded_optional_components_only() { + let optional_degraded = ConfigBootstrapResult { + status: ConfigBootstrapStatus::Ready as i32, + sandbox_config_outcome: ConfigApplyOutcome::Applied as i32, + provider_environment_outcome: ConfigApplyOutcome::Degraded as i32, + inference_bundle_outcome: ConfigApplyOutcome::RetainedLocalOverride as i32, + error: String::new(), + }; + assert!(validate_bootstrap_result(&optional_degraded).is_ok()); + + let required_degraded = ConfigBootstrapResult { + sandbox_config_outcome: ConfigApplyOutcome::Degraded as i32, + ..optional_degraded + }; + assert!(validate_bootstrap_result(&required_degraded).is_err()); + } + + #[test] + fn reconciliation_interval_stays_within_twenty_percent() { + for sandbox_id in ["a", "sandbox-1", "sandbox-2", "a-longer-sandbox-id"] { + let interval = reconciliation_interval(sandbox_id); + assert!((24..=36).contains(&interval.as_secs())); + } + } + // ---- registry: register / remove ---- #[test] @@ -1141,6 +2054,7 @@ mod tests { let registry = SupervisorSessionRegistry::new(); let (tx, mut rx) = mpsc::channel(4); registry.register("sbx".to_string(), "s1".to_string(), tx, make_shutdown()); + mark_session_ready(®istry, "sbx", "s1"); let (channel_id, _relay_rx) = registry .open_relay("sbx", Duration::from_secs(1)) @@ -1184,6 +2098,7 @@ mod tests { tx, make_shutdown(), ); + mark_session_ready(®istry_for_register, "sbx", "s1"); }); let result = registry.open_relay("sbx", Duration::from_secs(2)).await; @@ -1198,6 +2113,7 @@ mod tests { let registry = SupervisorSessionRegistry::new(); let (tx, rx) = mpsc::channel::(4); registry.register("sbx".to_string(), "s1".to_string(), tx, make_shutdown()); + mark_session_ready(®istry, "sbx", "s1"); // Simulate the supervisor's stream going away between lookup and send: // the receiver held by `ReceiverStream` is dropped. @@ -1223,6 +2139,8 @@ mod tests { make_shutdown(), ); registry.register("sbx-b".to_string(), "s-b".to_string(), tx, make_shutdown()); + mark_session_ready(®istry, "sbx-a", "s-a"); + mark_session_ready(®istry, "sbx-b", "s-b"); // Pre-seed pending_relays to exactly the global cap, split across two // sandboxes so neither hits the per-sandbox cap first. @@ -1251,6 +2169,7 @@ mod tests { let registry = SupervisorSessionRegistry::new(); let (tx, _rx) = mpsc::channel::(8); registry.register("sbx".to_string(), "s".to_string(), tx, make_shutdown()); + mark_session_ready(®istry, "sbx", "s"); { let mut pending = registry.pending_relays.lock().unwrap(); @@ -1278,6 +2197,7 @@ mod tests { tx2, make_shutdown(), ); + mark_session_ready(®istry, "sbx-other", "s-other"); registry .open_relay("sbx-other", Duration::from_millis(50)) .await @@ -1309,6 +2229,7 @@ mod tests { tx_new, make_shutdown(), ); + mark_session_ready(®istry, "sbx", "s-new"); let (_channel_id, _relay_rx) = registry .open_relay("sbx", Duration::from_secs(1)) @@ -1369,6 +2290,7 @@ mod tests { tx_old, make_shutdown(), ); + mark_session_ready(®istry, "sbx", "s-old"); let (channel_id, _relay_rx) = registry .open_relay("sbx", Duration::from_secs(1)) @@ -1391,6 +2313,7 @@ mod tests { make_shutdown(), ); assert!(superseded); + mark_session_ready(®istry, "sbx", "s-new"); registry .replay_pending_relays("sbx", ®istry.lookup_session("sbx").unwrap()) diff --git a/crates/openshell-server/tests/common/mod.rs b/crates/openshell-server/tests/common/mod.rs index 96b620a230..414aa815e3 100644 --- a/crates/openshell-server/tests/common/mod.rs +++ b/crates/openshell-server/tests/common/mod.rs @@ -18,14 +18,13 @@ use openshell_core::proto::{ DeleteProviderRequest, DeleteProviderResponse, DeleteSandboxRequest, DeleteSandboxResponse, ExchangeProviderSubjectTokenRequest, ExchangeProviderSubjectTokenResponse, ExecSandboxEvent, ExecSandboxInput, ExecSandboxRequest, GatewayMessage, GetGatewayConfigRequest, - GetGatewayConfigResponse, GetProviderRequest, GetSandboxConfigRequest, - GetSandboxConfigResponse, GetSandboxProviderEnvironmentRequest, - GetSandboxProviderEnvironmentResponse, GetSandboxRequest, HealthRequest, HealthResponse, - IssueSandboxTokenRequest, IssueSandboxTokenResponse, ListProvidersRequest, - ListProvidersResponse, ListSandboxesRequest, ListSandboxesResponse, ProviderResponse, - RefreshSandboxTokenRequest, RefreshSandboxTokenResponse, RelayFrame, RevokeSshSessionRequest, - RevokeSshSessionResponse, SandboxResponse, SandboxStreamEvent, ServiceStatus, - SupervisorMessage, TcpForwardFrame, UpdateProviderRequest, WatchSandboxRequest, + GetGatewayConfigResponse, GetProviderRequest, GetSandboxConfigRequest, GetSandboxRequest, + HealthRequest, HealthResponse, IssueSandboxTokenRequest, IssueSandboxTokenResponse, + ListProvidersRequest, ListProvidersResponse, ListSandboxesRequest, ListSandboxesResponse, + ProviderResponse, RefreshSandboxTokenRequest, RefreshSandboxTokenResponse, RelayFrame, + RevokeSshSessionRequest, RevokeSshSessionResponse, SandboxConfigSnapshot, SandboxResponse, + SandboxStreamEvent, ServiceStatus, SupervisorMessage, TcpForwardFrame, UpdateProviderRequest, + WatchSandboxRequest, open_shell_client::OpenShellClient, open_shell_server::{OpenShell, OpenShellServer}, }; @@ -156,8 +155,8 @@ impl OpenShell for TestOpenShell { async fn get_sandbox_config( &self, _request: tonic::Request, - ) -> Result, Status> { - Ok(Response::new(GetSandboxConfigResponse::default())) + ) -> Result, Status> { + Ok(Response::new(SandboxConfigSnapshot::default())) } async fn get_gateway_config( @@ -167,15 +166,6 @@ impl OpenShell for TestOpenShell { Ok(Response::new(GetGatewayConfigResponse::default())) } - async fn get_sandbox_provider_environment( - &self, - _request: tonic::Request, - ) -> Result, Status> { - Ok(Response::new( - GetSandboxProviderEnvironmentResponse::default(), - )) - } - async fn create_ssh_session( &self, _request: tonic::Request, diff --git a/crates/openshell-server/tests/supervisor_relay_integration.rs b/crates/openshell-server/tests/supervisor_relay_integration.rs index 32ef513adb..33254999f8 100644 --- a/crates/openshell-server/tests/supervisor_relay_integration.rs +++ b/crates/openshell-server/tests/supervisor_relay_integration.rs @@ -188,7 +188,7 @@ impl OpenShell for RelayGateway { async fn get_sandbox_config( &self, _: tonic::Request, - ) -> Result, Status> { + ) -> Result, Status> { Err(Status::unimplemented("unused")) } async fn get_gateway_config( @@ -197,13 +197,6 @@ impl OpenShell for RelayGateway { ) -> Result, Status> { Err(Status::unimplemented("unused")) } - async fn get_sandbox_provider_environment( - &self, - _: tonic::Request, - ) -> Result, Status> - { - Err(Status::unimplemented("unused")) - } async fn create_ssh_session( &self, _: tonic::Request, @@ -550,6 +543,8 @@ fn register_session_with_capacity( tx, shutdown_tx, ); + assert!(!registry.mark_initialized(sandbox_id, "sess-1")); + assert!(registry.mark_runtime_ready(sandbox_id, "sess-1")); rx } diff --git a/crates/openshell-supervisor-network/src/inference_routes.rs b/crates/openshell-supervisor-network/src/inference_routes.rs index 75a26a2499..88e7f31b0f 100644 --- a/crates/openshell-supervisor-network/src/inference_routes.rs +++ b/crates/openshell-supervisor-network/src/inference_routes.rs @@ -5,9 +5,9 @@ //! //! Resolves inference routes from one of two sources at sandbox startup: //! a local YAML file (`--inference-routes`) or a cluster bundle fetched via -//! gRPC. Builds the [`InferenceContext`] consumed by the proxy's L7 layer -//! and spawns a background refresh loop in cluster mode so route changes -//! propagate without restarting the sandbox. +//! the supervisor session. Builds the [`InferenceContext`] consumed by the +//! proxy's L7 layer. Later cluster changes replace the shared route caches +//! through desired-state delivery. //! //! Distinct from [`crate::l7::inference`], which parses HTTP requests and //! matches them against API patterns at request time. @@ -18,20 +18,11 @@ use std::sync::Arc; use std::time::Duration; use miette::Result; -use tracing::{info, trace, warn}; use openshell_ocsf::{ ConfigStateChangeBuilder, SeverityId, StateId, StatusId, ctx::ctx as ocsf_ctx, ocsf_emit, }; -/// Default interval (seconds) for re-fetching the inference route bundle from -/// the gateway in cluster mode. -/// -/// Override at runtime with the `OPENSHELL_ROUTE_REFRESH_INTERVAL_SECS` -/// environment variable. File-based routes (`--inference-routes`) are loaded -/// once at startup and never refreshed. -pub const DEFAULT_ROUTE_REFRESH_INTERVAL_SECS: u64 = 5; - /// Route name for the sandbox system inference route. const SANDBOX_SYSTEM_ROUTE_NAME: &str = "sandbox-system"; @@ -60,31 +51,6 @@ pub fn disable_inference_on_empty_routes(source: InferenceRouteSource) -> bool { !matches!(source, InferenceRouteSource::Cluster) } -pub fn route_refresh_interval_secs() -> u64 { - let Ok(value) = std::env::var("OPENSHELL_ROUTE_REFRESH_INTERVAL_SECS") else { - return DEFAULT_ROUTE_REFRESH_INTERVAL_SECS; - }; - match value.parse::() { - Ok(interval) if interval > 0 => interval, - Ok(_) => { - warn!( - default_interval_secs = DEFAULT_ROUTE_REFRESH_INTERVAL_SECS, - "Ignoring zero route refresh interval" - ); - DEFAULT_ROUTE_REFRESH_INTERVAL_SECS - } - Err(error) => { - warn!( - interval = %value, - error = %error, - default_interval_secs = DEFAULT_ROUTE_REFRESH_INTERVAL_SECS, - "Ignoring invalid route refresh interval" - ); - DEFAULT_ROUTE_REFRESH_INTERVAL_SECS - } - } -} - /// Build an [`InferenceContext`](crate::proxy::InferenceContext) by resolving /// inference routes from either a local YAML file or the gateway bundle. /// @@ -96,25 +62,45 @@ pub fn route_refresh_interval_secs() -> u64 { /// # Errors /// /// Returns an error if loading the routes file fails or the file's routes -/// cannot be resolved. gRPC errors are swallowed (logged) and produce -/// `Ok(None)` so a missing cluster bundle disables inference routing rather -/// than aborting sandbox startup. +/// cannot be resolved. The caller decides whether a gateway-snapshot failure +/// is fatal or establishes a degraded inference state. // `routes`/`router` are intentionally distinct nouns (the route list vs the // router that consumes them); both names are clearer than alternatives. #[allow(clippy::similar_names)] -pub async fn build_inference_context( +pub fn build_inference_context( + sandbox_id: Option<&str>, + openshell_endpoint: Option<&str>, + inference_routes: Option<&str>, +) -> Result>> { + build_inference_context_inner(sandbox_id, openshell_endpoint, inference_routes, None) +} + +pub fn build_inference_context_from_snapshot( sandbox_id: Option<&str>, openshell_endpoint: Option<&str>, inference_routes: Option<&str>, + gateway_bundle: openshell_core::proto::InferenceBundleSnapshot, +) -> Result>> { + build_inference_context_inner( + sandbox_id, + openshell_endpoint, + inference_routes, + Some(gateway_bundle), + ) +} + +#[allow(clippy::similar_names)] +fn build_inference_context_inner( + sandbox_id: Option<&str>, + openshell_endpoint: Option<&str>, + inference_routes: Option<&str>, + gateway_bundle: Option, ) -> Result>> { use openshell_router::Router; use openshell_router::config::RouterConfig; let source = infer_route_source(sandbox_id, openshell_endpoint, inference_routes); - - // Captured during the initial cluster bundle fetch so the background refresh - // loop can skip no-op updates from the very first tick. - let mut initial_revision: Option = None; + let session_delivered = gateway_bundle.is_some(); let routes = match source { InferenceRouteSource::File => { @@ -150,62 +136,30 @@ pub async fn build_inference_context( .map_err(|e| miette::miette!("failed to resolve routes from {path}: {e}"))? } InferenceRouteSource::Cluster => { - let (Some(_id), Some(endpoint)) = (sandbox_id, openshell_endpoint) else { + let (Some(_id), Some(_endpoint)) = (sandbox_id, openshell_endpoint) else { return Ok(None); }; - // Cluster mode: fetch bundle from gateway - info!(endpoint = %endpoint, "Fetching inference route bundle from gateway"); - match openshell_core::grpc_client::fetch_inference_bundle(endpoint).await { - Ok(bundle) => { - initial_revision = Some(bundle.revision.clone()); - ocsf_emit!( - ConfigStateChangeBuilder::new(ocsf_ctx()) - .severity(SeverityId::Informational) - .status(StatusId::Success) - .state(StateId::Enabled, "loaded") - .unmapped("route_count", serde_json::json!(bundle.routes.len())) - .unmapped("revision", serde_json::json!(&bundle.revision)) - .message(format!( - "Loaded inference route bundle [route_count:{} revision:{}]", - bundle.routes.len(), - bundle.revision - )) - .build() - ); - bundle_to_resolved_routes(&bundle) - } - Err(e) => { - // Distinguish expected "not configured" states from server errors. - // gRPC PermissionDenied/NotFound means inference bundle is unavailable - // for this sandbox — skip gracefully. Other errors are unexpected. - let msg = e.to_string(); - if msg.contains("permission denied") || msg.contains("not found") { - ocsf_emit!( - ConfigStateChangeBuilder::new(ocsf_ctx()) - .severity(SeverityId::Informational) - .status(StatusId::Success) - .state(StateId::Disabled, "disabled") - .unmapped("error", serde_json::json!(e.to_string())) - .message(format!( - "Inference bundle unavailable, routing disabled [error:{e}]" - )) - .build() - ); - return Ok(None); - } - ocsf_emit!(ConfigStateChangeBuilder::new(ocsf_ctx()) - .severity(SeverityId::Medium) - .status(StatusId::Failure) - .state(StateId::Disabled, "disabled") - .unmapped("error", serde_json::json!(e.to_string())) - .message(format!( - "Failed to fetch inference bundle, inference routing disabled [error:{e}]" - )) - .build()); - return Ok(None); - } - } + let Some(bundle) = gateway_bundle else { + return Err(miette::miette!( + "gateway-backed inference requires a supervisor-session bootstrap snapshot" + )); + }; + ocsf_emit!( + ConfigStateChangeBuilder::new(ocsf_ctx()) + .severity(SeverityId::Informational) + .status(StatusId::Success) + .state(StateId::Enabled, "loaded") + .unmapped("route_count", serde_json::json!(bundle.routes.len())) + .unmapped("revision", serde_json::json!(&bundle.revision)) + .message(format!( + "Loaded inference route bundle [route_count:{} revision:{}]", + bundle.routes.len(), + bundle.revision + )) + .build() + ); + bundle_to_resolved_routes(&bundle) } InferenceRouteSource::None => { // No route source — inference routing is not configured @@ -261,19 +215,12 @@ pub async fn build_inference_context( system_routes, )); - // Spawn background route cache refresh for cluster mode at startup so - // request handling never depends on control-plane latency. - if matches!(source, InferenceRouteSource::Cluster) - && let (Some(_id), Some(endpoint)) = (sandbox_id, openshell_endpoint) - { - spawn_route_refresh( - ctx.route_cache(), - ctx.system_route_cache(), - endpoint.to_string(), - route_refresh_interval_secs(), - initial_revision, - ); - } + // Cluster request handling consumes only session-delivered cache state and + // never depends on control-plane latency. + debug_assert!( + !matches!(source, InferenceRouteSource::Cluster) || session_delivered, + "cluster inference must be session-delivered" + ); Ok(Some(ctx)) } @@ -302,7 +249,7 @@ pub fn partition_routes( /// Convert a proto bundle response into resolved routes for the router. pub fn bundle_to_resolved_routes( - bundle: &openshell_core::proto::GetInferenceBundleResponse, + bundle: &openshell_core::proto::InferenceBundleSnapshot, ) -> Vec { bundle .routes @@ -332,64 +279,14 @@ pub fn bundle_to_resolved_routes( .collect() } -/// Spawn a background task that periodically refreshes both route caches from the gateway. -/// -/// The loop uses the bundle `revision` hash to avoid unnecessary cache writes -/// when routes haven't changed. `initial_revision` is the revision captured -/// during the startup fetch in [`build_inference_context`] so the first refresh -/// cycle can already skip a no-op update. -pub fn spawn_route_refresh( - user_cache: Arc>>, - system_cache: Arc>>, - endpoint: String, - interval_secs: u64, - initial_revision: Option, -) { - tokio::spawn(async move { - use tokio::time::{MissedTickBehavior, interval}; - - let mut current_revision = initial_revision; - - let mut tick = interval(Duration::from_secs(interval_secs)); - tick.set_missed_tick_behavior(MissedTickBehavior::Skip); - - loop { - tick.tick().await; - - match openshell_core::grpc_client::fetch_inference_bundle(&endpoint).await { - Ok(bundle) => { - if current_revision.as_deref() == Some(&bundle.revision) { - trace!(revision = %bundle.revision, "Inference bundle unchanged"); - continue; - } - - current_revision = - Some(apply_inference_bundle(&user_cache, &system_cache, bundle).await); - } - Err(e) => { - ocsf_emit!(ConfigStateChangeBuilder::new(ocsf_ctx()) - .severity(SeverityId::Medium) - .status(StatusId::Failure) - .state(StateId::Other, "stale") - .unmapped("error", serde_json::json!(e.to_string())) - .message(format!( - "Failed to refresh inference route cache, keeping stale routes [error:{e}]" - )) - .build()); - } - } - } - }); -} - /// Apply one complete inference bundle to the live user and system route caches. /// -/// Fetching and revision comparison stay with the caller so polling and future -/// supervisor-session updates can share this cache-installation boundary. +/// Revision comparison stays with the caller so bootstrap and steady-state +/// supervisor-session updates share this cache-installation boundary. pub async fn apply_inference_bundle( user_cache: &tokio::sync::RwLock>, system_cache: &tokio::sync::RwLock>, - bundle: openshell_core::proto::GetInferenceBundleResponse, + bundle: openshell_core::proto::InferenceBundleSnapshot, ) -> String { let routes = bundle_to_resolved_routes(&bundle); let (user_routes, system_routes) = partition_routes(routes); @@ -422,14 +319,10 @@ pub async fn apply_inference_bundle( )] mod tests { use super::*; - use std::sync::{LazyLock, Mutex}; - use temp_env::with_vars; - - static ENV_LOCK: LazyLock> = LazyLock::new(|| Mutex::new(())); #[test] fn bundle_to_resolved_routes_converts_all_fields() { - let bundle = openshell_core::proto::GetInferenceBundleResponse { + let bundle = openshell_core::proto::InferenceBundleSnapshot { routes: vec![ openshell_core::proto::ResolvedRoute { name: "frontier".to_string(), @@ -494,7 +387,7 @@ mod tests { #[test] fn bundle_to_resolved_routes_handles_empty_bundle() { - let bundle = openshell_core::proto::GetInferenceBundleResponse { + let bundle = openshell_core::proto::InferenceBundleSnapshot { routes: vec![], revision: "empty".to_string(), generated_at_ms: 0, @@ -508,7 +401,7 @@ mod tests { async fn inference_bundle_apply_replaces_both_route_caches() { let user_cache = tokio::sync::RwLock::new(Vec::new()); let system_cache = tokio::sync::RwLock::new(Vec::new()); - let bundle = openshell_core::proto::GetInferenceBundleResponse { + let bundle = openshell_core::proto::InferenceBundleSnapshot { routes: vec![ openshell_core::proto::ResolvedRoute { name: "inference.local".to_string(), @@ -536,7 +429,7 @@ mod tests { #[test] fn bundle_to_resolved_routes_preserves_name_field() { - let bundle = openshell_core::proto::GetInferenceBundleResponse { + let bundle = openshell_core::proto::InferenceBundleSnapshot { routes: vec![openshell_core::proto::ResolvedRoute { name: "sandbox-system".to_string(), base_url: "https://api.example.com/v1".to_string(), @@ -612,9 +505,8 @@ routes: f.write_all(yaml.as_bytes()).unwrap(); let path = f.path().to_str().unwrap(); - let ctx = build_inference_context(None, None, Some(path)) - .await - .expect("should load routes from file"); + let ctx = + build_inference_context(None, None, Some(path)).expect("should load routes from file"); let ctx = ctx.expect("context should be Some"); let cache = ctx.route_cache(); @@ -634,7 +526,6 @@ routes: let path = f.path().to_str().unwrap(); let ctx = build_inference_context(None, None, Some(path)) - .await .expect("empty routes file should not error"); assert!( ctx.is_none(), @@ -644,9 +535,7 @@ routes: #[tokio::test] async fn build_inference_context_no_sources_returns_none() { - let ctx = build_inference_context(None, None, None) - .await - .expect("should succeed with None"); + let ctx = build_inference_context(None, None, None).expect("should succeed with None"); assert!(ctx.is_none(), "no sources should return None"); } @@ -669,7 +558,6 @@ routes: // Even with sandbox_id and endpoint, route_file takes precedence let ctx = build_inference_context(Some("sb-1"), Some("http://localhost:50051"), Some(path)) - .await .expect("should load from file"); let ctx = ctx.expect("context should be Some"); @@ -719,52 +607,6 @@ routes: )); } - // ---- Route refresh interval + revision tests ---- - - #[test] - fn default_route_refresh_interval_is_five_seconds() { - assert_eq!(DEFAULT_ROUTE_REFRESH_INTERVAL_SECS, 5); - } - - #[test] - fn route_refresh_interval_uses_env_override() { - let _guard = ENV_LOCK.lock().unwrap(); - with_vars( - [("OPENSHELL_ROUTE_REFRESH_INTERVAL_SECS", Some("9"))], - || { - assert_eq!(route_refresh_interval_secs(), 9); - }, - ); - } - - #[test] - fn route_refresh_interval_rejects_zero() { - let _guard = ENV_LOCK.lock().unwrap(); - with_vars( - [("OPENSHELL_ROUTE_REFRESH_INTERVAL_SECS", Some("0"))], - || { - assert_eq!( - route_refresh_interval_secs(), - DEFAULT_ROUTE_REFRESH_INTERVAL_SECS - ); - }, - ); - } - - #[test] - fn route_refresh_interval_rejects_invalid_values() { - let _guard = ENV_LOCK.lock().unwrap(); - with_vars( - [("OPENSHELL_ROUTE_REFRESH_INTERVAL_SECS", Some("abc"))], - || { - assert_eq!( - route_refresh_interval_secs(), - DEFAULT_ROUTE_REFRESH_INTERVAL_SECS - ); - }, - ); - } - #[tokio::test] async fn route_cache_preserves_content_when_not_written() { use std::sync::Arc; diff --git a/crates/openshell-supervisor-network/src/l7/relay.rs b/crates/openshell-supervisor-network/src/l7/relay.rs index 2697fedb3c..dfbfb5bef2 100644 --- a/crates/openshell-supervisor-network/src/l7/relay.rs +++ b/crates/openshell-supervisor-network/src/l7/relay.rs @@ -42,7 +42,7 @@ pub struct L7EvalContext { pub host: String, /// Port from the CONNECT request. pub port: u16, - /// Workspace the sandbox belongs to, learned from `GetSandboxConfigResponse`. + /// Workspace the sandbox belongs to, learned from `SandboxConfigSnapshot`. pub workspace: String, /// Default authority port for the inspected HTTP transport (80 for /// plaintext, 443 after TLS termination). diff --git a/crates/openshell-supervisor-network/src/policy_local.rs b/crates/openshell-supervisor-network/src/policy_local.rs index 908587ed31..0286eaabc4 100644 --- a/crates/openshell-supervisor-network/src/policy_local.rs +++ b/crates/openshell-supervisor-network/src/policy_local.rs @@ -881,7 +881,7 @@ fn is_terminal_status(status: &str) -> bool { /// The polling cadence here is faster than `PROPOSAL_WAIT_POLL_INTERVAL` /// (which paces upstream gateway calls). This loop only reads in-memory /// state, so 200ms gives a responsive handoff to the agent's retry once -/// the supervisor's own policy poll catches up. +/// the supervisor's desired-state update catches up. async fn wait_for_local_policy_to_cover( ctx: &PolicyLocalContext, proposed_rule: &NetworkPolicyRule, diff --git a/crates/openshell-supervisor-network/src/run.rs b/crates/openshell-supervisor-network/src/run.rs index 2a71702b4b..9925232670 100644 --- a/crates/openshell-supervisor-network/src/run.rs +++ b/crates/openshell-supervisor-network/src/run.rs @@ -151,10 +151,15 @@ pub struct Networking { pub proxy: Option, pub ca_file_paths: Option<(std::path::PathBuf, std::path::PathBuf)>, - /// Policy-local route context: shared with the orchestrator's policy poll + /// Policy-local route context: shared with the orchestrator's desired-state /// loop so it can publish updated `SandboxPolicy` snapshots that the /// `policy.local` route handler returns to the workload. pub policy_local_ctx: Arc, + /// Shared inference route caches for desired-state updates. + pub inference_context: Option>, + /// The gateway inference snapshot could not initialize a safe live router. + /// Local route-file failures remain fatal and never set this flag. + pub inference_degraded: bool, #[cfg(target_os = "linux")] _policy_dns: Option, #[cfg(target_os = "linux")] @@ -191,6 +196,7 @@ pub async fn run_networking( sandbox_name: Option<&str>, openshell_endpoint: Option<&str>, #[allow(unused_variables)] inference_routes: Option<&str>, + inference_bundle: Option, denial_tx: Option>, activity_tx: Option, agent_proposals: AgentProposals, @@ -198,7 +204,7 @@ pub async fn run_networking( upstream_proxy_args: &crate::upstream_proxy::UpstreamProxyArgs, #[cfg(target_os = "linux")] transparent_runtime: Option, ) -> Result { - // Build the policy-local route context. The orchestrator's policy poll + // Build the policy-local route context. The orchestrator's desired-state // loop also holds an `Arc` clone (via `Networking::policy_local_ctx`) so // it can publish updated policy snapshots after a successful reload. let policy_local_ctx = Arc::new(PolicyLocalContext::new( @@ -402,6 +408,8 @@ pub async fn run_networking( (None, None) }; + let mut inference_context = None; + let mut inference_degraded = false; let proxy_handle = if matches!(policy.network.mode, NetworkMode::Proxy) { let proxy_policy = policy.network.proxy.as_ref().ok_or_else(|| { miette::miette!("Network mode is set to proxy but no proxy configuration was provided") @@ -426,12 +434,39 @@ pub async fn run_networking( }); // Build inference context for local routing of intercepted inference calls. - let inference_ctx = crate::inference_routes::build_inference_context( - sandbox_id, - openshell_endpoint, - inference_routes, - ) - .await?; + let inference_ctx = if let Some(bundle) = inference_bundle { + match crate::inference_routes::build_inference_context_from_snapshot( + sandbox_id, + openshell_endpoint, + inference_routes, + bundle, + ) { + Ok(context) => context, + Err(error) if inference_routes.is_none() => { + inference_degraded = true; + warn!(error = %error, "Gateway inference bootstrap degraded; inference routing disabled"); + ocsf_emit!( + ConfigStateChangeBuilder::new(ocsf_ctx()) + .severity(SeverityId::Medium) + .status(StatusId::Failure) + .state(StateId::Disabled, "degraded") + .message( + "Gateway inference bootstrap degraded; inference routing disabled" + ) + .build() + ); + None + } + Err(error) => return Err(error), + } + } else { + crate::inference_routes::build_inference_context( + sandbox_id, + openshell_endpoint, + inference_routes, + )? + }; + inference_context.clone_from(&inference_ctx); let proxy_handle = ProxyHandle::start_with_bind_addr( proxy_policy, @@ -492,6 +527,8 @@ pub async fn run_networking( proxy: proxy_handle, ca_file_paths, policy_local_ctx, + inference_context, + inference_degraded, #[cfg(target_os = "linux")] _policy_dns: policy_dns, #[cfg(target_os = "linux")] diff --git a/crates/openshell-supervisor-process/src/run.rs b/crates/openshell-supervisor-process/src/run.rs index f0e792306d..c6778a6904 100644 --- a/crates/openshell-supervisor-process/src/run.rs +++ b/crates/openshell-supervisor-process/src/run.rs @@ -6,7 +6,7 @@ //! Spawns the SSH server, optional supervisor session, the entrypoint child //! process, and waits for it to exit (with optional timeout). Long-running //! background tasks that aren't strictly tied to the workload's lifetime -//! (policy poll loop, denial aggregator, symlink resolver) live in the +//! (desired-state application, denial aggregation, symlink resolution) live in the //! orchestrator, not here. use miette::{IntoDiagnostic, Result}; @@ -78,6 +78,7 @@ pub async fn run_process( provider_env: std::collections::HashMap, ca_file_paths: Option<(std::path::PathBuf, std::path::PathBuf)>, agent_proposals: AgentProposals, + runtime_ready: Option, #[cfg(target_os = "linux")] netns: Option<&NetworkNamespace>, #[cfg(target_os = "linux")] bypass_denial_tx: Option< tokio::sync::mpsc::UnboundedSender, @@ -112,11 +113,9 @@ pub async fn run_process( )?; } - // Eagerly fetch initial settings and install the agent skill if the - // proposals flag is on at startup, rather than waiting for the policy - // poll loop's first tick. In offline/file-mode there is no gateway, so - // the flag stays at its default (false) and no skill is installed. - install_initial_agent_skill(sandbox_id, openshell_endpoint, &agent_proposals).await; + // The orchestrator initializes this shared flag from the session bootstrap + // (or sidecar bootstrap) before starting the process runtime. + install_initial_agent_skill(&agent_proposals); // Provider token grants may mount supervisor-only identity sockets such as // the SPIFFE Workload API. Prepare the child mount namespace that hides @@ -358,27 +357,18 @@ pub async fn run_process( // that is already terminal. let early_exit = handle.try_wait().into_diagnostic()?; - // Spawn the persistent supervisor session if we have a gateway endpoint - // and sandbox identity. The session provides relay channels for SSH - // connect and ExecSandbox through the gateway. - let supervisor_session_task = if early_exit.is_none() - && let (Some(endpoint), Some(id), Some(socket)) = - (openshell_endpoint, sandbox_id, ssh_socket_path.as_ref()) + if early_exit.is_none() + && let (Some(runtime_ready), Some(socket)) = (runtime_ready.as_ref(), ssh_socket_path) { - let task = crate::supervisor_session::spawn( - endpoint.to_string(), - id.to_string(), - socket.clone(), - ssh_netns_fd, - None, - Arc::clone(&supervisor_terminating), - main_instance_id.clone(), - ); - info!("supervisor session task spawned"); - Some(task) - } else { - None - }; + runtime_ready + .send(Some(crate::supervisor_session::RuntimeReadyState { + instance_id: main_instance_id.clone(), + ssh_socket_path: socket, + netns_fd: ssh_netns_fd, + expected_ssh_peer_pid: None, + })) + .map_err(|_| miette::miette!("supervisor session runtime-ready channel closed"))?; + } // Store the entrypoint PID so the proxy can resolve TCP peer identity entrypoint_pid.store(handle.pid(), Ordering::Release); @@ -446,8 +436,8 @@ pub async fn run_process( .build() ); - if let Some(task) = supervisor_session_task { - task.abort(); + if let Some(runtime_ready) = runtime_ready { + let _ = runtime_ready.send(None); } if let Some(tx) = sidecar_exit_tx { let (ack_tx, ack_rx) = tokio::sync::oneshot::channel(); @@ -620,40 +610,9 @@ fn ssh_proxy_url_for_policy( proxy.http_addr.map(|addr| format!("http://{addr}")) } -/// Eagerly fetch initial settings and install the agent-driven policy -/// proposal skill if the flag is on at startup. -/// -/// Without this, the skill would only get installed on the policy poll -/// loop's first false→true transition, which can be ~10 s after launch — -/// long enough for an agent to start running without seeing it. -/// -/// Best-effort: any failure (no gateway, RPC error, install failure) is -/// logged but does not fail sandbox startup. -async fn install_initial_agent_skill( - sandbox_id: Option<&str>, - openshell_endpoint: Option<&str>, - agent_proposals: &AgentProposals, -) { - use openshell_core::proto::setting_value; - - if let (Some(id), Some(endpoint)) = (sandbox_id, openshell_endpoint) - && let Ok(client) = - openshell_core::grpc_client::CachedOpenShellClient::connect(endpoint).await - && let Ok(result) = client.poll_settings(id).await - { - let initial = result - .settings - .get(openshell_core::settings::AGENT_POLICY_PROPOSALS_ENABLED_KEY) - .and_then(|es| es.value.as_ref()) - .and_then(|sv| sv.value.as_ref()) - .and_then(|v| match v { - setting_value::Value::BoolValue(b) => Some(*b), - _ => None, - }) - .unwrap_or(false); - agent_proposals.set_enabled(initial); - } - +/// Install the agent-driven policy proposal skill when bootstrap enabled it. +/// Installation is best-effort and never fails sandbox startup. +fn install_initial_agent_skill(agent_proposals: &AgentProposals) { if agent_proposals.enabled() { match crate::skills::install_static_skills() { Ok(installed) => info!( diff --git a/crates/openshell-supervisor-process/src/supervisor_session.rs b/crates/openshell-supervisor-process/src/supervisor_session.rs index e8a140e483..f42fcd2f01 100644 --- a/crates/openshell-supervisor-process/src/supervisor_session.rs +++ b/crates/openshell-supervisor-process/src/supervisor_session.rs @@ -19,16 +19,18 @@ use std::time::Duration; use openshell_core::proto::open_shell_client::OpenShellClient; use openshell_core::proto::{ - GatewayMessage, RelayFrame, RelayInit, RelayOpen, RelayOpenResult, - ReportMainProcessExitRequest, SupervisorHeartbeat, SupervisorHello, SupervisorMessage, - TcpRelayTarget, gateway_message, relay_open, supervisor_message, + ConfigApplyOutcome, ConfigBootstrapStatus, ConfigUpdateResult, GatewayMessage, RelayFrame, + RelayInit, RelayOpen, RelayOpenResult, ReportMainProcessExitRequest, SupervisorHeartbeat, + SupervisorHello, SupervisorMessage, SupervisorRuntimeReady, TcpRelayTarget, gateway_message, + relay_open, supervisor_message, }; use openshell_ocsf::{ ActivityId, ConnectionInfo, Endpoint, NetworkActivityBuilder, OcsfEvent, SandboxContext, SeverityId, StatusId, ocsf_emit, }; +use rand::RngExt as _; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; -use tokio::sync::mpsc; +use tokio::sync::{mpsc, oneshot, watch}; use tokio_stream::StreamExt; use tracing::{debug, warn}; @@ -37,7 +39,39 @@ use openshell_core::net::set_tcp_nodelay_best_effort; use openshell_core::transport_errors::is_expected_transport_close_status; const INITIAL_BACKOFF: Duration = Duration::from_secs(1); -const MAX_BACKOFF: Duration = Duration::from_secs(30); +const MAX_BACKOFF: Duration = Duration::from_secs(300); +const BOOTSTRAP_APPLY_TIMEOUT: Duration = Duration::from_secs(120); +const UPDATE_APPLY_TIMEOUT: Duration = Duration::from_secs(60); + +fn jittered_backoff(backoff: Duration) -> Duration { + let percent = rand::rng().random_range(80_u128..=120_u128); + let millis = backoff.as_millis().saturating_mul(percent) / 100; + Duration::from_millis(u64::try_from(millis).unwrap_or(u64::MAX)) +} + +pub enum DesiredStateRequest { + Bootstrap { + bootstrap: Box, + result: oneshot::Sender, + }, + Update { + update: Box, + result: oneshot::Sender, + }, +} + +pub type DesiredStateSender = mpsc::Sender; + +#[derive(Clone, Debug)] +pub struct RuntimeReadyState { + pub instance_id: String, + pub ssh_socket_path: std::path::PathBuf, + pub netns_fd: Option, + pub expected_ssh_peer_pid: Option, +} + +pub type RuntimeReadySender = watch::Sender>; +pub type RuntimeReadyReceiver = watch::Receiver>; /// Parse a gRPC endpoint URI into an OCSF `Endpoint` (host + port). Falls back /// to treating the whole string as a domain if parsing fails. @@ -277,31 +311,25 @@ fn map_session_stream_message( pub fn spawn( endpoint: String, sandbox_id: String, - ssh_socket_path: std::path::PathBuf, - netns_fd: Option, - expected_ssh_peer_pid: Option, terminating: Arc, - instance_id: String, + desired_state: DesiredStateSender, + runtime_ready: RuntimeReadyReceiver, ) -> tokio::task::JoinHandle<()> { tokio::spawn(run_session_loop( endpoint, sandbox_id, - ssh_socket_path, - netns_fd, - expected_ssh_peer_pid, terminating, - instance_id, + desired_state, + runtime_ready, )) } async fn run_session_loop( endpoint: String, sandbox_id: String, - ssh_socket_path: std::path::PathBuf, - netns_fd: Option, - expected_ssh_peer_pid: Option, terminating: Arc, - instance_id: String, + desired_state: DesiredStateSender, + runtime_ready: RuntimeReadyReceiver, ) { let mut backoff = INITIAL_BACKOFF; let mut attempt: u64 = 0; @@ -312,11 +340,9 @@ async fn run_session_loop( match run_single_session( &endpoint, &sandbox_id, - &ssh_socket_path, - netns_fd, - expected_ssh_peer_pid, Arc::clone(&terminating), - &instance_id, + &desired_state, + runtime_ready.clone(), ) .await { @@ -334,7 +360,7 @@ async fn run_session_loop( &e.to_string(), ); ocsf_emit!(event); - tokio::time::sleep(backoff).await; + tokio::time::sleep(jittered_backoff(backoff)).await; backoff = (backoff * 2).min(MAX_BACKOFF); } } @@ -344,11 +370,9 @@ async fn run_session_loop( async fn run_single_session( endpoint: &str, sandbox_id: &str, - ssh_socket_path: &std::path::Path, - netns_fd: Option, - expected_ssh_peer_pid: Option, terminating: Arc, - instance_id: &str, + desired_state: &DesiredStateSender, + mut runtime_ready: RuntimeReadyReceiver, ) -> Result<(), Box> { // Connect to the gateway. The same `Channel` is used for both the // long-lived control stream and all data-plane `RelayStream` calls, so @@ -367,7 +391,7 @@ async fn run_single_session( tx.send(SupervisorMessage { payload: Some(supervisor_message::Payload::Hello(SupervisorHello { sandbox_id: sandbox_id.to_string(), - instance_id: instance_id.to_string(), + instance_id: String::new(), })), }) .await @@ -395,6 +419,40 @@ async fn run_single_session( }; let heartbeat_secs = accepted.heartbeat_interval_secs.max(5); + let bootstrap = accepted + .bootstrap + .ok_or("incompatible gateway: SessionAccepted is missing ConfigBootstrap")?; + let (bootstrap_result_tx, bootstrap_result_rx) = oneshot::channel(); + desired_state + .send(DesiredStateRequest::Bootstrap { + bootstrap: Box::new(bootstrap), + result: bootstrap_result_tx, + }) + .await + .map_err(|_| "desired-state handler closed before bootstrap")?; + let bootstrap_result = tokio::time::timeout(BOOTSTRAP_APPLY_TIMEOUT, bootstrap_result_rx) + .await + .map_err(|_| "desired-state bootstrap apply timed out")? + .map_err(|_| "desired-state handler dropped bootstrap result")?; + let bootstrap_ready = ConfigBootstrapStatus::try_from(bootstrap_result.status).ok() + == Some(ConfigBootstrapStatus::Ready); + tx.send(SupervisorMessage { + payload: Some(supervisor_message::Payload::BootstrapResult( + bootstrap_result, + )), + }) + .await + .map_err(|_| "failed to queue bootstrap result")?; + if !bootstrap_ready { + return Err("desired-state bootstrap failed".into()); + } + + let mut runtime_ready_sent = false; + let initial_runtime = runtime_ready.borrow().clone(); + if let Some(runtime) = initial_runtime { + send_runtime_ready(&tx, &runtime).await?; + runtime_ready_sent = true; + } let event = session_established_event( openshell_ocsf::ctx::ctx(), endpoint, @@ -407,6 +465,7 @@ async fn run_single_session( let mut heartbeat_interval = tokio::time::interval(Duration::from_secs(u64::from(heartbeat_secs))); heartbeat_interval.tick().await; // skip immediate tick + let update_slots = Arc::new(tokio::sync::Semaphore::new(3)); loop { tokio::select! { @@ -421,12 +480,12 @@ async fn run_single_session( }; let context = GatewayMessageContext { sandbox_id, - ssh_socket_path, - netns_fd, - expected_ssh_peer_pid, channel: &channel, tx: &tx, terminating: &terminating, + desired_state, + runtime_ready: runtime_ready.borrow().clone(), + update_slots: &update_slots, }; handle_gateway_message( &msg, @@ -443,10 +502,33 @@ async fn run_single_session( return Err("outbound channel closed".into()); } } + changed = runtime_ready.changed(), if !runtime_ready_sent => { + changed.map_err(|_| "runtime-ready state channel closed")?; + let ready = runtime_ready.borrow().clone(); + if let Some(runtime) = ready { + send_runtime_ready(&tx, &runtime).await?; + runtime_ready_sent = true; + } + } } } } +async fn send_runtime_ready( + tx: &mpsc::Sender, + runtime: &RuntimeReadyState, +) -> Result<(), Box> { + tx.send(SupervisorMessage { + payload: Some(supervisor_message::Payload::RuntimeReady( + SupervisorRuntimeReady { + instance_id: runtime.instance_id.clone(), + }, + )), + }) + .await + .map_err(|_| "failed to queue runtime-ready signal".into()) +} + /// Report the canonical process result and wait for durable handling. pub async fn report_main_process_exit( endpoint: &str, @@ -470,12 +552,12 @@ pub async fn report_main_process_exit( struct GatewayMessageContext<'a> { sandbox_id: &'a str, - ssh_socket_path: &'a std::path::Path, - netns_fd: Option, - expected_ssh_peer_pid: Option, channel: &'a grpc_client::AuthedChannel, tx: &'a mpsc::Sender, terminating: &'a Arc, + desired_state: &'a DesiredStateSender, + runtime_ready: Option, + update_slots: &'a Arc, } fn handle_gateway_message(msg: &GatewayMessage, context: &GatewayMessageContext<'_>) { @@ -484,14 +566,32 @@ fn handle_gateway_message(msg: &GatewayMessage, context: &GatewayMessageContext< // Gateway heartbeat — nothing to do. } Some(gateway_message::Payload::RelayOpen(open)) => { + let Some(runtime) = context.runtime_ready.as_ref() else { + let tx = context.tx.clone(); + let channel_id = open.channel_id.clone(); + tokio::spawn(async move { + let _ = tx + .send(SupervisorMessage { + payload: Some(supervisor_message::Payload::RelayOpenResult( + RelayOpenResult { + channel_id, + success: false, + error: "supervisor runtime is not ready".to_string(), + }, + )), + }) + .await; + }); + return; + }; let channel_id = open.channel_id.clone(); let relay_open = open.clone(); let sandbox_id = context.sandbox_id.to_string(); let channel = context.channel.clone(); - let ssh_socket_path = context.ssh_socket_path.to_path_buf(); + let ssh_socket_path = runtime.ssh_socket_path.clone(); let tx = context.tx.clone(); - let netns_fd = context.netns_fd; - let expected_ssh_peer_pid = context.expected_ssh_peer_pid; + let netns_fd = runtime.netns_fd; + let expected_ssh_peer_pid = runtime.expected_ssh_peer_pid; let terminating = Arc::clone(context.terminating); let event = relay_open_event(openshell_ocsf::ctx::ctx(), &relay_open, &ssh_socket_path); @@ -544,12 +644,83 @@ fn handle_gateway_message(msg: &GatewayMessage, context: &GatewayMessageContext< ); ocsf_emit!(event); } + Some(gateway_message::Payload::ConfigUpdate(update)) => { + let tx = context.tx.clone(); + let desired_state = context.desired_state.clone(); + let update = update.clone(); + let Ok(update_slot) = Arc::clone(context.update_slots).try_acquire_owned() else { + tokio::spawn(async move { + let result = config_update_result_for_failure( + &update, + ConfigApplyOutcome::Failed, + "supervisor config-update concurrency limit reached", + ); + let _ = tx + .send(SupervisorMessage { + payload: Some(supervisor_message::Payload::ConfigUpdateResult(result)), + }) + .await; + }); + return; + }; + tokio::spawn(async move { + let _update_slot = update_slot; + let (result_tx, result_rx) = oneshot::channel(); + let result = if desired_state + .send(DesiredStateRequest::Update { + update: Box::new(update.clone()), + result: result_tx, + }) + .await + .is_ok() + { + match tokio::time::timeout(UPDATE_APPLY_TIMEOUT, result_rx).await { + Ok(Ok(result)) => result, + Ok(Err(_)) => { + unsupported_update_result(&update, "desired-state apply loop closed") + } + Err(_) => config_update_result_for_failure( + &update, + ConfigApplyOutcome::Failed, + "desired-state update apply timed out", + ), + } + } else { + unsupported_update_result(&update, "desired-state apply loop unavailable") + }; + let _ = tx + .send(SupervisorMessage { + payload: Some(supervisor_message::Payload::ConfigUpdateResult(result)), + }) + .await; + }); + } _ => { warn!(sandbox_id = %context.sandbox_id, "supervisor session: unexpected gateway message"); } } } +fn unsupported_update_result( + update: &openshell_core::proto::ConfigUpdate, + error: &str, +) -> ConfigUpdateResult { + config_update_result_for_failure(update, ConfigApplyOutcome::Unsupported, error) +} + +fn config_update_result_for_failure( + update: &openshell_core::proto::ConfigUpdate, + outcome: ConfigApplyOutcome, + error: &str, +) -> ConfigUpdateResult { + ConfigUpdateResult { + request_id: update.request_id.clone(), + component_sequence: update.component_sequence, + outcome: outcome as i32, + error: error.to_string(), + } +} + /// Handle a `RelayOpen` by initiating a `RelayStream` RPC on the gateway and /// bridging that stream to the local SSH daemon. /// @@ -754,7 +925,7 @@ async fn connect_tcp_target( netns_fd: Option, ) -> Result> { if let Some(fd) = netns_fd { - let (tx, rx) = tokio::sync::oneshot::channel(); + let (tx, rx) = oneshot::channel(); std::thread::spawn(move || { let result = (|| -> std::io::Result { #[allow(unsafe_code)] @@ -831,6 +1002,36 @@ mod target_tests { } } + #[test] + fn reconnect_jitter_stays_within_twenty_percent() { + for _ in 0..100 { + let delay = jittered_backoff(Duration::from_secs(10)); + assert!((Duration::from_secs(8)..=Duration::from_secs(12)).contains(&delay)); + } + } + + #[test] + fn failed_update_result_preserves_correlation() { + let update = openshell_core::proto::ConfigUpdate { + request_id: "request-1".to_string(), + component_sequence: 7, + ..Default::default() + }; + + let result = config_update_result_for_failure( + &update, + ConfigApplyOutcome::Failed, + "apply timed out", + ); + + assert_eq!(result.request_id, "request-1"); + assert_eq!(result.component_sequence, 7); + assert_eq!( + ConfigApplyOutcome::try_from(result.outcome).unwrap(), + ConfigApplyOutcome::Failed + ); + } + /// Regression test: the TCP relay connect path sets `TCP_NODELAY`. #[tokio::test] async fn connect_tcp_target_sets_tcp_nodelay() { diff --git a/deploy/rpm/TROUBLESHOOTING.md b/deploy/rpm/TROUBLESHOOTING.md index 103ce3bf9d..635efc1392 100644 --- a/deploy/rpm/TROUBLESHOOTING.md +++ b/deploy/rpm/TROUBLESHOOTING.md @@ -238,6 +238,11 @@ podman pull ghcr.io/nvidia/openshell/supervisor:latest podman pull ghcr.io/nvidia/openshell-community/sandboxes/base:latest ``` +Use the same release tag for the gateway and supervisor when the package is +pinned to a versioned image rather than `latest`. Restart the gateway and +recreate existing sandboxes after the upgrade. The supervisor session does not +fall back to the legacy configuration polling protocol across mixed versions. + ### Migrating from gateway.env Previous releases generated `~/.config/openshell/gateway.env` on first diff --git a/docs/about/installation.mdx b/docs/about/installation.mdx index a733a1b881..ce6b4fd7af 100644 --- a/docs/about/installation.mdx +++ b/docs/about/installation.mdx @@ -28,6 +28,11 @@ uv add openshell Use `openshell status` to confirm the CLI can reach the gateway. +Package upgrades install a matched CLI and gateway release. The gateway uses +the corresponding supervisor image by default. Restart the gateway service and +recreate existing sandboxes after an upgrade; overriding the supervisor image +with a different OpenShell version is unsupported. + ## Supported Compute Drivers OpenShell supports several local compute drivers. Package-managed gateways leave the driver unset by default so the gateway can auto-detect an available driver. Set `compute_drivers` in the gateway TOML when you need to pin a specific driver. diff --git a/docs/get-started/tutorials/docker-compose.mdx b/docs/get-started/tutorials/docker-compose.mdx index 677f34595b..edceecb56d 100644 --- a/docs/get-started/tutorials/docker-compose.mdx +++ b/docs/get-started/tutorials/docker-compose.mdx @@ -51,6 +51,11 @@ Docker resolves bind-mount sources against the **host filesystem**, not the cont The Compose file uses `/var/lib/openshell` for this purpose and sets `create_host_path: true` so Docker creates it on first run. +Keep the Compose gateway and supervisor image tags aligned. The supervisor +session does not support a mixed-version polling fallback. After upgrading the +Compose images, recreate existing sandboxes so each one starts with the +release-matched supervisor binary. + ## Start the gateway ```shell diff --git a/docs/get-started/tutorials/microsoft-graph-provider-refresh.mdx b/docs/get-started/tutorials/microsoft-graph-provider-refresh.mdx index d3c4a75847..d660d6add5 100644 --- a/docs/get-started/tutorials/microsoft-graph-provider-refresh.mdx +++ b/docs/get-started/tutorials/microsoft-graph-provider-refresh.mdx @@ -181,7 +181,7 @@ The request uses the [Microsoft Graph list messages API](https://learn.microsoft ## Update Running Sandboxes -Provider refresh updates the provider record at the gateway. Running sandboxes poll for provider environment revisions, but already-running processes keep the environment they started with. +Provider refresh updates the provider record at the gateway. The gateway pushes the new provider environment to running sandboxes, but already-running processes keep the environment they started with. If you attach this provider to an existing sandbox or update provider credentials after a process has already started, launch a new process inside the sandbox before expecting `MS_GRAPH_ACCESS_TOKEN` to appear in that process environment. diff --git a/docs/kubernetes/topology.mdx b/docs/kubernetes/topology.mdx index 869fc07f1b..a3473f4295 100644 --- a/docs/kubernetes/topology.mdx +++ b/docs/kubernetes/topology.mdx @@ -171,7 +171,8 @@ the workload, the process supervisor establishes the only accepted connection. The sidecar validates its UID, GID, and PID with peer credentials, unlinks the listener, derives the SSH target from trusted configuration, and rejects later clients. The connection receives bootstrap state and provider-environment -updates after settings polls. If it closes, the network sidecar exits so +updates pushed over the network sidecar's gateway session. If it closes, the +network sidecar exits so Kubernetes recreates the one-client bootstrap listener, and the process supervisor exits so Kubernetes terminates the workload and restarts the agent container. This symmetric failure behavior prevents a surviving workload from diff --git a/docs/observability/logging.mdx b/docs/observability/logging.mdx index bc8d543246..0246cb5f11 100644 --- a/docs/observability/logging.mdx +++ b/docs/observability/logging.mdx @@ -172,10 +172,9 @@ A process launched inside the sandbox: OCSF PROC:LAUNCH [INFO] sleep(49) ``` -A policy reload after a settings change: +A policy reload after the gateway delivers a settings change: ```text -OCSF CONFIG:DETECTED [INFO] Settings poll: config change detected [old_revision:2915564174587774909 new_revision:11008534403127604466 policy_changed:true] OCSF CONFIG:LOADED [INFO] Policy reloaded successfully [policy_hash:0cc0c2b525573c07] ``` diff --git a/docs/observability/ocsf-json-export.mdx b/docs/observability/ocsf-json-export.mdx index bb85ff6cfc..ae0694dfae 100644 --- a/docs/observability/ocsf-json-export.mdx +++ b/docs/observability/ocsf-json-export.mdx @@ -25,7 +25,7 @@ Per-sandbox: openshell settings set my-sandbox --key ocsf_json_enabled --value true ``` -The setting takes effect on the next poll cycle, by default every 10 seconds. No sandbox restart is required. +The setting takes effect when the gateway delivers the updated sandbox configuration. No sandbox restart is required. To disable: diff --git a/docs/reference/gateway-auth.mdx b/docs/reference/gateway-auth.mdx index b5157b92a5..4ac632f546 100644 --- a/docs/reference/gateway-auth.mdx +++ b/docs/reference/gateway-auth.mdx @@ -191,7 +191,7 @@ to [Manage Workspaces and Access](/sandboxes/manage-workspaces). If `OPENSHELL_OIDC_SCOPES_CLAIM` is set, the gateway also enforces scopes. It accepts space-delimited scope strings such as `scope: "openid sandbox:read"` and JSON arrays such as `scp: ["sandbox:read"]`. Standard OIDC scopes such as `openid`, `profile`, `email`, and `offline_access` are ignored for authorization. `openshell:all` grants access to all scoped methods. -Supervisor-to-gateway RPCs do not use user OIDC tokens or mTLS user identity. Each sandbox supervisor presents a gateway-minted `Authorization: Bearer` token scoped to its sandbox ID. On Kubernetes, the gateway mints that token only after TokenReview validates the projected ServiceAccount token, the pod UID matches the live pod, and the pod's controlling `Sandbox` ownerReference matches the live Sandbox CR. Log upload, policy status, credential environment lookup, inference bundle lookup, and sandbox config sync run with sandbox-restricted scope, while CLI users authenticate with OIDC, edge auth, local mTLS user authentication, or an explicitly enabled unauthenticated local developer mode. `GetInferenceBundle` returns route material that includes provider credentials, so it requires a sandbox principal; user callers manage inference configuration through the user-facing inference APIs instead. +Supervisor-to-gateway RPCs do not use user OIDC tokens or mTLS user identity. Each sandbox supervisor presents a gateway-minted `Authorization: Bearer` token scoped to its sandbox ID. On Kubernetes, the gateway mints that token only after TokenReview validates the projected ServiceAccount token, the pod UID matches the live pod, and the pod's controlling `Sandbox` ownerReference matches the live Sandbox CR. The `ConnectSupervisor` stream delivers configuration, provider environment, and inference snapshots only to that sandbox principal. Log upload, policy status, policy analysis, credential refresh, and relay calls also run with sandbox-restricted scope, while CLI users authenticate with OIDC, edge auth, local mTLS user authentication, or an explicitly enabled unauthenticated local developer mode. User callers manage inference configuration through the user-facing inference APIs instead of reading credential-bearing supervisor snapshots. Re-authenticate an OIDC gateway with: diff --git a/docs/reference/sandbox-compute-drivers.mdx b/docs/reference/sandbox-compute-drivers.mdx index 5773fb2bb1..982208f672 100644 --- a/docs/reference/sandbox-compute-drivers.mdx +++ b/docs/reference/sandbox-compute-drivers.mdx @@ -418,10 +418,10 @@ policy to endpoint/L7 matching only. The network sidecar owns gateway authentication and writes local policy/provider state to the process supervisor over a local control socket, so the agent container does not mount the sandbox bootstrap token or client TLS secret in -the default sidecar path. The provider environment is refreshed by the network -sidecar after settings polls and streamed to the process supervisor so future -child processes can see updated provider env without gateway access in the -agent container. +the default sidecar path. The network sidecar receives provider environment +updates over its gateway session and streams them to the process supervisor so +future child processes can see updated provider env without gateway access in +the agent container. Sidecar mode keeps gateway session and SSH behavior. The process supervisor applies Landlock filesystem policy and child seccomp filters where supported, but it does not perform root-to-sandbox privilege dropping or supervisor diff --git a/docs/reference/support-matrix.mdx b/docs/reference/support-matrix.mdx index cfa5b69549..8c5977b021 100644 --- a/docs/reference/support-matrix.mdx +++ b/docs/reference/support-matrix.mdx @@ -75,6 +75,29 @@ The Helm chart in `deploy/helm/openshell` deploys the gateway workload, service Sandbox images are maintained separately in the [OpenShell Community](https://github.com/NVIDIA/OpenShell-Community) repository. +## Gateway and Supervisor Compatibility + +Run the gateway and sandbox supervisor from the same OpenShell release. The +supervisor session protocol changes in place and does not provide a legacy +polling fallback. A release-matched gateway supplies the supervisor image by +default for Docker, Podman, Kubernetes, and packaged installations. If you +override that image, pin it to the same version as the gateway and recreate +existing sandboxes during the upgrade. + +Raw protobuf consumers must regenerate when upgrading to the desired-state +session protocol. The wire-compatible payload types were renamed as follows: + +| Previous generated type | Current generated type | +|---|---| +| `GetSandboxConfigResponse` | `SandboxConfigSnapshot` | +| `GetSandboxProviderEnvironmentResponse` | `ProviderEnvironmentSnapshot` | +| `GetInferenceBundleResponse` | `InferenceBundleSnapshot` | + +`GetSandboxConfig` keeps its existing RPC path and binary payload shape. The +supervisor-only provider-environment and inference-bundle fetch methods were +removed. The curated Rust, Go, and TypeScript client APIs continue to return +their existing domain models rather than these generated snapshot types. + To override the default image references, use Helm values: | Helm value | Purpose | diff --git a/docs/sandboxes/policy-advisor.mdx b/docs/sandboxes/policy-advisor.mdx index b4d607cb33..4e196c130c 100644 --- a/docs/sandboxes/policy-advisor.mdx +++ b/docs/sandboxes/policy-advisor.mdx @@ -45,7 +45,7 @@ openshell settings delete --global \ --yes ``` -Set the value before creating a sandbox when you want the first denied request to include policy advisor guidance. Running sandboxes poll settings and can enable the surface after startup, but startup enablement gives the agent the clearest first-denial path. +Set the value before creating a sandbox when you want the first denied request to include policy advisor guidance. The gateway pushes setting changes to running sandboxes, so they can enable the surface after startup, but startup enablement gives the agent the clearest first-denial path. ## Approval Modes diff --git a/docs/sandboxes/providers-v2.mdx b/docs/sandboxes/providers-v2.mdx index 7a0f96b5ac..82d2107267 100644 --- a/docs/sandboxes/providers-v2.mdx +++ b/docs/sandboxes/providers-v2.mdx @@ -987,7 +987,7 @@ Attach and detach are idempotent. Attach validates that the provider exists befo ### Runtime Limitations -Provider attach and detach update the persisted sandbox provider list. Running sandboxes poll for provider environment revisions and effective policy changes. +Provider attach and detach update the persisted sandbox provider list. The gateway pushes provider environment and effective policy changes to running sandboxes over the supervisor session. The policy effect applies to future effective policy reads after the sandbox observes the update. The credential environment effect applies only to new process launches after the update is observed, such as later SSH, exec, or SFTP sessions. diff --git a/e2e/python/test_sandbox_providers.py b/e2e/python/test_sandbox_providers.py index 40fd05a122..2f797a410c 100644 --- a/e2e/python/test_sandbox_providers.py +++ b/e2e/python/test_sandbox_providers.py @@ -3,8 +3,8 @@ """E2E tests for supervisor-managed provider placeholders in sandboxes. -Provider credentials are fetched at runtime by the sandbox supervisor via the -GetSandboxProviderEnvironment gRPC call. Sandboxed child processes should see +Provider credentials are delivered at runtime in the supervisor session's +bootstrap and live desired-state updates. Sandboxed child processes should see placeholder values (not raw secrets). Credentials must never be present in the persisted sandbox spec environment map. """ diff --git a/e2e/rust/tests/live_policy_update.rs b/e2e/rust/tests/live_policy_update.rs index 7a1e12923a..add222ff94 100644 --- a/e2e/rust/tests/live_policy_update.rs +++ b/e2e/rust/tests/live_policy_update.rs @@ -542,7 +542,7 @@ async fn initial_sparse_policy_is_acknowledged_as_loaded() { .expect("create keep sandbox with sparse policy"); // The enriched revision (2) is synced during startup; the acknowledgement - // (LOADED) is delivered by the supervisor's poll loop shortly after Ready. + // (LOADED) is delivered after the supervisor applies its session bootstrap. // Poll until the effective policy is version 2 and no revision is Pending. let mut acknowledged = false; let mut last_list = String::new(); diff --git a/proto/inference.proto b/proto/inference.proto index a28d7149e5..875ebd20f5 100644 --- a/proto/inference.proto +++ b/proto/inference.proto @@ -10,14 +10,6 @@ import "options.proto"; // Inference service provides workspace-scoped inference route configuration and bundle delivery. service Inference { - // Return the resolved inference route bundle for sandbox-local execution. - rpc GetInferenceBundle(GetInferenceBundleRequest) - returns (GetInferenceBundleResponse) { - option (openshell.options.v1.authorization) = { - auth_mode: "sandbox" - }; - } - // Set the inference route for a workspace. // // This controls how requests sent to `inference.local` are routed @@ -145,6 +137,9 @@ message DeleteInferenceRouteResponse { bool deleted = 1; } +// Retained as a source-compatible message for raw SDK consumers. The +// supervisor fetch RPC was removed; desired state now arrives on +// ConnectSupervisor. message GetInferenceBundleRequest {} // A single resolved route ready for sandbox-local execution. @@ -164,7 +159,7 @@ message ResolvedRoute { optional string request_path_override = 9; } -message GetInferenceBundleResponse { +message InferenceBundleSnapshot { repeated ResolvedRoute routes = 1; // Opaque revision tag for cache freshness checks. string revision = 2; diff --git a/proto/openshell.proto b/proto/openshell.proto index 246fe0626f..c1cdbe757d 100644 --- a/proto/openshell.proto +++ b/proto/openshell.proto @@ -7,6 +7,7 @@ package openshell.v1; import "datamodel.proto"; import "google/protobuf/struct.proto"; +import "inference.proto"; import "options.proto"; import "sandbox.proto"; @@ -354,9 +355,10 @@ service OpenShell { }; } - // Get sandbox settings by id (called by sandbox entrypoint and poll loop). + // Get sandbox settings by id. This remains a public read API; supervisors + // receive the same snapshot through ConnectSupervisor. rpc GetSandboxConfig(openshell.sandbox.v1.GetSandboxConfigRequest) - returns (openshell.sandbox.v1.GetSandboxConfigResponse) { + returns (openshell.sandbox.v1.SandboxConfigSnapshot) { option (openshell.options.v1.authorization) = { auth_mode: "dual" scope: "config:read" @@ -418,14 +420,6 @@ service OpenShell { }; } - // Get provider environment for a sandbox (called by sandbox supervisor at startup). - rpc GetSandboxProviderEnvironment(GetSandboxProviderEnvironmentRequest) - returns (GetSandboxProviderEnvironmentResponse) { - option (openshell.options.v1.authorization) = { - auth_mode: "sandbox" - }; - } - // Exchange a stored provider subject token for an intermediate token scoped // to the calling supervisor's SPIFFE identity. rpc ExchangeProviderSubjectToken(ExchangeProviderSubjectTokenRequest) @@ -1874,13 +1868,11 @@ message DeleteProviderProfileResponse { bool deleted = 1; } -// Get sandbox provider environment request. +// Retained as a source-compatible message for raw SDK consumers. The +// supervisor fetch RPC was removed; desired state now arrives on +// ConnectSupervisor. message GetSandboxProviderEnvironmentRequest { - // The sandbox ID. string sandbox_id = 1; - // Whether the requesting supervisor enforces endpoint bindings for static - // provider credentials. Gateways withhold static credential material when - // this capability is absent. bool supports_static_credential_bindings = 2; } @@ -1906,8 +1898,8 @@ message StaticCredentialBinding { string workload_credential_handle = 3; } -// Get sandbox provider environment response. -message GetSandboxProviderEnvironmentResponse { +// Complete provider environment and credential snapshot for a sandbox. +message ProviderEnvironmentSnapshot { // Provider credential environment variables. map environment = 1 [(openshell.options.v1.secret) = true]; // Fingerprint for the provider credential inputs that produced environment. @@ -2187,6 +2179,9 @@ message SupervisorMessage { SupervisorHeartbeat heartbeat = 2; RelayOpenResult relay_open_result = 3; RelayClose relay_close = 4; + ConfigBootstrapResult bootstrap_result = 5; + ConfigUpdateResult config_update_result = 6; + SupervisorRuntimeReady runtime_ready = 7; } } @@ -2198,6 +2193,7 @@ message GatewayMessage { GatewayHeartbeat heartbeat = 3; RelayOpen relay_open = 4; RelayClose relay_close = 5; + ConfigUpdate config_update = 6; } } @@ -2215,6 +2211,83 @@ message SessionAccepted { string session_id = 1; // Recommended heartbeat interval in seconds. uint32 heartbeat_interval_secs = 2; + // Complete gateway-owned desired state. Its presence is the compatibility + // gate for supervisors that use gateway-backed configuration. + ConfigBootstrap bootstrap = 3; +} + +// Complete desired state required to initialize a gateway-backed supervisor. +message ConfigBootstrap { + openshell.sandbox.v1.SandboxConfigSnapshot sandbox_config = 1; + ProviderEnvironmentSnapshot provider_environment = 2; + openshell.inference.v1.InferenceBundleSnapshot inference_bundle = 3; +} + +enum ConfigBootstrapStatus { + CONFIG_BOOTSTRAP_STATUS_UNSPECIFIED = 0; + CONFIG_BOOTSTRAP_STATUS_READY = 1; + CONFIG_BOOTSTRAP_STATUS_FAILED = 2; +} + +// Aggregate bootstrap acknowledgement with a terminal outcome for every +// component. Error text is sanitized and bounded by the sender. +message ConfigBootstrapResult { + ConfigBootstrapStatus status = 1; + string error = 2; + ConfigApplyOutcome sandbox_config_outcome = 3; + ConfigApplyOutcome provider_environment_outcome = 4; + ConfigApplyOutcome inference_bundle_outcome = 5; +} + +// A level-triggered update for exactly one desired-state component. +message ConfigUpdate { + // Opaque, non-empty identifier scoped to the active session. + string request_id = 1; + oneof component { + SandboxConfigUpdate sandbox_config = 2; + ProviderEnvironmentUpdate provider_environment = 3; + InferenceBundleUpdate inference_bundle = 4; + } + // Monotonic within one session and component. Snapshot revisions remain + // content fingerprints and are compared only for equality. + uint64 component_sequence = 5; +} + +message SandboxConfigUpdate { + openshell.sandbox.v1.SandboxConfigSnapshot snapshot = 1; +} + +message ProviderEnvironmentUpdate { + ProviderEnvironmentSnapshot snapshot = 1; +} + +message InferenceBundleUpdate { + openshell.inference.v1.InferenceBundleSnapshot snapshot = 1; +} + +enum ConfigApplyOutcome { + CONFIG_APPLY_OUTCOME_UNSPECIFIED = 0; + CONFIG_APPLY_OUTCOME_APPLIED = 1; + CONFIG_APPLY_OUTCOME_IGNORED_DUPLICATE = 2; + CONFIG_APPLY_OUTCOME_IGNORED_STALE = 3; + CONFIG_APPLY_OUTCOME_RETAINED_LOCAL_OVERRIDE = 4; + CONFIG_APPLY_OUTCOME_DEGRADED = 5; + CONFIG_APPLY_OUTCOME_FAILED = 6; + CONFIG_APPLY_OUTCOME_UNSUPPORTED = 7; +} + +message ConfigUpdateResult { + string request_id = 1; + uint64 component_sequence = 2; + ConfigApplyOutcome outcome = 3; + // Sanitized error text, capped at 1 KiB by the sender. + string error = 4; +} + +// Signals that runtime-dependent services such as SSH and relays are ready. +message SupervisorRuntimeReady { + // Canonical workload instance associated with this ready runtime. + string instance_id = 1; } // Gateway rejects the supervisor session. diff --git a/proto/sandbox.proto b/proto/sandbox.proto index 51139ba461..c0f5c69c1e 100644 --- a/proto/sandbox.proto +++ b/proto/sandbox.proto @@ -352,15 +352,15 @@ message EffectiveSetting { SettingScope scope = 2; } -// Source used for the policy payload in GetSandboxConfigResponse. +// Source used for the policy payload in SandboxConfigSnapshot. enum PolicySource { POLICY_SOURCE_UNSPECIFIED = 0; POLICY_SOURCE_SANDBOX = 1; POLICY_SOURCE_GLOBAL = 2; } -// Response containing effective sandbox settings and policy. -message GetSandboxConfigResponse { +// Complete effective sandbox settings and policy snapshot. +message SandboxConfigSnapshot { // The sandbox policy configuration. SandboxPolicy policy = 1; // Current policy version (monotonically increasing per sandbox). diff --git a/rfc/0011-multi-player-design/README.md b/rfc/0011-multi-player-design/README.md index 32a2f584e1..cc53090ddd 100644 --- a/rfc/0011-multi-player-design/README.md +++ b/rfc/0011-multi-player-design/README.md @@ -104,8 +104,8 @@ layer and per-handler scope guards (`ensure_sandbox_principal_scope`) that verify the JWT's sandbox UUID matches the request target. The supervisor never goes through the RBAC role check or workspace membership lookup. -The supervisor learns its workspace from the `GetSandboxConfigResponse.workspace` -field returned by its first settings poll. It caches this value and passes it +The supervisor learns its workspace from the `SandboxConfigSnapshot.workspace` +field in its session bootstrap. It caches this value and passes it in subsequent workspace-scoped RPCs (policy sync, policy analysis, draft policy queries). This avoids server-side special-casing for sandbox principals while keeping the supervisor's JWT scoped to a single sandbox UUID. @@ -135,7 +135,7 @@ Supervisor section above). | Policy draft inspection (`GetDraftPolicy`, `GetDraftHistory`) | read | read (own ws) | read (own ws) | `GetDraftPolicy` for own sandbox only | | Policy draft decisions (`Approve`, `Reject`, `Edit`, `Undo`, `Clear`) | read-write | read-write (own ws) | none | none | | Policy analysis submission (`SubmitPolicyAnalysis`) | none | none | none | own sandbox | -| Supervisor path (`ConnectSupervisor`, `RelayStream`, `IssueSandboxToken`, `RefreshSandboxToken`, `GetSandboxProviderEnvironment`, `PushSandboxLogs`, `ReportPolicyStatus`) | none | none | none | own sandbox | +| Supervisor path (`ConnectSupervisor`, `RelayStream`, `IssueSandboxToken`, `RefreshSandboxToken`, `PushSandboxLogs`, `ReportPolicyStatus`) | none | none | none | own sandbox | **Control-plane audit log.** Every mutating gRPC call emits an OCSF `ApiActivity` event recording the principal, action, target resource, and @@ -1535,38 +1535,39 @@ Auth: Bearer → router: is_sandbox_callable("ConnectSupervisor") → yes → supervisor sends SupervisorHello { sandbox_id: "uuid-a" } → ensure_sandbox_principal_scope: JWT sandbox_id == hello sandbox_id → pass -→ register session, send SessionAccepted, notify driver: sandbox ready +→ register connected session, send SessionAccepted + ConfigBootstrap +→ bootstrap result marks the session initialized +→ SupervisorRuntimeReady marks the sandbox ready ``` -**9. Supervisor fetches provider credentials.** +**9. Gateway delivers provider credentials.** ```text -GetSandboxProviderEnvironment { sandbox_id: "uuid-a" } -→ enforce_sandbox_scope: JWT sandbox_id == request sandbox_id → pass +SessionAccepted.bootstrap.provider_environment → gateway resolves providers for sandbox uuid-a (workspace-internal lookup) -→ return { ANTHROPIC_API_KEY: "sk-...", ... } +→ supervisor installs placeholders and bound credential material ``` **10. Cross-sandbox and cross-principal access is rejected.** ```text -Supervisor-A → GetSandboxProviderEnvironment { sandbox_id: "uuid-b" } -→ enforce_sandbox_scope: "uuid-a" != "uuid-b" → PERMISSION_DENIED +Supervisor-A → SupervisorHello { sandbox_id: "uuid-b" } +→ ensure_sandbox_principal_scope: "uuid-a" != "uuid-b" → PERMISSION_DENIED Supervisor-A → ListSandboxes { workspace: "team-ml" } → router: is_sandbox_callable("ListSandboxes") → false → PERMISSION_DENIED ``` -**11. Supervisor learns its workspace from the config response.** +**11. Supervisor learns its workspace from the session bootstrap.** ```text -GetSandboxConfig { sandbox_id: "uuid-a" } -→ response includes workspace: "team-ml" +SessionAccepted.bootstrap.sandbox_config +→ snapshot includes workspace: "team-ml" → supervisor caches workspace for subsequent RPCs ``` The supervisor discovers its workspace from the `workspace` field in -`GetSandboxConfigResponse`, returned by its first settings poll. It caches +`SandboxConfigSnapshot`, delivered in its session bootstrap. It caches this value and uses it for workspace-scoped RPCs such as `UpdateConfig` (policy sync), `SubmitPolicyAnalysis`, and `GetDraftPolicy`. The supervisor's authorization surface remains a single sandbox UUID — the workspace is used diff --git a/sdk/go/openshell/v1/config_client_test.go b/sdk/go/openshell/v1/config_client_test.go index 9fa8675ff2..09788bb7f8 100644 --- a/sdk/go/openshell/v1/config_client_test.go +++ b/sdk/go/openshell/v1/config_client_test.go @@ -28,7 +28,7 @@ type mockConfigServer struct { mu sync.Mutex // Canned responses. - sandboxResp *sbv1.GetSandboxConfigResponse + sandboxResp *sbv1.SandboxConfigSnapshot gatewayResp *sbv1.GetGatewayConfigResponse updateResp *pb.UpdateConfigResponse @@ -47,7 +47,7 @@ func newMockConfigServer() *mockConfigServer { return &mockConfigServer{} } -func (s *mockConfigServer) GetSandboxConfig(_ context.Context, req *sbv1.GetSandboxConfigRequest) (*sbv1.GetSandboxConfigResponse, error) { +func (s *mockConfigServer) GetSandboxConfig(_ context.Context, req *sbv1.GetSandboxConfigRequest) (*sbv1.SandboxConfigSnapshot, error) { s.mu.Lock() defer s.mu.Unlock() s.lastSandboxReq = req @@ -104,7 +104,7 @@ func setupConfigTest(t *testing.T, mock *mockConfigServer) (*configClient, func( func TestConfigGetSandbox(t *testing.T) { mock := newMockConfigServer() - mock.sandboxResp = &sbv1.GetSandboxConfigResponse{ + mock.sandboxResp = &sbv1.SandboxConfigSnapshot{ Policy: &sbv1.SandboxPolicy{ Version: 4, Filesystem: &sbv1.FilesystemPolicy{ @@ -176,7 +176,7 @@ func TestConfigGetSandbox(t *testing.T) { func TestConfigGetSandbox_DeepCopy(t *testing.T) { mock := newMockConfigServer() - mock.sandboxResp = &sbv1.GetSandboxConfigResponse{ + mock.sandboxResp = &sbv1.SandboxConfigSnapshot{ Version: 1, Settings: map[string]*sbv1.EffectiveSetting{ "key": { @@ -224,7 +224,7 @@ func TestConfigGetSandbox_Error(t *testing.T) { func TestConfigGetSandbox_ResolvesNameToID(t *testing.T) { mock := newMockConfigServer() - mock.sandboxResp = &sbv1.GetSandboxConfigResponse{Version: 1} + mock.sandboxResp = &sbv1.SandboxConfigSnapshot{Version: 1} client, cleanup := setupConfigTest(t, mock) defer cleanup() diff --git a/sdk/go/openshell/v1/internal/converter/setting.go b/sdk/go/openshell/v1/internal/converter/setting.go index 495545938d..c1f0bb07db 100644 --- a/sdk/go/openshell/v1/internal/converter/setting.go +++ b/sdk/go/openshell/v1/internal/converter/setting.go @@ -117,8 +117,8 @@ func EffectiveSettingFromProto(pv *sbv1.EffectiveSetting) *v1.EffectiveSetting { // --- SandboxConfig --- -// SandboxConfigFromProto converts a GetSandboxConfigResponse to an SDK SandboxConfig. -func SandboxConfigFromProto(resp *sbv1.GetSandboxConfigResponse) *v1.SandboxConfig { +// SandboxConfigFromProto converts a SandboxConfigSnapshot to an SDK SandboxConfig. +func SandboxConfigFromProto(resp *sbv1.SandboxConfigSnapshot) *v1.SandboxConfig { if resp == nil { return nil } diff --git a/sdk/go/openshell/v1/internal/converter/setting_test.go b/sdk/go/openshell/v1/internal/converter/setting_test.go index f546902429..7891adf966 100644 --- a/sdk/go/openshell/v1/internal/converter/setting_test.go +++ b/sdk/go/openshell/v1/internal/converter/setting_test.go @@ -243,10 +243,10 @@ func TestEffectiveSettingFromProto_Nil(t *testing.T) { assert.Nil(t, es) } -// --- SandboxConfig (GetSandboxConfigResponse → SandboxConfig) --- +// --- SandboxConfig (SandboxConfigSnapshot → SandboxConfig) --- func TestSandboxConfigFromProto(t *testing.T) { - resp := &sbv1.GetSandboxConfigResponse{ + resp := &sbv1.SandboxConfigSnapshot{ Policy: &sbv1.SandboxPolicy{ Version: 7, Filesystem: &sbv1.FilesystemPolicy{ @@ -305,7 +305,7 @@ func TestSandboxConfigFromProto(t *testing.T) { } func TestSandboxConfigFromProto_NilPolicy(t *testing.T) { - resp := &sbv1.GetSandboxConfigResponse{ + resp := &sbv1.SandboxConfigSnapshot{ Version: 1, PolicyHash: "sha256:empty", } @@ -324,7 +324,7 @@ func TestSandboxConfigFromProto_Nil(t *testing.T) { } func TestSandboxConfigFromProto_SettingsDeepCopy(t *testing.T) { - resp := &sbv1.GetSandboxConfigResponse{ + resp := &sbv1.SandboxConfigSnapshot{ Settings: map[string]*sbv1.EffectiveSetting{ "key1": { Value: &sbv1.SettingValue{ diff --git a/sdk/go/proto/inferencev1/inference.pb.go b/sdk/go/proto/inferencev1/inference.pb.go index decc6c4f39..41106bc0bd 100644 --- a/sdk/go/proto/inferencev1/inference.pb.go +++ b/sdk/go/proto/inferencev1/inference.pb.go @@ -654,6 +654,9 @@ func (x *DeleteInferenceRouteResponse) GetDeleted() bool { return false } +// Retained as a source-compatible message for raw SDK consumers. The +// supervisor fetch RPC was removed; desired state now arrives on +// ConnectSupervisor. type GetInferenceBundleRequest struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -803,7 +806,7 @@ func (x *ResolvedRoute) GetRequestPathOverride() string { return "" } -type GetInferenceBundleResponse struct { +type InferenceBundleSnapshot struct { state protoimpl.MessageState `protogen:"open.v1"` Routes []*ResolvedRoute `protobuf:"bytes,1,rep,name=routes,proto3" json:"routes,omitempty"` // Opaque revision tag for cache freshness checks. @@ -814,20 +817,20 @@ type GetInferenceBundleResponse struct { sizeCache protoimpl.SizeCache } -func (x *GetInferenceBundleResponse) Reset() { - *x = GetInferenceBundleResponse{} +func (x *InferenceBundleSnapshot) Reset() { + *x = InferenceBundleSnapshot{} mi := &file_inference_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *GetInferenceBundleResponse) String() string { +func (x *InferenceBundleSnapshot) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetInferenceBundleResponse) ProtoMessage() {} +func (*InferenceBundleSnapshot) ProtoMessage() {} -func (x *GetInferenceBundleResponse) ProtoReflect() protoreflect.Message { +func (x *InferenceBundleSnapshot) ProtoReflect() protoreflect.Message { mi := &file_inference_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -839,26 +842,26 @@ func (x *GetInferenceBundleResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetInferenceBundleResponse.ProtoReflect.Descriptor instead. -func (*GetInferenceBundleResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use InferenceBundleSnapshot.ProtoReflect.Descriptor instead. +func (*InferenceBundleSnapshot) Descriptor() ([]byte, []int) { return file_inference_proto_rawDescGZIP(), []int{11} } -func (x *GetInferenceBundleResponse) GetRoutes() []*ResolvedRoute { +func (x *InferenceBundleSnapshot) GetRoutes() []*ResolvedRoute { if x != nil { return x.Routes } return nil } -func (x *GetInferenceBundleResponse) GetRevision() string { +func (x *InferenceBundleSnapshot) GetRevision() string { if x != nil { return x.Revision } return "" } -func (x *GetInferenceBundleResponse) GetGeneratedAtMs() int64 { +func (x *InferenceBundleSnapshot) GetGeneratedAtMs() int64 { if x != nil { return x.GeneratedAtMs } @@ -929,14 +932,12 @@ const file_inference_proto_rawDesc = "" + "\ftimeout_secs\x18\a \x01(\x04R\vtimeoutSecs\x12\"\n" + "\rmodel_in_path\x18\b \x01(\bR\vmodelInPath\x127\n" + "\x15request_path_override\x18\t \x01(\tH\x00R\x13requestPathOverride\x88\x01\x01B\x18\n" + - "\x16_request_path_override\"\x9f\x01\n" + - "\x1aGetInferenceBundleResponse\x12=\n" + + "\x16_request_path_override\"\x9c\x01\n" + + "\x17InferenceBundleSnapshot\x12=\n" + "\x06routes\x18\x01 \x03(\v2%.openshell.inference.v1.ResolvedRouteR\x06routes\x12\x1a\n" + "\brevision\x18\x02 \x01(\tR\brevision\x12&\n" + - "\x0fgenerated_at_ms\x18\x03 \x01(\x03R\rgeneratedAtMs2\x82\x05\n" + - "\tInference\x12\x8a\x01\n" + - "\x12GetInferenceBundle\x121.openshell.inference.v1.GetInferenceBundleRequest\x1a2.openshell.inference.v1.GetInferenceBundleResponse\"\r\x82\xb5\x18\t\n" + - "\asandbox\x12\x9e\x01\n" + + "\x0fgenerated_at_ms\x18\x03 \x01(\x03R\rgeneratedAtMs2\xf5\x03\n" + + "\tInference\x12\x9e\x01\n" + "\x11SetInferenceRoute\x120.openshell.inference.v1.SetInferenceRouteRequest\x1a1.openshell.inference.v1.SetInferenceRouteResponse\"$\x82\xb5\x18 \n" + "\x06bearer\x12\x05admin\"\x0finference:write\x12\x9c\x01\n" + "\x11GetInferenceRoute\x120.openshell.inference.v1.GetInferenceRouteRequest\x1a1.openshell.inference.v1.GetInferenceRouteResponse\"\"\x82\xb5\x18\x1e\n" + @@ -969,24 +970,22 @@ var file_inference_proto_goTypes = []any{ (*DeleteInferenceRouteResponse)(nil), // 8: openshell.inference.v1.DeleteInferenceRouteResponse (*GetInferenceBundleRequest)(nil), // 9: openshell.inference.v1.GetInferenceBundleRequest (*ResolvedRoute)(nil), // 10: openshell.inference.v1.ResolvedRoute - (*GetInferenceBundleResponse)(nil), // 11: openshell.inference.v1.GetInferenceBundleResponse + (*InferenceBundleSnapshot)(nil), // 11: openshell.inference.v1.InferenceBundleSnapshot (*datamodelv1.ObjectMeta)(nil), // 12: openshell.datamodel.v1.ObjectMeta } var file_inference_proto_depIdxs = []int32{ 12, // 0: openshell.inference.v1.InferenceRoute.metadata:type_name -> openshell.datamodel.v1.ObjectMeta 0, // 1: openshell.inference.v1.InferenceRoute.config:type_name -> openshell.inference.v1.InferenceRouteConfig 3, // 2: openshell.inference.v1.SetInferenceRouteResponse.validated_endpoints:type_name -> openshell.inference.v1.ValidatedEndpoint - 10, // 3: openshell.inference.v1.GetInferenceBundleResponse.routes:type_name -> openshell.inference.v1.ResolvedRoute - 9, // 4: openshell.inference.v1.Inference.GetInferenceBundle:input_type -> openshell.inference.v1.GetInferenceBundleRequest - 2, // 5: openshell.inference.v1.Inference.SetInferenceRoute:input_type -> openshell.inference.v1.SetInferenceRouteRequest - 5, // 6: openshell.inference.v1.Inference.GetInferenceRoute:input_type -> openshell.inference.v1.GetInferenceRouteRequest - 7, // 7: openshell.inference.v1.Inference.DeleteInferenceRoute:input_type -> openshell.inference.v1.DeleteInferenceRouteRequest - 11, // 8: openshell.inference.v1.Inference.GetInferenceBundle:output_type -> openshell.inference.v1.GetInferenceBundleResponse - 4, // 9: openshell.inference.v1.Inference.SetInferenceRoute:output_type -> openshell.inference.v1.SetInferenceRouteResponse - 6, // 10: openshell.inference.v1.Inference.GetInferenceRoute:output_type -> openshell.inference.v1.GetInferenceRouteResponse - 8, // 11: openshell.inference.v1.Inference.DeleteInferenceRoute:output_type -> openshell.inference.v1.DeleteInferenceRouteResponse - 8, // [8:12] is the sub-list for method output_type - 4, // [4:8] is the sub-list for method input_type + 10, // 3: openshell.inference.v1.InferenceBundleSnapshot.routes:type_name -> openshell.inference.v1.ResolvedRoute + 2, // 4: openshell.inference.v1.Inference.SetInferenceRoute:input_type -> openshell.inference.v1.SetInferenceRouteRequest + 5, // 5: openshell.inference.v1.Inference.GetInferenceRoute:input_type -> openshell.inference.v1.GetInferenceRouteRequest + 7, // 6: openshell.inference.v1.Inference.DeleteInferenceRoute:input_type -> openshell.inference.v1.DeleteInferenceRouteRequest + 4, // 7: openshell.inference.v1.Inference.SetInferenceRoute:output_type -> openshell.inference.v1.SetInferenceRouteResponse + 6, // 8: openshell.inference.v1.Inference.GetInferenceRoute:output_type -> openshell.inference.v1.GetInferenceRouteResponse + 8, // 9: openshell.inference.v1.Inference.DeleteInferenceRoute:output_type -> openshell.inference.v1.DeleteInferenceRouteResponse + 7, // [7:10] is the sub-list for method output_type + 4, // [4:7] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name diff --git a/sdk/go/proto/inferencev1/inference_grpc.pb.go b/sdk/go/proto/inferencev1/inference_grpc.pb.go index 61f74348c0..52c1959809 100644 --- a/sdk/go/proto/inferencev1/inference_grpc.pb.go +++ b/sdk/go/proto/inferencev1/inference_grpc.pb.go @@ -22,7 +22,6 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - Inference_GetInferenceBundle_FullMethodName = "/openshell.inference.v1.Inference/GetInferenceBundle" Inference_SetInferenceRoute_FullMethodName = "/openshell.inference.v1.Inference/SetInferenceRoute" Inference_GetInferenceRoute_FullMethodName = "/openshell.inference.v1.Inference/GetInferenceRoute" Inference_DeleteInferenceRoute_FullMethodName = "/openshell.inference.v1.Inference/DeleteInferenceRoute" @@ -34,8 +33,6 @@ const ( // // Inference service provides workspace-scoped inference route configuration and bundle delivery. type InferenceClient interface { - // Return the resolved inference route bundle for sandbox-local execution. - GetInferenceBundle(ctx context.Context, in *GetInferenceBundleRequest, opts ...grpc.CallOption) (*GetInferenceBundleResponse, error) // Set the inference route for a workspace. // // This controls how requests sent to `inference.local` are routed @@ -55,16 +52,6 @@ func NewInferenceClient(cc grpc.ClientConnInterface) InferenceClient { return &inferenceClient{cc} } -func (c *inferenceClient) GetInferenceBundle(ctx context.Context, in *GetInferenceBundleRequest, opts ...grpc.CallOption) (*GetInferenceBundleResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetInferenceBundleResponse) - err := c.cc.Invoke(ctx, Inference_GetInferenceBundle_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *inferenceClient) SetInferenceRoute(ctx context.Context, in *SetInferenceRouteRequest, opts ...grpc.CallOption) (*SetInferenceRouteResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(SetInferenceRouteResponse) @@ -101,8 +88,6 @@ func (c *inferenceClient) DeleteInferenceRoute(ctx context.Context, in *DeleteIn // // Inference service provides workspace-scoped inference route configuration and bundle delivery. type InferenceServer interface { - // Return the resolved inference route bundle for sandbox-local execution. - GetInferenceBundle(context.Context, *GetInferenceBundleRequest) (*GetInferenceBundleResponse, error) // Set the inference route for a workspace. // // This controls how requests sent to `inference.local` are routed @@ -122,9 +107,6 @@ type InferenceServer interface { // pointer dereference when methods are called. type UnimplementedInferenceServer struct{} -func (UnimplementedInferenceServer) GetInferenceBundle(context.Context, *GetInferenceBundleRequest) (*GetInferenceBundleResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetInferenceBundle not implemented") -} func (UnimplementedInferenceServer) SetInferenceRoute(context.Context, *SetInferenceRouteRequest) (*SetInferenceRouteResponse, error) { return nil, status.Error(codes.Unimplemented, "method SetInferenceRoute not implemented") } @@ -155,24 +137,6 @@ func RegisterInferenceServer(s grpc.ServiceRegistrar, srv InferenceServer) { s.RegisterService(&Inference_ServiceDesc, srv) } -func _Inference_GetInferenceBundle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetInferenceBundleRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(InferenceServer).GetInferenceBundle(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: Inference_GetInferenceBundle_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(InferenceServer).GetInferenceBundle(ctx, req.(*GetInferenceBundleRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _Inference_SetInferenceRoute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(SetInferenceRouteRequest) if err := dec(in); err != nil { @@ -234,10 +198,6 @@ var Inference_ServiceDesc = grpc.ServiceDesc{ ServiceName: "openshell.inference.v1.Inference", HandlerType: (*InferenceServer)(nil), Methods: []grpc.MethodDesc{ - { - MethodName: "GetInferenceBundle", - Handler: _Inference_GetInferenceBundle_Handler, - }, { MethodName: "SetInferenceRoute", Handler: _Inference_SetInferenceRoute_Handler, diff --git a/sdk/go/proto/openshellv1/openshell.pb.go b/sdk/go/proto/openshellv1/openshell.pb.go index 6b3b740a0b..247dfe1bd2 100644 --- a/sdk/go/proto/openshellv1/openshell.pb.go +++ b/sdk/go/proto/openshellv1/openshell.pb.go @@ -11,6 +11,7 @@ package openshellv1 import ( datamodelv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" + inferencev1 "github.com/NVIDIA/OpenShell/sdk/go/proto/inferencev1" _ "github.com/NVIDIA/OpenShell/sdk/go/proto/optionsv1" sandboxv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" protoreflect "google.golang.org/protobuf/reflect/protoreflect" @@ -336,6 +337,119 @@ func (PolicyStatus) EnumDescriptor() ([]byte, []int) { return file_openshell_proto_rawDescGZIP(), []int{4} } +type ConfigBootstrapStatus int32 + +const ( + ConfigBootstrapStatus_CONFIG_BOOTSTRAP_STATUS_UNSPECIFIED ConfigBootstrapStatus = 0 + ConfigBootstrapStatus_CONFIG_BOOTSTRAP_STATUS_READY ConfigBootstrapStatus = 1 + ConfigBootstrapStatus_CONFIG_BOOTSTRAP_STATUS_FAILED ConfigBootstrapStatus = 2 +) + +// Enum value maps for ConfigBootstrapStatus. +var ( + ConfigBootstrapStatus_name = map[int32]string{ + 0: "CONFIG_BOOTSTRAP_STATUS_UNSPECIFIED", + 1: "CONFIG_BOOTSTRAP_STATUS_READY", + 2: "CONFIG_BOOTSTRAP_STATUS_FAILED", + } + ConfigBootstrapStatus_value = map[string]int32{ + "CONFIG_BOOTSTRAP_STATUS_UNSPECIFIED": 0, + "CONFIG_BOOTSTRAP_STATUS_READY": 1, + "CONFIG_BOOTSTRAP_STATUS_FAILED": 2, + } +) + +func (x ConfigBootstrapStatus) Enum() *ConfigBootstrapStatus { + p := new(ConfigBootstrapStatus) + *p = x + return p +} + +func (x ConfigBootstrapStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ConfigBootstrapStatus) Descriptor() protoreflect.EnumDescriptor { + return file_openshell_proto_enumTypes[5].Descriptor() +} + +func (ConfigBootstrapStatus) Type() protoreflect.EnumType { + return &file_openshell_proto_enumTypes[5] +} + +func (x ConfigBootstrapStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ConfigBootstrapStatus.Descriptor instead. +func (ConfigBootstrapStatus) EnumDescriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{5} +} + +type ConfigApplyOutcome int32 + +const ( + ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_UNSPECIFIED ConfigApplyOutcome = 0 + ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_APPLIED ConfigApplyOutcome = 1 + ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_IGNORED_DUPLICATE ConfigApplyOutcome = 2 + ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_IGNORED_STALE ConfigApplyOutcome = 3 + ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_RETAINED_LOCAL_OVERRIDE ConfigApplyOutcome = 4 + ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_DEGRADED ConfigApplyOutcome = 5 + ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_FAILED ConfigApplyOutcome = 6 + ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_UNSUPPORTED ConfigApplyOutcome = 7 +) + +// Enum value maps for ConfigApplyOutcome. +var ( + ConfigApplyOutcome_name = map[int32]string{ + 0: "CONFIG_APPLY_OUTCOME_UNSPECIFIED", + 1: "CONFIG_APPLY_OUTCOME_APPLIED", + 2: "CONFIG_APPLY_OUTCOME_IGNORED_DUPLICATE", + 3: "CONFIG_APPLY_OUTCOME_IGNORED_STALE", + 4: "CONFIG_APPLY_OUTCOME_RETAINED_LOCAL_OVERRIDE", + 5: "CONFIG_APPLY_OUTCOME_DEGRADED", + 6: "CONFIG_APPLY_OUTCOME_FAILED", + 7: "CONFIG_APPLY_OUTCOME_UNSUPPORTED", + } + ConfigApplyOutcome_value = map[string]int32{ + "CONFIG_APPLY_OUTCOME_UNSPECIFIED": 0, + "CONFIG_APPLY_OUTCOME_APPLIED": 1, + "CONFIG_APPLY_OUTCOME_IGNORED_DUPLICATE": 2, + "CONFIG_APPLY_OUTCOME_IGNORED_STALE": 3, + "CONFIG_APPLY_OUTCOME_RETAINED_LOCAL_OVERRIDE": 4, + "CONFIG_APPLY_OUTCOME_DEGRADED": 5, + "CONFIG_APPLY_OUTCOME_FAILED": 6, + "CONFIG_APPLY_OUTCOME_UNSUPPORTED": 7, + } +) + +func (x ConfigApplyOutcome) Enum() *ConfigApplyOutcome { + p := new(ConfigApplyOutcome) + *p = x + return p +} + +func (x ConfigApplyOutcome) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ConfigApplyOutcome) Descriptor() protoreflect.EnumDescriptor { + return file_openshell_proto_enumTypes[6].Descriptor() +} + +func (ConfigApplyOutcome) Type() protoreflect.EnumType { + return &file_openshell_proto_enumTypes[6] +} + +func (x ConfigApplyOutcome) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ConfigApplyOutcome.Descriptor instead. +func (ConfigApplyOutcome) EnumDescriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{6} +} + // Service status enum. type ServiceStatus int32 @@ -373,11 +487,11 @@ func (x ServiceStatus) String() string { } func (ServiceStatus) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[5].Descriptor() + return file_openshell_proto_enumTypes[7].Descriptor() } func (ServiceStatus) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[5] + return &file_openshell_proto_enumTypes[7] } func (x ServiceStatus) Number() protoreflect.EnumNumber { @@ -386,7 +500,7 @@ func (x ServiceStatus) Number() protoreflect.EnumNumber { // Deprecated: Use ServiceStatus.Descriptor instead. func (ServiceStatus) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{5} + return file_openshell_proto_rawDescGZIP(), []int{7} } // Workspace-scoped role for members. @@ -423,11 +537,11 @@ func (x WorkspaceRole) String() string { } func (WorkspaceRole) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[6].Descriptor() + return file_openshell_proto_enumTypes[8].Descriptor() } func (WorkspaceRole) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[6] + return &file_openshell_proto_enumTypes[8] } func (x WorkspaceRole) Number() protoreflect.EnumNumber { @@ -436,7 +550,7 @@ func (x WorkspaceRole) Number() protoreflect.EnumNumber { // Deprecated: Use WorkspaceRole.Descriptor instead. func (WorkspaceRole) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{6} + return file_openshell_proto_rawDescGZIP(), []int{8} } // Stable recovery action for the most recent provider credential refresh @@ -482,11 +596,11 @@ func (x ProviderCredentialRefreshRecoveryAction) String() string { } func (ProviderCredentialRefreshRecoveryAction) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[7].Descriptor() + return file_openshell_proto_enumTypes[9].Descriptor() } func (ProviderCredentialRefreshRecoveryAction) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[7] + return &file_openshell_proto_enumTypes[9] } func (x ProviderCredentialRefreshRecoveryAction) Number() protoreflect.EnumNumber { @@ -495,7 +609,7 @@ func (x ProviderCredentialRefreshRecoveryAction) Number() protoreflect.EnumNumbe // Deprecated: Use ProviderCredentialRefreshRecoveryAction.Descriptor instead. func (ProviderCredentialRefreshRecoveryAction) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{7} + return file_openshell_proto_rawDescGZIP(), []int{9} } // IssueSandboxToken request. Empty body; identity is established by the @@ -7672,15 +7786,13 @@ func (x *DeleteProviderProfileResponse) GetDeleted() bool { return false } -// Get sandbox provider environment request. +// Retained as a source-compatible message for raw SDK consumers. The +// supervisor fetch RPC was removed; desired state now arrives on +// ConnectSupervisor. type GetSandboxProviderEnvironmentRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The sandbox ID. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Whether the requesting supervisor enforces endpoint bindings for static - // provider credentials. Gateways withhold static credential material when - // this capability is absent. - SupportsStaticCredentialBindings bool `protobuf:"varint,2,opt,name=supports_static_credential_bindings,json=supportsStaticCredentialBindings,proto3" json:"supports_static_credential_bindings,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` + SupportsStaticCredentialBindings bool `protobuf:"varint,2,opt,name=supports_static_credential_bindings,json=supportsStaticCredentialBindings,proto3" json:"supports_static_credential_bindings,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -7859,8 +7971,8 @@ func (x *StaticCredentialBinding) GetWorkloadCredentialHandle() string { return "" } -// Get sandbox provider environment response. -type GetSandboxProviderEnvironmentResponse struct { +// Complete provider environment and credential snapshot for a sandbox. +type ProviderEnvironmentSnapshot struct { state protoimpl.MessageState `protogen:"open.v1"` // Provider credential environment variables. Environment map[string]string `protobuf:"bytes,1,rep,name=environment,proto3" json:"environment,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` @@ -7884,20 +7996,20 @@ type GetSandboxProviderEnvironmentResponse struct { sizeCache protoimpl.SizeCache } -func (x *GetSandboxProviderEnvironmentResponse) Reset() { - *x = GetSandboxProviderEnvironmentResponse{} +func (x *ProviderEnvironmentSnapshot) Reset() { + *x = ProviderEnvironmentSnapshot{} mi := &file_openshell_proto_msgTypes[107] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *GetSandboxProviderEnvironmentResponse) String() string { +func (x *ProviderEnvironmentSnapshot) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetSandboxProviderEnvironmentResponse) ProtoMessage() {} +func (*ProviderEnvironmentSnapshot) ProtoMessage() {} -func (x *GetSandboxProviderEnvironmentResponse) ProtoReflect() protoreflect.Message { +func (x *ProviderEnvironmentSnapshot) ProtoReflect() protoreflect.Message { mi := &file_openshell_proto_msgTypes[107] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -7909,47 +8021,47 @@ func (x *GetSandboxProviderEnvironmentResponse) ProtoReflect() protoreflect.Mess return mi.MessageOf(x) } -// Deprecated: Use GetSandboxProviderEnvironmentResponse.ProtoReflect.Descriptor instead. -func (*GetSandboxProviderEnvironmentResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use ProviderEnvironmentSnapshot.ProtoReflect.Descriptor instead. +func (*ProviderEnvironmentSnapshot) Descriptor() ([]byte, []int) { return file_openshell_proto_rawDescGZIP(), []int{107} } -func (x *GetSandboxProviderEnvironmentResponse) GetEnvironment() map[string]string { +func (x *ProviderEnvironmentSnapshot) GetEnvironment() map[string]string { if x != nil { return x.Environment } return nil } -func (x *GetSandboxProviderEnvironmentResponse) GetProviderEnvRevision() uint64 { +func (x *ProviderEnvironmentSnapshot) GetProviderEnvRevision() uint64 { if x != nil { return x.ProviderEnvRevision } return 0 } -func (x *GetSandboxProviderEnvironmentResponse) GetCredentialExpiresAtMs() map[string]int64 { +func (x *ProviderEnvironmentSnapshot) GetCredentialExpiresAtMs() map[string]int64 { if x != nil { return x.CredentialExpiresAtMs } return nil } -func (x *GetSandboxProviderEnvironmentResponse) GetDynamicCredentials() map[string]*ProviderProfileCredential { +func (x *ProviderEnvironmentSnapshot) GetDynamicCredentials() map[string]*ProviderProfileCredential { if x != nil { return x.DynamicCredentials } return nil } -func (x *GetSandboxProviderEnvironmentResponse) GetStaticCredentialBindings() map[string]*StaticCredentialBinding { +func (x *ProviderEnvironmentSnapshot) GetStaticCredentialBindings() map[string]*StaticCredentialBinding { if x != nil { return x.StaticCredentialBindings } return nil } -func (x *GetSandboxProviderEnvironmentResponse) GetNonSecretEnvironmentKeys() []string { +func (x *ProviderEnvironmentSnapshot) GetNonSecretEnvironmentKeys() []string { if x != nil { return x.NonSecretEnvironmentKeys } @@ -9509,6 +9621,9 @@ type SupervisorMessage struct { // *SupervisorMessage_Heartbeat // *SupervisorMessage_RelayOpenResult // *SupervisorMessage_RelayClose + // *SupervisorMessage_BootstrapResult + // *SupervisorMessage_ConfigUpdateResult + // *SupervisorMessage_RuntimeReady Payload isSupervisorMessage_Payload `protobuf_oneof:"payload"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -9587,6 +9702,33 @@ func (x *SupervisorMessage) GetRelayClose() *RelayClose { return nil } +func (x *SupervisorMessage) GetBootstrapResult() *ConfigBootstrapResult { + if x != nil { + if x, ok := x.Payload.(*SupervisorMessage_BootstrapResult); ok { + return x.BootstrapResult + } + } + return nil +} + +func (x *SupervisorMessage) GetConfigUpdateResult() *ConfigUpdateResult { + if x != nil { + if x, ok := x.Payload.(*SupervisorMessage_ConfigUpdateResult); ok { + return x.ConfigUpdateResult + } + } + return nil +} + +func (x *SupervisorMessage) GetRuntimeReady() *SupervisorRuntimeReady { + if x != nil { + if x, ok := x.Payload.(*SupervisorMessage_RuntimeReady); ok { + return x.RuntimeReady + } + } + return nil +} + type isSupervisorMessage_Payload interface { isSupervisorMessage_Payload() } @@ -9607,6 +9749,18 @@ type SupervisorMessage_RelayClose struct { RelayClose *RelayClose `protobuf:"bytes,4,opt,name=relay_close,json=relayClose,proto3,oneof"` } +type SupervisorMessage_BootstrapResult struct { + BootstrapResult *ConfigBootstrapResult `protobuf:"bytes,5,opt,name=bootstrap_result,json=bootstrapResult,proto3,oneof"` +} + +type SupervisorMessage_ConfigUpdateResult struct { + ConfigUpdateResult *ConfigUpdateResult `protobuf:"bytes,6,opt,name=config_update_result,json=configUpdateResult,proto3,oneof"` +} + +type SupervisorMessage_RuntimeReady struct { + RuntimeReady *SupervisorRuntimeReady `protobuf:"bytes,7,opt,name=runtime_ready,json=runtimeReady,proto3,oneof"` +} + func (*SupervisorMessage_Hello) isSupervisorMessage_Payload() {} func (*SupervisorMessage_Heartbeat) isSupervisorMessage_Payload() {} @@ -9615,6 +9769,12 @@ func (*SupervisorMessage_RelayOpenResult) isSupervisorMessage_Payload() {} func (*SupervisorMessage_RelayClose) isSupervisorMessage_Payload() {} +func (*SupervisorMessage_BootstrapResult) isSupervisorMessage_Payload() {} + +func (*SupervisorMessage_ConfigUpdateResult) isSupervisorMessage_Payload() {} + +func (*SupervisorMessage_RuntimeReady) isSupervisorMessage_Payload() {} + // Envelope for gateway-to-supervisor messages on the ConnectSupervisor stream. type GatewayMessage struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -9625,6 +9785,7 @@ type GatewayMessage struct { // *GatewayMessage_Heartbeat // *GatewayMessage_RelayOpen // *GatewayMessage_RelayClose + // *GatewayMessage_ConfigUpdate Payload isGatewayMessage_Payload `protobuf_oneof:"payload"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -9712,6 +9873,15 @@ func (x *GatewayMessage) GetRelayClose() *RelayClose { return nil } +func (x *GatewayMessage) GetConfigUpdate() *ConfigUpdate { + if x != nil { + if x, ok := x.Payload.(*GatewayMessage_ConfigUpdate); ok { + return x.ConfigUpdate + } + } + return nil +} + type isGatewayMessage_Payload interface { isGatewayMessage_Payload() } @@ -9736,6 +9906,10 @@ type GatewayMessage_RelayClose struct { RelayClose *RelayClose `protobuf:"bytes,5,opt,name=relay_close,json=relayClose,proto3,oneof"` } +type GatewayMessage_ConfigUpdate struct { + ConfigUpdate *ConfigUpdate `protobuf:"bytes,6,opt,name=config_update,json=configUpdate,proto3,oneof"` +} + func (*GatewayMessage_SessionAccepted) isGatewayMessage_Payload() {} func (*GatewayMessage_SessionRejected) isGatewayMessage_Payload() {} @@ -9746,6 +9920,8 @@ func (*GatewayMessage_RelayOpen) isGatewayMessage_Payload() {} func (*GatewayMessage_RelayClose) isGatewayMessage_Payload() {} +func (*GatewayMessage_ConfigUpdate) isGatewayMessage_Payload() {} + // Supervisor identifies itself and the sandbox it manages. type SupervisorHello struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -9808,8 +9984,11 @@ type SessionAccepted struct { SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` // Recommended heartbeat interval in seconds. HeartbeatIntervalSecs uint32 `protobuf:"varint,2,opt,name=heartbeat_interval_secs,json=heartbeatIntervalSecs,proto3" json:"heartbeat_interval_secs,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Complete gateway-owned desired state. Its presence is the compatibility + // gate for supervisors that use gateway-backed configuration. + Bootstrap *ConfigBootstrap `protobuf:"bytes,3,opt,name=bootstrap,proto3" json:"bootstrap,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SessionAccepted) Reset() { @@ -9856,29 +10035,37 @@ func (x *SessionAccepted) GetHeartbeatIntervalSecs() uint32 { return 0 } -// Gateway rejects the supervisor session. -type SessionRejected struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Human-readable rejection reason. - Reason string `protobuf:"bytes,1,opt,name=reason,proto3" json:"reason,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache +func (x *SessionAccepted) GetBootstrap() *ConfigBootstrap { + if x != nil { + return x.Bootstrap + } + return nil } -func (x *SessionRejected) Reset() { - *x = SessionRejected{} +// Complete desired state required to initialize a gateway-backed supervisor. +type ConfigBootstrap struct { + state protoimpl.MessageState `protogen:"open.v1"` + SandboxConfig *sandboxv1.SandboxConfigSnapshot `protobuf:"bytes,1,opt,name=sandbox_config,json=sandboxConfig,proto3" json:"sandbox_config,omitempty"` + ProviderEnvironment *ProviderEnvironmentSnapshot `protobuf:"bytes,2,opt,name=provider_environment,json=providerEnvironment,proto3" json:"provider_environment,omitempty"` + InferenceBundle *inferencev1.InferenceBundleSnapshot `protobuf:"bytes,3,opt,name=inference_bundle,json=inferenceBundle,proto3" json:"inference_bundle,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ConfigBootstrap) Reset() { + *x = ConfigBootstrap{} mi := &file_openshell_proto_msgTypes[134] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *SessionRejected) String() string { +func (x *ConfigBootstrap) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SessionRejected) ProtoMessage() {} +func (*ConfigBootstrap) ProtoMessage() {} -func (x *SessionRejected) ProtoReflect() protoreflect.Message { +func (x *ConfigBootstrap) ProtoReflect() protoreflect.Message { mi := &file_openshell_proto_msgTypes[134] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9890,39 +10077,59 @@ func (x *SessionRejected) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SessionRejected.ProtoReflect.Descriptor instead. -func (*SessionRejected) Descriptor() ([]byte, []int) { +// Deprecated: Use ConfigBootstrap.ProtoReflect.Descriptor instead. +func (*ConfigBootstrap) Descriptor() ([]byte, []int) { return file_openshell_proto_rawDescGZIP(), []int{134} } -func (x *SessionRejected) GetReason() string { +func (x *ConfigBootstrap) GetSandboxConfig() *sandboxv1.SandboxConfigSnapshot { if x != nil { - return x.Reason + return x.SandboxConfig } - return "" + return nil } -// Supervisor heartbeat. -type SupervisorHeartbeat struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache +func (x *ConfigBootstrap) GetProviderEnvironment() *ProviderEnvironmentSnapshot { + if x != nil { + return x.ProviderEnvironment + } + return nil } -func (x *SupervisorHeartbeat) Reset() { - *x = SupervisorHeartbeat{} +func (x *ConfigBootstrap) GetInferenceBundle() *inferencev1.InferenceBundleSnapshot { + if x != nil { + return x.InferenceBundle + } + return nil +} + +// Aggregate bootstrap acknowledgement with a terminal outcome for every +// component. Error text is sanitized and bounded by the sender. +type ConfigBootstrapResult struct { + state protoimpl.MessageState `protogen:"open.v1"` + Status ConfigBootstrapStatus `protobuf:"varint,1,opt,name=status,proto3,enum=openshell.v1.ConfigBootstrapStatus" json:"status,omitempty"` + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + SandboxConfigOutcome ConfigApplyOutcome `protobuf:"varint,3,opt,name=sandbox_config_outcome,json=sandboxConfigOutcome,proto3,enum=openshell.v1.ConfigApplyOutcome" json:"sandbox_config_outcome,omitempty"` + ProviderEnvironmentOutcome ConfigApplyOutcome `protobuf:"varint,4,opt,name=provider_environment_outcome,json=providerEnvironmentOutcome,proto3,enum=openshell.v1.ConfigApplyOutcome" json:"provider_environment_outcome,omitempty"` + InferenceBundleOutcome ConfigApplyOutcome `protobuf:"varint,5,opt,name=inference_bundle_outcome,json=inferenceBundleOutcome,proto3,enum=openshell.v1.ConfigApplyOutcome" json:"inference_bundle_outcome,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ConfigBootstrapResult) Reset() { + *x = ConfigBootstrapResult{} mi := &file_openshell_proto_msgTypes[135] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *SupervisorHeartbeat) String() string { +func (x *ConfigBootstrapResult) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SupervisorHeartbeat) ProtoMessage() {} +func (*ConfigBootstrapResult) ProtoMessage() {} -func (x *SupervisorHeartbeat) ProtoReflect() protoreflect.Message { +func (x *ConfigBootstrapResult) ProtoReflect() protoreflect.Message { mi := &file_openshell_proto_msgTypes[135] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -9934,75 +10141,79 @@ func (x *SupervisorHeartbeat) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SupervisorHeartbeat.ProtoReflect.Descriptor instead. -func (*SupervisorHeartbeat) Descriptor() ([]byte, []int) { +// Deprecated: Use ConfigBootstrapResult.ProtoReflect.Descriptor instead. +func (*ConfigBootstrapResult) Descriptor() ([]byte, []int) { return file_openshell_proto_rawDescGZIP(), []int{135} } -// Gateway heartbeat. -type GatewayHeartbeat struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache +func (x *ConfigBootstrapResult) GetStatus() ConfigBootstrapStatus { + if x != nil { + return x.Status + } + return ConfigBootstrapStatus_CONFIG_BOOTSTRAP_STATUS_UNSPECIFIED } -func (x *GatewayHeartbeat) Reset() { - *x = GatewayHeartbeat{} - mi := &file_openshell_proto_msgTypes[136] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *ConfigBootstrapResult) GetError() string { + if x != nil { + return x.Error + } + return "" } -func (x *GatewayHeartbeat) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *ConfigBootstrapResult) GetSandboxConfigOutcome() ConfigApplyOutcome { + if x != nil { + return x.SandboxConfigOutcome + } + return ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_UNSPECIFIED } -func (*GatewayHeartbeat) ProtoMessage() {} - -func (x *GatewayHeartbeat) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[136] +func (x *ConfigBootstrapResult) GetProviderEnvironmentOutcome() ConfigApplyOutcome { if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms + return x.ProviderEnvironmentOutcome } - return mi.MessageOf(x) + return ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_UNSPECIFIED } -// Deprecated: Use GatewayHeartbeat.ProtoReflect.Descriptor instead. -func (*GatewayHeartbeat) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{136} +func (x *ConfigBootstrapResult) GetInferenceBundleOutcome() ConfigApplyOutcome { + if x != nil { + return x.InferenceBundleOutcome + } + return ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_UNSPECIFIED } -// Terminal result reported before the supervisor shuts down. A successful RPC -// response confirms that the result was durably handled by the gateway. -type ReportMainProcessExitRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - InstanceId string `protobuf:"bytes,2,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` - // Normalized process result. Signal exits use 128 + signal number. - ExitCode int32 `protobuf:"varint,3,opt,name=exit_code,json=exitCode,proto3" json:"exit_code,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache +// A level-triggered update for exactly one desired-state component. +type ConfigUpdate struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Opaque, non-empty identifier scoped to the active session. + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + // Types that are valid to be assigned to Component: + // + // *ConfigUpdate_SandboxConfig + // *ConfigUpdate_ProviderEnvironment + // *ConfigUpdate_InferenceBundle + Component isConfigUpdate_Component `protobuf_oneof:"component"` + // Monotonic within one session and component. Snapshot revisions remain + // content fingerprints and are compared only for equality. + ComponentSequence uint64 `protobuf:"varint,5,opt,name=component_sequence,json=componentSequence,proto3" json:"component_sequence,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } -func (x *ReportMainProcessExitRequest) Reset() { - *x = ReportMainProcessExitRequest{} - mi := &file_openshell_proto_msgTypes[137] +func (x *ConfigUpdate) Reset() { + *x = ConfigUpdate{} + mi := &file_openshell_proto_msgTypes[136] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *ReportMainProcessExitRequest) String() string { +func (x *ConfigUpdate) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ReportMainProcessExitRequest) ProtoMessage() {} +func (*ConfigUpdate) ProtoMessage() {} -func (x *ReportMainProcessExitRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[137] +func (x *ConfigUpdate) ProtoReflect() protoreflect.Message { + mi := &file_openshell_proto_msgTypes[136] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10013,53 +10224,532 @@ func (x *ReportMainProcessExitRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ReportMainProcessExitRequest.ProtoReflect.Descriptor instead. -func (*ReportMainProcessExitRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{137} +// Deprecated: Use ConfigUpdate.ProtoReflect.Descriptor instead. +func (*ConfigUpdate) Descriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{136} } -func (x *ReportMainProcessExitRequest) GetSandboxId() string { +func (x *ConfigUpdate) GetRequestId() string { if x != nil { - return x.SandboxId + return x.RequestId } return "" } -func (x *ReportMainProcessExitRequest) GetInstanceId() string { +func (x *ConfigUpdate) GetComponent() isConfigUpdate_Component { if x != nil { - return x.InstanceId + return x.Component } - return "" + return nil } -func (x *ReportMainProcessExitRequest) GetExitCode() int32 { +func (x *ConfigUpdate) GetSandboxConfig() *SandboxConfigUpdate { if x != nil { - return x.ExitCode + if x, ok := x.Component.(*ConfigUpdate_SandboxConfig); ok { + return x.SandboxConfig + } } - return 0 + return nil } -type ReportMainProcessExitResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache +func (x *ConfigUpdate) GetProviderEnvironment() *ProviderEnvironmentUpdate { + if x != nil { + if x, ok := x.Component.(*ConfigUpdate_ProviderEnvironment); ok { + return x.ProviderEnvironment + } + } + return nil } -func (x *ReportMainProcessExitResponse) Reset() { - *x = ReportMainProcessExitResponse{} - mi := &file_openshell_proto_msgTypes[138] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *ConfigUpdate) GetInferenceBundle() *InferenceBundleUpdate { + if x != nil { + if x, ok := x.Component.(*ConfigUpdate_InferenceBundle); ok { + return x.InferenceBundle + } + } + return nil } -func (x *ReportMainProcessExitResponse) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *ConfigUpdate) GetComponentSequence() uint64 { + if x != nil { + return x.ComponentSequence + } + return 0 } -func (*ReportMainProcessExitResponse) ProtoMessage() {} +type isConfigUpdate_Component interface { + isConfigUpdate_Component() +} -func (x *ReportMainProcessExitResponse) ProtoReflect() protoreflect.Message { +type ConfigUpdate_SandboxConfig struct { + SandboxConfig *SandboxConfigUpdate `protobuf:"bytes,2,opt,name=sandbox_config,json=sandboxConfig,proto3,oneof"` +} + +type ConfigUpdate_ProviderEnvironment struct { + ProviderEnvironment *ProviderEnvironmentUpdate `protobuf:"bytes,3,opt,name=provider_environment,json=providerEnvironment,proto3,oneof"` +} + +type ConfigUpdate_InferenceBundle struct { + InferenceBundle *InferenceBundleUpdate `protobuf:"bytes,4,opt,name=inference_bundle,json=inferenceBundle,proto3,oneof"` +} + +func (*ConfigUpdate_SandboxConfig) isConfigUpdate_Component() {} + +func (*ConfigUpdate_ProviderEnvironment) isConfigUpdate_Component() {} + +func (*ConfigUpdate_InferenceBundle) isConfigUpdate_Component() {} + +type SandboxConfigUpdate struct { + state protoimpl.MessageState `protogen:"open.v1"` + Snapshot *sandboxv1.SandboxConfigSnapshot `protobuf:"bytes,1,opt,name=snapshot,proto3" json:"snapshot,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SandboxConfigUpdate) Reset() { + *x = SandboxConfigUpdate{} + mi := &file_openshell_proto_msgTypes[137] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SandboxConfigUpdate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SandboxConfigUpdate) ProtoMessage() {} + +func (x *SandboxConfigUpdate) ProtoReflect() protoreflect.Message { + mi := &file_openshell_proto_msgTypes[137] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SandboxConfigUpdate.ProtoReflect.Descriptor instead. +func (*SandboxConfigUpdate) Descriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{137} +} + +func (x *SandboxConfigUpdate) GetSnapshot() *sandboxv1.SandboxConfigSnapshot { + if x != nil { + return x.Snapshot + } + return nil +} + +type ProviderEnvironmentUpdate struct { + state protoimpl.MessageState `protogen:"open.v1"` + Snapshot *ProviderEnvironmentSnapshot `protobuf:"bytes,1,opt,name=snapshot,proto3" json:"snapshot,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProviderEnvironmentUpdate) Reset() { + *x = ProviderEnvironmentUpdate{} mi := &file_openshell_proto_msgTypes[138] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProviderEnvironmentUpdate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProviderEnvironmentUpdate) ProtoMessage() {} + +func (x *ProviderEnvironmentUpdate) ProtoReflect() protoreflect.Message { + mi := &file_openshell_proto_msgTypes[138] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProviderEnvironmentUpdate.ProtoReflect.Descriptor instead. +func (*ProviderEnvironmentUpdate) Descriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{138} +} + +func (x *ProviderEnvironmentUpdate) GetSnapshot() *ProviderEnvironmentSnapshot { + if x != nil { + return x.Snapshot + } + return nil +} + +type InferenceBundleUpdate struct { + state protoimpl.MessageState `protogen:"open.v1"` + Snapshot *inferencev1.InferenceBundleSnapshot `protobuf:"bytes,1,opt,name=snapshot,proto3" json:"snapshot,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InferenceBundleUpdate) Reset() { + *x = InferenceBundleUpdate{} + mi := &file_openshell_proto_msgTypes[139] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InferenceBundleUpdate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InferenceBundleUpdate) ProtoMessage() {} + +func (x *InferenceBundleUpdate) ProtoReflect() protoreflect.Message { + mi := &file_openshell_proto_msgTypes[139] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InferenceBundleUpdate.ProtoReflect.Descriptor instead. +func (*InferenceBundleUpdate) Descriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{139} +} + +func (x *InferenceBundleUpdate) GetSnapshot() *inferencev1.InferenceBundleSnapshot { + if x != nil { + return x.Snapshot + } + return nil +} + +type ConfigUpdateResult struct { + state protoimpl.MessageState `protogen:"open.v1"` + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + ComponentSequence uint64 `protobuf:"varint,2,opt,name=component_sequence,json=componentSequence,proto3" json:"component_sequence,omitempty"` + Outcome ConfigApplyOutcome `protobuf:"varint,3,opt,name=outcome,proto3,enum=openshell.v1.ConfigApplyOutcome" json:"outcome,omitempty"` + // Sanitized error text, capped at 1 KiB by the sender. + Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ConfigUpdateResult) Reset() { + *x = ConfigUpdateResult{} + mi := &file_openshell_proto_msgTypes[140] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ConfigUpdateResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConfigUpdateResult) ProtoMessage() {} + +func (x *ConfigUpdateResult) ProtoReflect() protoreflect.Message { + mi := &file_openshell_proto_msgTypes[140] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConfigUpdateResult.ProtoReflect.Descriptor instead. +func (*ConfigUpdateResult) Descriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{140} +} + +func (x *ConfigUpdateResult) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +func (x *ConfigUpdateResult) GetComponentSequence() uint64 { + if x != nil { + return x.ComponentSequence + } + return 0 +} + +func (x *ConfigUpdateResult) GetOutcome() ConfigApplyOutcome { + if x != nil { + return x.Outcome + } + return ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_UNSPECIFIED +} + +func (x *ConfigUpdateResult) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +// Signals that runtime-dependent services such as SSH and relays are ready. +type SupervisorRuntimeReady struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Canonical workload instance associated with this ready runtime. + InstanceId string `protobuf:"bytes,1,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SupervisorRuntimeReady) Reset() { + *x = SupervisorRuntimeReady{} + mi := &file_openshell_proto_msgTypes[141] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SupervisorRuntimeReady) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SupervisorRuntimeReady) ProtoMessage() {} + +func (x *SupervisorRuntimeReady) ProtoReflect() protoreflect.Message { + mi := &file_openshell_proto_msgTypes[141] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SupervisorRuntimeReady.ProtoReflect.Descriptor instead. +func (*SupervisorRuntimeReady) Descriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{141} +} + +func (x *SupervisorRuntimeReady) GetInstanceId() string { + if x != nil { + return x.InstanceId + } + return "" +} + +// Gateway rejects the supervisor session. +type SessionRejected struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Human-readable rejection reason. + Reason string `protobuf:"bytes,1,opt,name=reason,proto3" json:"reason,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SessionRejected) Reset() { + *x = SessionRejected{} + mi := &file_openshell_proto_msgTypes[142] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SessionRejected) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SessionRejected) ProtoMessage() {} + +func (x *SessionRejected) ProtoReflect() protoreflect.Message { + mi := &file_openshell_proto_msgTypes[142] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SessionRejected.ProtoReflect.Descriptor instead. +func (*SessionRejected) Descriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{142} +} + +func (x *SessionRejected) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +// Supervisor heartbeat. +type SupervisorHeartbeat struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SupervisorHeartbeat) Reset() { + *x = SupervisorHeartbeat{} + mi := &file_openshell_proto_msgTypes[143] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SupervisorHeartbeat) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SupervisorHeartbeat) ProtoMessage() {} + +func (x *SupervisorHeartbeat) ProtoReflect() protoreflect.Message { + mi := &file_openshell_proto_msgTypes[143] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SupervisorHeartbeat.ProtoReflect.Descriptor instead. +func (*SupervisorHeartbeat) Descriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{143} +} + +// Gateway heartbeat. +type GatewayHeartbeat struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GatewayHeartbeat) Reset() { + *x = GatewayHeartbeat{} + mi := &file_openshell_proto_msgTypes[144] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GatewayHeartbeat) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GatewayHeartbeat) ProtoMessage() {} + +func (x *GatewayHeartbeat) ProtoReflect() protoreflect.Message { + mi := &file_openshell_proto_msgTypes[144] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GatewayHeartbeat.ProtoReflect.Descriptor instead. +func (*GatewayHeartbeat) Descriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{144} +} + +// Terminal result reported before the supervisor shuts down. A successful RPC +// response confirms that the result was durably handled by the gateway. +type ReportMainProcessExitRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` + InstanceId string `protobuf:"bytes,2,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` + // Normalized process result. Signal exits use 128 + signal number. + ExitCode int32 `protobuf:"varint,3,opt,name=exit_code,json=exitCode,proto3" json:"exit_code,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReportMainProcessExitRequest) Reset() { + *x = ReportMainProcessExitRequest{} + mi := &file_openshell_proto_msgTypes[145] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReportMainProcessExitRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReportMainProcessExitRequest) ProtoMessage() {} + +func (x *ReportMainProcessExitRequest) ProtoReflect() protoreflect.Message { + mi := &file_openshell_proto_msgTypes[145] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReportMainProcessExitRequest.ProtoReflect.Descriptor instead. +func (*ReportMainProcessExitRequest) Descriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{145} +} + +func (x *ReportMainProcessExitRequest) GetSandboxId() string { + if x != nil { + return x.SandboxId + } + return "" +} + +func (x *ReportMainProcessExitRequest) GetInstanceId() string { + if x != nil { + return x.InstanceId + } + return "" +} + +func (x *ReportMainProcessExitRequest) GetExitCode() int32 { + if x != nil { + return x.ExitCode + } + return 0 +} + +type ReportMainProcessExitResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReportMainProcessExitResponse) Reset() { + *x = ReportMainProcessExitResponse{} + mi := &file_openshell_proto_msgTypes[146] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReportMainProcessExitResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReportMainProcessExitResponse) ProtoMessage() {} + +func (x *ReportMainProcessExitResponse) ProtoReflect() protoreflect.Message { + mi := &file_openshell_proto_msgTypes[146] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10072,7 +10762,7 @@ func (x *ReportMainProcessExitResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportMainProcessExitResponse.ProtoReflect.Descriptor instead. func (*ReportMainProcessExitResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{138} + return file_openshell_proto_rawDescGZIP(), []int{146} } // Gateway requests the supervisor to open a relay channel. @@ -10101,7 +10791,7 @@ type RelayOpen struct { func (x *RelayOpen) Reset() { *x = RelayOpen{} - mi := &file_openshell_proto_msgTypes[139] + mi := &file_openshell_proto_msgTypes[147] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10113,7 +10803,7 @@ func (x *RelayOpen) String() string { func (*RelayOpen) ProtoMessage() {} func (x *RelayOpen) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[139] + mi := &file_openshell_proto_msgTypes[147] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10126,7 +10816,7 @@ func (x *RelayOpen) ProtoReflect() protoreflect.Message { // Deprecated: Use RelayOpen.ProtoReflect.Descriptor instead. func (*RelayOpen) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{139} + return file_openshell_proto_rawDescGZIP(), []int{147} } func (x *RelayOpen) GetChannelId() string { @@ -10193,7 +10883,7 @@ type SshRelayTarget struct { func (x *SshRelayTarget) Reset() { *x = SshRelayTarget{} - mi := &file_openshell_proto_msgTypes[140] + mi := &file_openshell_proto_msgTypes[148] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10205,7 +10895,7 @@ func (x *SshRelayTarget) String() string { func (*SshRelayTarget) ProtoMessage() {} func (x *SshRelayTarget) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[140] + mi := &file_openshell_proto_msgTypes[148] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10218,7 +10908,7 @@ func (x *SshRelayTarget) ProtoReflect() protoreflect.Message { // Deprecated: Use SshRelayTarget.ProtoReflect.Descriptor instead. func (*SshRelayTarget) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{140} + return file_openshell_proto_rawDescGZIP(), []int{148} } // TCP target dialed by the supervisor from inside the sandbox. @@ -10234,7 +10924,7 @@ type TcpRelayTarget struct { func (x *TcpRelayTarget) Reset() { *x = TcpRelayTarget{} - mi := &file_openshell_proto_msgTypes[141] + mi := &file_openshell_proto_msgTypes[149] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10246,7 +10936,7 @@ func (x *TcpRelayTarget) String() string { func (*TcpRelayTarget) ProtoMessage() {} func (x *TcpRelayTarget) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[141] + mi := &file_openshell_proto_msgTypes[149] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10259,7 +10949,7 @@ func (x *TcpRelayTarget) ProtoReflect() protoreflect.Message { // Deprecated: Use TcpRelayTarget.ProtoReflect.Descriptor instead. func (*TcpRelayTarget) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{141} + return file_openshell_proto_rawDescGZIP(), []int{149} } func (x *TcpRelayTarget) GetHost() string { @@ -10287,7 +10977,7 @@ type RelayInit struct { func (x *RelayInit) Reset() { *x = RelayInit{} - mi := &file_openshell_proto_msgTypes[142] + mi := &file_openshell_proto_msgTypes[150] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10299,7 +10989,7 @@ func (x *RelayInit) String() string { func (*RelayInit) ProtoMessage() {} func (x *RelayInit) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[142] + mi := &file_openshell_proto_msgTypes[150] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10312,7 +11002,7 @@ func (x *RelayInit) ProtoReflect() protoreflect.Message { // Deprecated: Use RelayInit.ProtoReflect.Descriptor instead. func (*RelayInit) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{142} + return file_openshell_proto_rawDescGZIP(), []int{150} } func (x *RelayInit) GetChannelId() string { @@ -10339,7 +11029,7 @@ type RelayFrame struct { func (x *RelayFrame) Reset() { *x = RelayFrame{} - mi := &file_openshell_proto_msgTypes[143] + mi := &file_openshell_proto_msgTypes[151] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10351,7 +11041,7 @@ func (x *RelayFrame) String() string { func (*RelayFrame) ProtoMessage() {} func (x *RelayFrame) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[143] + mi := &file_openshell_proto_msgTypes[151] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10364,7 +11054,7 @@ func (x *RelayFrame) ProtoReflect() protoreflect.Message { // Deprecated: Use RelayFrame.ProtoReflect.Descriptor instead. func (*RelayFrame) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{143} + return file_openshell_proto_rawDescGZIP(), []int{151} } func (x *RelayFrame) GetPayload() isRelayFrame_Payload { @@ -10423,7 +11113,7 @@ type RelayOpenResult struct { func (x *RelayOpenResult) Reset() { *x = RelayOpenResult{} - mi := &file_openshell_proto_msgTypes[144] + mi := &file_openshell_proto_msgTypes[152] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10435,7 +11125,7 @@ func (x *RelayOpenResult) String() string { func (*RelayOpenResult) ProtoMessage() {} func (x *RelayOpenResult) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[144] + mi := &file_openshell_proto_msgTypes[152] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10448,7 +11138,7 @@ func (x *RelayOpenResult) ProtoReflect() protoreflect.Message { // Deprecated: Use RelayOpenResult.ProtoReflect.Descriptor instead. func (*RelayOpenResult) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{144} + return file_openshell_proto_rawDescGZIP(), []int{152} } func (x *RelayOpenResult) GetChannelId() string { @@ -10485,7 +11175,7 @@ type RelayClose struct { func (x *RelayClose) Reset() { *x = RelayClose{} - mi := &file_openshell_proto_msgTypes[145] + mi := &file_openshell_proto_msgTypes[153] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10497,7 +11187,7 @@ func (x *RelayClose) String() string { func (*RelayClose) ProtoMessage() {} func (x *RelayClose) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[145] + mi := &file_openshell_proto_msgTypes[153] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10510,7 +11200,7 @@ func (x *RelayClose) ProtoReflect() protoreflect.Message { // Deprecated: Use RelayClose.ProtoReflect.Descriptor instead. func (*RelayClose) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{145} + return file_openshell_proto_rawDescGZIP(), []int{153} } func (x *RelayClose) GetChannelId() string { @@ -10544,7 +11234,7 @@ type L7RequestSample struct { func (x *L7RequestSample) Reset() { *x = L7RequestSample{} - mi := &file_openshell_proto_msgTypes[146] + mi := &file_openshell_proto_msgTypes[154] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10556,7 +11246,7 @@ func (x *L7RequestSample) String() string { func (*L7RequestSample) ProtoMessage() {} func (x *L7RequestSample) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[146] + mi := &file_openshell_proto_msgTypes[154] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10569,7 +11259,7 @@ func (x *L7RequestSample) ProtoReflect() protoreflect.Message { // Deprecated: Use L7RequestSample.ProtoReflect.Descriptor instead. func (*L7RequestSample) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{146} + return file_openshell_proto_rawDescGZIP(), []int{154} } func (x *L7RequestSample) GetMethod() string { @@ -10643,7 +11333,7 @@ type DenialSummary struct { func (x *DenialSummary) Reset() { *x = DenialSummary{} - mi := &file_openshell_proto_msgTypes[147] + mi := &file_openshell_proto_msgTypes[155] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10655,7 +11345,7 @@ func (x *DenialSummary) String() string { func (*DenialSummary) ProtoMessage() {} func (x *DenialSummary) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[147] + mi := &file_openshell_proto_msgTypes[155] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10668,7 +11358,7 @@ func (x *DenialSummary) ProtoReflect() protoreflect.Message { // Deprecated: Use DenialSummary.ProtoReflect.Descriptor instead. func (*DenialSummary) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{147} + return file_openshell_proto_rawDescGZIP(), []int{155} } func (x *DenialSummary) GetSandboxId() string { @@ -10803,7 +11493,7 @@ type DenialGroupCount struct { func (x *DenialGroupCount) Reset() { *x = DenialGroupCount{} - mi := &file_openshell_proto_msgTypes[148] + mi := &file_openshell_proto_msgTypes[156] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10815,7 +11505,7 @@ func (x *DenialGroupCount) String() string { func (*DenialGroupCount) ProtoMessage() {} func (x *DenialGroupCount) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[148] + mi := &file_openshell_proto_msgTypes[156] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10828,7 +11518,7 @@ func (x *DenialGroupCount) ProtoReflect() protoreflect.Message { // Deprecated: Use DenialGroupCount.ProtoReflect.Descriptor instead. func (*DenialGroupCount) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{148} + return file_openshell_proto_rawDescGZIP(), []int{156} } func (x *DenialGroupCount) GetDenyGroup() string { @@ -10861,7 +11551,7 @@ type NetworkActivitySummary struct { func (x *NetworkActivitySummary) Reset() { *x = NetworkActivitySummary{} - mi := &file_openshell_proto_msgTypes[149] + mi := &file_openshell_proto_msgTypes[157] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10873,7 +11563,7 @@ func (x *NetworkActivitySummary) String() string { func (*NetworkActivitySummary) ProtoMessage() {} func (x *NetworkActivitySummary) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[149] + mi := &file_openshell_proto_msgTypes[157] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10886,7 +11576,7 @@ func (x *NetworkActivitySummary) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkActivitySummary.ProtoReflect.Descriptor instead. func (*NetworkActivitySummary) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{149} + return file_openshell_proto_rawDescGZIP(), []int{157} } func (x *NetworkActivitySummary) GetNetworkActivityCount() uint32 { @@ -10974,7 +11664,7 @@ type PolicyChunk struct { func (x *PolicyChunk) Reset() { *x = PolicyChunk{} - mi := &file_openshell_proto_msgTypes[150] + mi := &file_openshell_proto_msgTypes[158] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10986,7 +11676,7 @@ func (x *PolicyChunk) String() string { func (*PolicyChunk) ProtoMessage() {} func (x *PolicyChunk) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[150] + mi := &file_openshell_proto_msgTypes[158] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10999,7 +11689,7 @@ func (x *PolicyChunk) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyChunk.ProtoReflect.Descriptor instead. func (*PolicyChunk) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{150} + return file_openshell_proto_rawDescGZIP(), []int{158} } func (x *PolicyChunk) GetId() string { @@ -11187,7 +11877,7 @@ type DraftPolicyUpdate struct { func (x *DraftPolicyUpdate) Reset() { *x = DraftPolicyUpdate{} - mi := &file_openshell_proto_msgTypes[151] + mi := &file_openshell_proto_msgTypes[159] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11199,7 +11889,7 @@ func (x *DraftPolicyUpdate) String() string { func (*DraftPolicyUpdate) ProtoMessage() {} func (x *DraftPolicyUpdate) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[151] + mi := &file_openshell_proto_msgTypes[159] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11212,7 +11902,7 @@ func (x *DraftPolicyUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use DraftPolicyUpdate.ProtoReflect.Descriptor instead. func (*DraftPolicyUpdate) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{151} + return file_openshell_proto_rawDescGZIP(), []int{159} } func (x *DraftPolicyUpdate) GetDraftVersion() uint64 { @@ -11270,7 +11960,7 @@ type SubmitPolicyAnalysisRequest struct { func (x *SubmitPolicyAnalysisRequest) Reset() { *x = SubmitPolicyAnalysisRequest{} - mi := &file_openshell_proto_msgTypes[152] + mi := &file_openshell_proto_msgTypes[160] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11282,7 +11972,7 @@ func (x *SubmitPolicyAnalysisRequest) String() string { func (*SubmitPolicyAnalysisRequest) ProtoMessage() {} func (x *SubmitPolicyAnalysisRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[152] + mi := &file_openshell_proto_msgTypes[160] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11295,7 +11985,7 @@ func (x *SubmitPolicyAnalysisRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SubmitPolicyAnalysisRequest.ProtoReflect.Descriptor instead. func (*SubmitPolicyAnalysisRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{152} + return file_openshell_proto_rawDescGZIP(), []int{160} } func (x *SubmitPolicyAnalysisRequest) GetSummaries() []*DenialSummary { @@ -11358,7 +12048,7 @@ type SubmitPolicyAnalysisResponse struct { func (x *SubmitPolicyAnalysisResponse) Reset() { *x = SubmitPolicyAnalysisResponse{} - mi := &file_openshell_proto_msgTypes[153] + mi := &file_openshell_proto_msgTypes[161] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11370,7 +12060,7 @@ func (x *SubmitPolicyAnalysisResponse) String() string { func (*SubmitPolicyAnalysisResponse) ProtoMessage() {} func (x *SubmitPolicyAnalysisResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[153] + mi := &file_openshell_proto_msgTypes[161] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11383,7 +12073,7 @@ func (x *SubmitPolicyAnalysisResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SubmitPolicyAnalysisResponse.ProtoReflect.Descriptor instead. func (*SubmitPolicyAnalysisResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{153} + return file_openshell_proto_rawDescGZIP(), []int{161} } func (x *SubmitPolicyAnalysisResponse) GetAcceptedChunks() uint32 { @@ -11429,7 +12119,7 @@ type GetDraftPolicyRequest struct { func (x *GetDraftPolicyRequest) Reset() { *x = GetDraftPolicyRequest{} - mi := &file_openshell_proto_msgTypes[154] + mi := &file_openshell_proto_msgTypes[162] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11441,7 +12131,7 @@ func (x *GetDraftPolicyRequest) String() string { func (*GetDraftPolicyRequest) ProtoMessage() {} func (x *GetDraftPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[154] + mi := &file_openshell_proto_msgTypes[162] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11454,7 +12144,7 @@ func (x *GetDraftPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDraftPolicyRequest.ProtoReflect.Descriptor instead. func (*GetDraftPolicyRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{154} + return file_openshell_proto_rawDescGZIP(), []int{162} } func (x *GetDraftPolicyRequest) GetName() string { @@ -11494,7 +12184,7 @@ type GetDraftPolicyResponse struct { func (x *GetDraftPolicyResponse) Reset() { *x = GetDraftPolicyResponse{} - mi := &file_openshell_proto_msgTypes[155] + mi := &file_openshell_proto_msgTypes[163] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11506,7 +12196,7 @@ func (x *GetDraftPolicyResponse) String() string { func (*GetDraftPolicyResponse) ProtoMessage() {} func (x *GetDraftPolicyResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[155] + mi := &file_openshell_proto_msgTypes[163] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11519,7 +12209,7 @@ func (x *GetDraftPolicyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDraftPolicyResponse.ProtoReflect.Descriptor instead. func (*GetDraftPolicyResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{155} + return file_openshell_proto_rawDescGZIP(), []int{163} } func (x *GetDraftPolicyResponse) GetChunks() []*PolicyChunk { @@ -11568,7 +12258,7 @@ type ApproveDraftChunkRequest struct { func (x *ApproveDraftChunkRequest) Reset() { *x = ApproveDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[156] + mi := &file_openshell_proto_msgTypes[164] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11580,7 +12270,7 @@ func (x *ApproveDraftChunkRequest) String() string { func (*ApproveDraftChunkRequest) ProtoMessage() {} func (x *ApproveDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[156] + mi := &file_openshell_proto_msgTypes[164] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11593,7 +12283,7 @@ func (x *ApproveDraftChunkRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ApproveDraftChunkRequest.ProtoReflect.Descriptor instead. func (*ApproveDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{156} + return file_openshell_proto_rawDescGZIP(), []int{164} } func (x *ApproveDraftChunkRequest) GetName() string { @@ -11636,7 +12326,7 @@ type ApproveDraftChunkResponse struct { func (x *ApproveDraftChunkResponse) Reset() { *x = ApproveDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[157] + mi := &file_openshell_proto_msgTypes[165] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11648,7 +12338,7 @@ func (x *ApproveDraftChunkResponse) String() string { func (*ApproveDraftChunkResponse) ProtoMessage() {} func (x *ApproveDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[157] + mi := &file_openshell_proto_msgTypes[165] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11661,7 +12351,7 @@ func (x *ApproveDraftChunkResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ApproveDraftChunkResponse.ProtoReflect.Descriptor instead. func (*ApproveDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{157} + return file_openshell_proto_rawDescGZIP(), []int{165} } func (x *ApproveDraftChunkResponse) GetPolicyVersion() uint32 { @@ -11695,7 +12385,7 @@ type RejectDraftChunkRequest struct { func (x *RejectDraftChunkRequest) Reset() { *x = RejectDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[158] + mi := &file_openshell_proto_msgTypes[166] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11707,7 +12397,7 @@ func (x *RejectDraftChunkRequest) String() string { func (*RejectDraftChunkRequest) ProtoMessage() {} func (x *RejectDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[158] + mi := &file_openshell_proto_msgTypes[166] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11720,7 +12410,7 @@ func (x *RejectDraftChunkRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RejectDraftChunkRequest.ProtoReflect.Descriptor instead. func (*RejectDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{158} + return file_openshell_proto_rawDescGZIP(), []int{166} } func (x *RejectDraftChunkRequest) GetName() string { @@ -11759,7 +12449,7 @@ type RejectDraftChunkResponse struct { func (x *RejectDraftChunkResponse) Reset() { *x = RejectDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[159] + mi := &file_openshell_proto_msgTypes[167] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11771,7 +12461,7 @@ func (x *RejectDraftChunkResponse) String() string { func (*RejectDraftChunkResponse) ProtoMessage() {} func (x *RejectDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[159] + mi := &file_openshell_proto_msgTypes[167] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11784,7 +12474,7 @@ func (x *RejectDraftChunkResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RejectDraftChunkResponse.ProtoReflect.Descriptor instead. func (*RejectDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{159} + return file_openshell_proto_rawDescGZIP(), []int{167} } // Approve all pending chunks. @@ -11798,7 +12488,7 @@ type DraftChunkApproval struct { func (x *DraftChunkApproval) Reset() { *x = DraftChunkApproval{} - mi := &file_openshell_proto_msgTypes[160] + mi := &file_openshell_proto_msgTypes[168] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11810,7 +12500,7 @@ func (x *DraftChunkApproval) String() string { func (*DraftChunkApproval) ProtoMessage() {} func (x *DraftChunkApproval) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[160] + mi := &file_openshell_proto_msgTypes[168] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11823,7 +12513,7 @@ func (x *DraftChunkApproval) ProtoReflect() protoreflect.Message { // Deprecated: Use DraftChunkApproval.ProtoReflect.Descriptor instead. func (*DraftChunkApproval) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{160} + return file_openshell_proto_rawDescGZIP(), []int{168} } func (x *DraftChunkApproval) GetChunkId() string { @@ -11857,7 +12547,7 @@ type ApproveAllDraftChunksRequest struct { func (x *ApproveAllDraftChunksRequest) Reset() { *x = ApproveAllDraftChunksRequest{} - mi := &file_openshell_proto_msgTypes[161] + mi := &file_openshell_proto_msgTypes[169] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11869,7 +12559,7 @@ func (x *ApproveAllDraftChunksRequest) String() string { func (*ApproveAllDraftChunksRequest) ProtoMessage() {} func (x *ApproveAllDraftChunksRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[161] + mi := &file_openshell_proto_msgTypes[169] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11882,7 +12572,7 @@ func (x *ApproveAllDraftChunksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ApproveAllDraftChunksRequest.ProtoReflect.Descriptor instead. func (*ApproveAllDraftChunksRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{161} + return file_openshell_proto_rawDescGZIP(), []int{169} } func (x *ApproveAllDraftChunksRequest) GetName() string { @@ -11930,7 +12620,7 @@ type ApproveAllDraftChunksResponse struct { func (x *ApproveAllDraftChunksResponse) Reset() { *x = ApproveAllDraftChunksResponse{} - mi := &file_openshell_proto_msgTypes[162] + mi := &file_openshell_proto_msgTypes[170] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11942,7 +12632,7 @@ func (x *ApproveAllDraftChunksResponse) String() string { func (*ApproveAllDraftChunksResponse) ProtoMessage() {} func (x *ApproveAllDraftChunksResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[162] + mi := &file_openshell_proto_msgTypes[170] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11955,7 +12645,7 @@ func (x *ApproveAllDraftChunksResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ApproveAllDraftChunksResponse.ProtoReflect.Descriptor instead. func (*ApproveAllDraftChunksResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{162} + return file_openshell_proto_rawDescGZIP(), []int{170} } func (x *ApproveAllDraftChunksResponse) GetPolicyVersion() uint32 { @@ -12003,7 +12693,7 @@ type EditDraftChunkRequest struct { func (x *EditDraftChunkRequest) Reset() { *x = EditDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[163] + mi := &file_openshell_proto_msgTypes[171] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12015,7 +12705,7 @@ func (x *EditDraftChunkRequest) String() string { func (*EditDraftChunkRequest) ProtoMessage() {} func (x *EditDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[163] + mi := &file_openshell_proto_msgTypes[171] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12028,7 +12718,7 @@ func (x *EditDraftChunkRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use EditDraftChunkRequest.ProtoReflect.Descriptor instead. func (*EditDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{163} + return file_openshell_proto_rawDescGZIP(), []int{171} } func (x *EditDraftChunkRequest) GetName() string { @@ -12067,7 +12757,7 @@ type EditDraftChunkResponse struct { func (x *EditDraftChunkResponse) Reset() { *x = EditDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[164] + mi := &file_openshell_proto_msgTypes[172] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12079,7 +12769,7 @@ func (x *EditDraftChunkResponse) String() string { func (*EditDraftChunkResponse) ProtoMessage() {} func (x *EditDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[164] + mi := &file_openshell_proto_msgTypes[172] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12092,7 +12782,7 @@ func (x *EditDraftChunkResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use EditDraftChunkResponse.ProtoReflect.Descriptor instead. func (*EditDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{164} + return file_openshell_proto_rawDescGZIP(), []int{172} } // Reverse an approval (remove merged rule from active policy). @@ -12110,7 +12800,7 @@ type UndoDraftChunkRequest struct { func (x *UndoDraftChunkRequest) Reset() { *x = UndoDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[165] + mi := &file_openshell_proto_msgTypes[173] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12122,7 +12812,7 @@ func (x *UndoDraftChunkRequest) String() string { func (*UndoDraftChunkRequest) ProtoMessage() {} func (x *UndoDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[165] + mi := &file_openshell_proto_msgTypes[173] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12135,7 +12825,7 @@ func (x *UndoDraftChunkRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UndoDraftChunkRequest.ProtoReflect.Descriptor instead. func (*UndoDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{165} + return file_openshell_proto_rawDescGZIP(), []int{173} } func (x *UndoDraftChunkRequest) GetName() string { @@ -12171,7 +12861,7 @@ type UndoDraftChunkResponse struct { func (x *UndoDraftChunkResponse) Reset() { *x = UndoDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[166] + mi := &file_openshell_proto_msgTypes[174] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12183,7 +12873,7 @@ func (x *UndoDraftChunkResponse) String() string { func (*UndoDraftChunkResponse) ProtoMessage() {} func (x *UndoDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[166] + mi := &file_openshell_proto_msgTypes[174] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12196,7 +12886,7 @@ func (x *UndoDraftChunkResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UndoDraftChunkResponse.ProtoReflect.Descriptor instead. func (*UndoDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{166} + return file_openshell_proto_rawDescGZIP(), []int{174} } func (x *UndoDraftChunkResponse) GetPolicyVersion() uint32 { @@ -12226,7 +12916,7 @@ type ClearDraftChunksRequest struct { func (x *ClearDraftChunksRequest) Reset() { *x = ClearDraftChunksRequest{} - mi := &file_openshell_proto_msgTypes[167] + mi := &file_openshell_proto_msgTypes[175] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12238,7 +12928,7 @@ func (x *ClearDraftChunksRequest) String() string { func (*ClearDraftChunksRequest) ProtoMessage() {} func (x *ClearDraftChunksRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[167] + mi := &file_openshell_proto_msgTypes[175] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12251,7 +12941,7 @@ func (x *ClearDraftChunksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ClearDraftChunksRequest.ProtoReflect.Descriptor instead. func (*ClearDraftChunksRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{167} + return file_openshell_proto_rawDescGZIP(), []int{175} } func (x *ClearDraftChunksRequest) GetName() string { @@ -12278,7 +12968,7 @@ type ClearDraftChunksResponse struct { func (x *ClearDraftChunksResponse) Reset() { *x = ClearDraftChunksResponse{} - mi := &file_openshell_proto_msgTypes[168] + mi := &file_openshell_proto_msgTypes[176] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12290,7 +12980,7 @@ func (x *ClearDraftChunksResponse) String() string { func (*ClearDraftChunksResponse) ProtoMessage() {} func (x *ClearDraftChunksResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[168] + mi := &file_openshell_proto_msgTypes[176] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12303,7 +12993,7 @@ func (x *ClearDraftChunksResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ClearDraftChunksResponse.ProtoReflect.Descriptor instead. func (*ClearDraftChunksResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{168} + return file_openshell_proto_rawDescGZIP(), []int{176} } func (x *ClearDraftChunksResponse) GetChunksCleared() uint32 { @@ -12326,7 +13016,7 @@ type GetDraftHistoryRequest struct { func (x *GetDraftHistoryRequest) Reset() { *x = GetDraftHistoryRequest{} - mi := &file_openshell_proto_msgTypes[169] + mi := &file_openshell_proto_msgTypes[177] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12338,7 +13028,7 @@ func (x *GetDraftHistoryRequest) String() string { func (*GetDraftHistoryRequest) ProtoMessage() {} func (x *GetDraftHistoryRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[169] + mi := &file_openshell_proto_msgTypes[177] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12351,7 +13041,7 @@ func (x *GetDraftHistoryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDraftHistoryRequest.ProtoReflect.Descriptor instead. func (*GetDraftHistoryRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{169} + return file_openshell_proto_rawDescGZIP(), []int{177} } func (x *GetDraftHistoryRequest) GetName() string { @@ -12385,7 +13075,7 @@ type DraftHistoryEntry struct { func (x *DraftHistoryEntry) Reset() { *x = DraftHistoryEntry{} - mi := &file_openshell_proto_msgTypes[170] + mi := &file_openshell_proto_msgTypes[178] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12397,7 +13087,7 @@ func (x *DraftHistoryEntry) String() string { func (*DraftHistoryEntry) ProtoMessage() {} func (x *DraftHistoryEntry) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[170] + mi := &file_openshell_proto_msgTypes[178] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12410,7 +13100,7 @@ func (x *DraftHistoryEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use DraftHistoryEntry.ProtoReflect.Descriptor instead. func (*DraftHistoryEntry) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{170} + return file_openshell_proto_rawDescGZIP(), []int{178} } func (x *DraftHistoryEntry) GetTimestampMs() int64 { @@ -12451,7 +13141,7 @@ type GetDraftHistoryResponse struct { func (x *GetDraftHistoryResponse) Reset() { *x = GetDraftHistoryResponse{} - mi := &file_openshell_proto_msgTypes[171] + mi := &file_openshell_proto_msgTypes[179] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12463,7 +13153,7 @@ func (x *GetDraftHistoryResponse) String() string { func (*GetDraftHistoryResponse) ProtoMessage() {} func (x *GetDraftHistoryResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[171] + mi := &file_openshell_proto_msgTypes[179] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12476,7 +13166,7 @@ func (x *GetDraftHistoryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDraftHistoryResponse.ProtoReflect.Descriptor instead. func (*GetDraftHistoryResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{171} + return file_openshell_proto_rawDescGZIP(), []int{179} } func (x *GetDraftHistoryResponse) GetEntries() []*DraftHistoryEntry { @@ -12505,7 +13195,7 @@ type PolicyRevisionPayload struct { func (x *PolicyRevisionPayload) Reset() { *x = PolicyRevisionPayload{} - mi := &file_openshell_proto_msgTypes[172] + mi := &file_openshell_proto_msgTypes[180] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12517,7 +13207,7 @@ func (x *PolicyRevisionPayload) String() string { func (*PolicyRevisionPayload) ProtoMessage() {} func (x *PolicyRevisionPayload) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[172] + mi := &file_openshell_proto_msgTypes[180] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12530,7 +13220,7 @@ func (x *PolicyRevisionPayload) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyRevisionPayload.ProtoReflect.Descriptor instead. func (*PolicyRevisionPayload) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{172} + return file_openshell_proto_rawDescGZIP(), []int{180} } func (x *PolicyRevisionPayload) GetPolicy() *sandboxv1.SandboxPolicy { @@ -12609,7 +13299,7 @@ type DraftChunkPayload struct { func (x *DraftChunkPayload) Reset() { *x = DraftChunkPayload{} - mi := &file_openshell_proto_msgTypes[173] + mi := &file_openshell_proto_msgTypes[181] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12621,7 +13311,7 @@ func (x *DraftChunkPayload) String() string { func (*DraftChunkPayload) ProtoMessage() {} func (x *DraftChunkPayload) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[173] + mi := &file_openshell_proto_msgTypes[181] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12634,7 +13324,7 @@ func (x *DraftChunkPayload) ProtoReflect() protoreflect.Message { // Deprecated: Use DraftChunkPayload.ProtoReflect.Descriptor instead. func (*DraftChunkPayload) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{173} + return file_openshell_proto_rawDescGZIP(), []int{181} } func (x *DraftChunkPayload) GetRuleName() string { @@ -12782,7 +13472,7 @@ type StoredPolicyRevision struct { func (x *StoredPolicyRevision) Reset() { *x = StoredPolicyRevision{} - mi := &file_openshell_proto_msgTypes[174] + mi := &file_openshell_proto_msgTypes[182] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12794,7 +13484,7 @@ func (x *StoredPolicyRevision) String() string { func (*StoredPolicyRevision) ProtoMessage() {} func (x *StoredPolicyRevision) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[174] + mi := &file_openshell_proto_msgTypes[182] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12807,7 +13497,7 @@ func (x *StoredPolicyRevision) ProtoReflect() protoreflect.Message { // Deprecated: Use StoredPolicyRevision.ProtoReflect.Descriptor instead. func (*StoredPolicyRevision) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{174} + return file_openshell_proto_rawDescGZIP(), []int{182} } func (x *StoredPolicyRevision) GetId() string { @@ -12916,7 +13606,7 @@ type StoredDraftChunk struct { func (x *StoredDraftChunk) Reset() { *x = StoredDraftChunk{} - mi := &file_openshell_proto_msgTypes[175] + mi := &file_openshell_proto_msgTypes[183] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12928,7 +13618,7 @@ func (x *StoredDraftChunk) String() string { func (*StoredDraftChunk) ProtoMessage() {} func (x *StoredDraftChunk) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[175] + mi := &file_openshell_proto_msgTypes[183] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12941,7 +13631,7 @@ func (x *StoredDraftChunk) ProtoReflect() protoreflect.Message { // Deprecated: Use StoredDraftChunk.ProtoReflect.Descriptor instead. func (*StoredDraftChunk) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{175} + return file_openshell_proto_rawDescGZIP(), []int{183} } func (x *StoredDraftChunk) GetId() string { @@ -13132,7 +13822,7 @@ type CreateWorkspaceRequest struct { func (x *CreateWorkspaceRequest) Reset() { *x = CreateWorkspaceRequest{} - mi := &file_openshell_proto_msgTypes[176] + mi := &file_openshell_proto_msgTypes[184] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13144,7 +13834,7 @@ func (x *CreateWorkspaceRequest) String() string { func (*CreateWorkspaceRequest) ProtoMessage() {} func (x *CreateWorkspaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[176] + mi := &file_openshell_proto_msgTypes[184] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13157,7 +13847,7 @@ func (x *CreateWorkspaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateWorkspaceRequest.ProtoReflect.Descriptor instead. func (*CreateWorkspaceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{176} + return file_openshell_proto_rawDescGZIP(), []int{184} } func (x *CreateWorkspaceRequest) GetName() string { @@ -13184,7 +13874,7 @@ type CreateWorkspaceResponse struct { func (x *CreateWorkspaceResponse) Reset() { *x = CreateWorkspaceResponse{} - mi := &file_openshell_proto_msgTypes[177] + mi := &file_openshell_proto_msgTypes[185] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13196,7 +13886,7 @@ func (x *CreateWorkspaceResponse) String() string { func (*CreateWorkspaceResponse) ProtoMessage() {} func (x *CreateWorkspaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[177] + mi := &file_openshell_proto_msgTypes[185] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13209,7 +13899,7 @@ func (x *CreateWorkspaceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateWorkspaceResponse.ProtoReflect.Descriptor instead. func (*CreateWorkspaceResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{177} + return file_openshell_proto_rawDescGZIP(), []int{185} } func (x *CreateWorkspaceResponse) GetWorkspace() *datamodelv1.Workspace { @@ -13230,7 +13920,7 @@ type GetWorkspaceRequest struct { func (x *GetWorkspaceRequest) Reset() { *x = GetWorkspaceRequest{} - mi := &file_openshell_proto_msgTypes[178] + mi := &file_openshell_proto_msgTypes[186] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13242,7 +13932,7 @@ func (x *GetWorkspaceRequest) String() string { func (*GetWorkspaceRequest) ProtoMessage() {} func (x *GetWorkspaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[178] + mi := &file_openshell_proto_msgTypes[186] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13255,7 +13945,7 @@ func (x *GetWorkspaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetWorkspaceRequest.ProtoReflect.Descriptor instead. func (*GetWorkspaceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{178} + return file_openshell_proto_rawDescGZIP(), []int{186} } func (x *GetWorkspaceRequest) GetName() string { @@ -13275,7 +13965,7 @@ type GetWorkspaceResponse struct { func (x *GetWorkspaceResponse) Reset() { *x = GetWorkspaceResponse{} - mi := &file_openshell_proto_msgTypes[179] + mi := &file_openshell_proto_msgTypes[187] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13287,7 +13977,7 @@ func (x *GetWorkspaceResponse) String() string { func (*GetWorkspaceResponse) ProtoMessage() {} func (x *GetWorkspaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[179] + mi := &file_openshell_proto_msgTypes[187] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13300,7 +13990,7 @@ func (x *GetWorkspaceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetWorkspaceResponse.ProtoReflect.Descriptor instead. func (*GetWorkspaceResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{179} + return file_openshell_proto_rawDescGZIP(), []int{187} } func (x *GetWorkspaceResponse) GetWorkspace() *datamodelv1.Workspace { @@ -13323,7 +14013,7 @@ type ListWorkspacesRequest struct { func (x *ListWorkspacesRequest) Reset() { *x = ListWorkspacesRequest{} - mi := &file_openshell_proto_msgTypes[180] + mi := &file_openshell_proto_msgTypes[188] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13335,7 +14025,7 @@ func (x *ListWorkspacesRequest) String() string { func (*ListWorkspacesRequest) ProtoMessage() {} func (x *ListWorkspacesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[180] + mi := &file_openshell_proto_msgTypes[188] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13348,7 +14038,7 @@ func (x *ListWorkspacesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkspacesRequest.ProtoReflect.Descriptor instead. func (*ListWorkspacesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{180} + return file_openshell_proto_rawDescGZIP(), []int{188} } func (x *ListWorkspacesRequest) GetLimit() uint32 { @@ -13382,7 +14072,7 @@ type ListWorkspacesResponse struct { func (x *ListWorkspacesResponse) Reset() { *x = ListWorkspacesResponse{} - mi := &file_openshell_proto_msgTypes[181] + mi := &file_openshell_proto_msgTypes[189] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13394,7 +14084,7 @@ func (x *ListWorkspacesResponse) String() string { func (*ListWorkspacesResponse) ProtoMessage() {} func (x *ListWorkspacesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[181] + mi := &file_openshell_proto_msgTypes[189] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13407,7 +14097,7 @@ func (x *ListWorkspacesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkspacesResponse.ProtoReflect.Descriptor instead. func (*ListWorkspacesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{181} + return file_openshell_proto_rawDescGZIP(), []int{189} } func (x *ListWorkspacesResponse) GetWorkspaces() []*datamodelv1.Workspace { @@ -13428,7 +14118,7 @@ type DeleteWorkspaceRequest struct { func (x *DeleteWorkspaceRequest) Reset() { *x = DeleteWorkspaceRequest{} - mi := &file_openshell_proto_msgTypes[182] + mi := &file_openshell_proto_msgTypes[190] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13440,7 +14130,7 @@ func (x *DeleteWorkspaceRequest) String() string { func (*DeleteWorkspaceRequest) ProtoMessage() {} func (x *DeleteWorkspaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[182] + mi := &file_openshell_proto_msgTypes[190] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13453,7 +14143,7 @@ func (x *DeleteWorkspaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteWorkspaceRequest.ProtoReflect.Descriptor instead. func (*DeleteWorkspaceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{182} + return file_openshell_proto_rawDescGZIP(), []int{190} } func (x *DeleteWorkspaceRequest) GetName() string { @@ -13473,7 +14163,7 @@ type DeleteWorkspaceResponse struct { func (x *DeleteWorkspaceResponse) Reset() { *x = DeleteWorkspaceResponse{} - mi := &file_openshell_proto_msgTypes[183] + mi := &file_openshell_proto_msgTypes[191] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13485,7 +14175,7 @@ func (x *DeleteWorkspaceResponse) String() string { func (*DeleteWorkspaceResponse) ProtoMessage() {} func (x *DeleteWorkspaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[183] + mi := &file_openshell_proto_msgTypes[191] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13498,7 +14188,7 @@ func (x *DeleteWorkspaceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteWorkspaceResponse.ProtoReflect.Descriptor instead. func (*DeleteWorkspaceResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{183} + return file_openshell_proto_rawDescGZIP(), []int{191} } func (x *DeleteWorkspaceResponse) GetDeleted() bool { @@ -13522,7 +14212,7 @@ type WorkspaceMember struct { func (x *WorkspaceMember) Reset() { *x = WorkspaceMember{} - mi := &file_openshell_proto_msgTypes[184] + mi := &file_openshell_proto_msgTypes[192] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13534,7 +14224,7 @@ func (x *WorkspaceMember) String() string { func (*WorkspaceMember) ProtoMessage() {} func (x *WorkspaceMember) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[184] + mi := &file_openshell_proto_msgTypes[192] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13547,7 +14237,7 @@ func (x *WorkspaceMember) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkspaceMember.ProtoReflect.Descriptor instead. func (*WorkspaceMember) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{184} + return file_openshell_proto_rawDescGZIP(), []int{192} } func (x *WorkspaceMember) GetMetadata() *datamodelv1.ObjectMeta { @@ -13586,7 +14276,7 @@ type AddWorkspaceMemberRequest struct { func (x *AddWorkspaceMemberRequest) Reset() { *x = AddWorkspaceMemberRequest{} - mi := &file_openshell_proto_msgTypes[185] + mi := &file_openshell_proto_msgTypes[193] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13598,7 +14288,7 @@ func (x *AddWorkspaceMemberRequest) String() string { func (*AddWorkspaceMemberRequest) ProtoMessage() {} func (x *AddWorkspaceMemberRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[185] + mi := &file_openshell_proto_msgTypes[193] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13611,7 +14301,7 @@ func (x *AddWorkspaceMemberRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AddWorkspaceMemberRequest.ProtoReflect.Descriptor instead. func (*AddWorkspaceMemberRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{185} + return file_openshell_proto_rawDescGZIP(), []int{193} } func (x *AddWorkspaceMemberRequest) GetWorkspace() string { @@ -13645,7 +14335,7 @@ type AddWorkspaceMemberResponse struct { func (x *AddWorkspaceMemberResponse) Reset() { *x = AddWorkspaceMemberResponse{} - mi := &file_openshell_proto_msgTypes[186] + mi := &file_openshell_proto_msgTypes[194] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13657,7 +14347,7 @@ func (x *AddWorkspaceMemberResponse) String() string { func (*AddWorkspaceMemberResponse) ProtoMessage() {} func (x *AddWorkspaceMemberResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[186] + mi := &file_openshell_proto_msgTypes[194] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13670,7 +14360,7 @@ func (x *AddWorkspaceMemberResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AddWorkspaceMemberResponse.ProtoReflect.Descriptor instead. func (*AddWorkspaceMemberResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{186} + return file_openshell_proto_rawDescGZIP(), []int{194} } func (x *AddWorkspaceMemberResponse) GetMember() *WorkspaceMember { @@ -13693,7 +14383,7 @@ type RemoveWorkspaceMemberRequest struct { func (x *RemoveWorkspaceMemberRequest) Reset() { *x = RemoveWorkspaceMemberRequest{} - mi := &file_openshell_proto_msgTypes[187] + mi := &file_openshell_proto_msgTypes[195] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13705,7 +14395,7 @@ func (x *RemoveWorkspaceMemberRequest) String() string { func (*RemoveWorkspaceMemberRequest) ProtoMessage() {} func (x *RemoveWorkspaceMemberRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[187] + mi := &file_openshell_proto_msgTypes[195] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13718,7 +14408,7 @@ func (x *RemoveWorkspaceMemberRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveWorkspaceMemberRequest.ProtoReflect.Descriptor instead. func (*RemoveWorkspaceMemberRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{187} + return file_openshell_proto_rawDescGZIP(), []int{195} } func (x *RemoveWorkspaceMemberRequest) GetWorkspace() string { @@ -13745,7 +14435,7 @@ type RemoveWorkspaceMemberResponse struct { func (x *RemoveWorkspaceMemberResponse) Reset() { *x = RemoveWorkspaceMemberResponse{} - mi := &file_openshell_proto_msgTypes[188] + mi := &file_openshell_proto_msgTypes[196] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13757,7 +14447,7 @@ func (x *RemoveWorkspaceMemberResponse) String() string { func (*RemoveWorkspaceMemberResponse) ProtoMessage() {} func (x *RemoveWorkspaceMemberResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[188] + mi := &file_openshell_proto_msgTypes[196] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13770,7 +14460,7 @@ func (x *RemoveWorkspaceMemberResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveWorkspaceMemberResponse.ProtoReflect.Descriptor instead. func (*RemoveWorkspaceMemberResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{188} + return file_openshell_proto_rawDescGZIP(), []int{196} } func (x *RemoveWorkspaceMemberResponse) GetRemoved() bool { @@ -13793,7 +14483,7 @@ type ListWorkspaceMembersRequest struct { func (x *ListWorkspaceMembersRequest) Reset() { *x = ListWorkspaceMembersRequest{} - mi := &file_openshell_proto_msgTypes[189] + mi := &file_openshell_proto_msgTypes[197] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13805,7 +14495,7 @@ func (x *ListWorkspaceMembersRequest) String() string { func (*ListWorkspaceMembersRequest) ProtoMessage() {} func (x *ListWorkspaceMembersRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[189] + mi := &file_openshell_proto_msgTypes[197] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13818,7 +14508,7 @@ func (x *ListWorkspaceMembersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkspaceMembersRequest.ProtoReflect.Descriptor instead. func (*ListWorkspaceMembersRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{189} + return file_openshell_proto_rawDescGZIP(), []int{197} } func (x *ListWorkspaceMembersRequest) GetWorkspace() string { @@ -13852,7 +14542,7 @@ type ListWorkspaceMembersResponse struct { func (x *ListWorkspaceMembersResponse) Reset() { *x = ListWorkspaceMembersResponse{} - mi := &file_openshell_proto_msgTypes[190] + mi := &file_openshell_proto_msgTypes[198] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13864,7 +14554,7 @@ func (x *ListWorkspaceMembersResponse) String() string { func (*ListWorkspaceMembersResponse) ProtoMessage() {} func (x *ListWorkspaceMembersResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[190] + mi := &file_openshell_proto_msgTypes[198] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13877,7 +14567,7 @@ func (x *ListWorkspaceMembersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkspaceMembersResponse.ProtoReflect.Descriptor instead. func (*ListWorkspaceMembersResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{190} + return file_openshell_proto_rawDescGZIP(), []int{198} } func (x *ListWorkspaceMembersResponse) GetMembers() []*WorkspaceMember { @@ -13905,7 +14595,7 @@ type ExtensionServiceCredential struct { func (x *ExtensionServiceCredential) Reset() { *x = ExtensionServiceCredential{} - mi := &file_openshell_proto_msgTypes[191] + mi := &file_openshell_proto_msgTypes[199] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13917,7 +14607,7 @@ func (x *ExtensionServiceCredential) String() string { func (*ExtensionServiceCredential) ProtoMessage() {} func (x *ExtensionServiceCredential) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[191] + mi := &file_openshell_proto_msgTypes[199] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13930,7 +14620,7 @@ func (x *ExtensionServiceCredential) ProtoReflect() protoreflect.Message { // Deprecated: Use ExtensionServiceCredential.ProtoReflect.Descriptor instead. func (*ExtensionServiceCredential) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{191} + return file_openshell_proto_rawDescGZIP(), []int{199} } func (x *ExtensionServiceCredential) GetServiceName() string { @@ -13958,7 +14648,7 @@ var File_openshell_proto protoreflect.FileDescriptor const file_openshell_proto_rawDesc = "" + "\n" + - "\x0fopenshell.proto\x12\fopenshell.v1\x1a\x0fdatamodel.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\roptions.proto\x1a\rsandbox.proto\"\x1a\n" + + "\x0fopenshell.proto\x12\fopenshell.v1\x1a\x0fdatamodel.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x0finference.proto\x1a\roptions.proto\x1a\rsandbox.proto\"\x1a\n" + "\x18IssueSandboxTokenRequest\"[\n" + "\x19IssueSandboxTokenResponse\x12\x1a\n" + "\x05token\x18\x01 \x01(\tB\x04\x88\xb5\x18\x01R\x05token\x12\"\n" + @@ -14526,13 +15216,13 @@ const file_openshell_proto_rawDesc = "" + "\x17StaticCredentialBinding\x12K\n" + "\tendpoints\x18\x01 \x03(\v2-.openshell.v1.StaticCredentialEndpointBindingR\tendpoints\x12/\n" + "\x13credential_identity\x18\x02 \x01(\tR\x12credentialIdentity\x12<\n" + - "\x1aworkload_credential_handle\x18\x03 \x01(\tR\x18workloadCredentialHandle\"\x90\b\n" + - "%GetSandboxProviderEnvironmentResponse\x12l\n" + - "\venvironment\x18\x01 \x03(\v2D.openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntryB\x04\x88\xb5\x18\x01R\venvironment\x122\n" + - "\x15provider_env_revision\x18\x02 \x01(\x04R\x13providerEnvRevision\x12\x87\x01\n" + - "\x18credential_expires_at_ms\x18\x03 \x03(\v2N.openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpiresAtMsEntryR\x15credentialExpiresAtMs\x12|\n" + - "\x13dynamic_credentials\x18\x04 \x03(\v2K.openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntryR\x12dynamicCredentials\x12\x8f\x01\n" + - "\x1astatic_credential_bindings\x18\x05 \x03(\v2Q.openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntryR\x18staticCredentialBindings\x12=\n" + + "\x1aworkload_credential_handle\x18\x03 \x01(\tR\x18workloadCredentialHandle\"\xdd\a\n" + + "\x1bProviderEnvironmentSnapshot\x12b\n" + + "\venvironment\x18\x01 \x03(\v2:.openshell.v1.ProviderEnvironmentSnapshot.EnvironmentEntryB\x04\x88\xb5\x18\x01R\venvironment\x122\n" + + "\x15provider_env_revision\x18\x02 \x01(\x04R\x13providerEnvRevision\x12}\n" + + "\x18credential_expires_at_ms\x18\x03 \x03(\v2D.openshell.v1.ProviderEnvironmentSnapshot.CredentialExpiresAtMsEntryR\x15credentialExpiresAtMs\x12r\n" + + "\x13dynamic_credentials\x18\x04 \x03(\v2A.openshell.v1.ProviderEnvironmentSnapshot.DynamicCredentialsEntryR\x12dynamicCredentials\x12\x85\x01\n" + + "\x1astatic_credential_bindings\x18\x05 \x03(\v2G.openshell.v1.ProviderEnvironmentSnapshot.StaticCredentialBindingsEntryR\x18staticCredentialBindings\x12=\n" + "\x1bnon_secret_environment_keys\x18\x06 \x03(\tR\x18nonSecretEnvironmentKeys\x1a>\n" + "\x10EnvironmentEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + @@ -14671,14 +15361,17 @@ const file_openshell_proto_rawDesc = "" + "\x17PushSandboxLogsResponse\"m\n" + "\x16GetSandboxLogsResponse\x120\n" + "\x04logs\x18\x01 \x03(\v2\x1c.openshell.v1.SandboxLogLineR\x04logs\x12!\n" + - "\fbuffer_total\x18\x02 \x01(\rR\vbufferTotal\"\xa2\x02\n" + + "\fbuffer_total\x18\x02 \x01(\rR\vbufferTotal\"\x97\x04\n" + "\x11SupervisorMessage\x125\n" + "\x05hello\x18\x01 \x01(\v2\x1d.openshell.v1.SupervisorHelloH\x00R\x05hello\x12A\n" + "\theartbeat\x18\x02 \x01(\v2!.openshell.v1.SupervisorHeartbeatH\x00R\theartbeat\x12K\n" + "\x11relay_open_result\x18\x03 \x01(\v2\x1d.openshell.v1.RelayOpenResultH\x00R\x0frelayOpenResult\x12;\n" + "\vrelay_close\x18\x04 \x01(\v2\x18.openshell.v1.RelayCloseH\x00R\n" + - "relayCloseB\t\n" + - "\apayload\"\xea\x02\n" + + "relayClose\x12P\n" + + "\x10bootstrap_result\x18\x05 \x01(\v2#.openshell.v1.ConfigBootstrapResultH\x00R\x0fbootstrapResult\x12T\n" + + "\x14config_update_result\x18\x06 \x01(\v2 .openshell.v1.ConfigUpdateResultH\x00R\x12configUpdateResult\x12K\n" + + "\rruntime_ready\x18\a \x01(\v2$.openshell.v1.SupervisorRuntimeReadyH\x00R\fruntimeReadyB\t\n" + + "\apayload\"\xad\x03\n" + "\x0eGatewayMessage\x12J\n" + "\x10session_accepted\x18\x01 \x01(\v2\x1d.openshell.v1.SessionAcceptedH\x00R\x0fsessionAccepted\x12J\n" + "\x10session_rejected\x18\x02 \x01(\v2\x1d.openshell.v1.SessionRejectedH\x00R\x0fsessionRejected\x12>\n" + @@ -14686,17 +15379,52 @@ const file_openshell_proto_rawDesc = "" + "\n" + "relay_open\x18\x04 \x01(\v2\x17.openshell.v1.RelayOpenH\x00R\trelayOpen\x12;\n" + "\vrelay_close\x18\x05 \x01(\v2\x18.openshell.v1.RelayCloseH\x00R\n" + - "relayCloseB\t\n" + + "relayClose\x12A\n" + + "\rconfig_update\x18\x06 \x01(\v2\x1a.openshell.v1.ConfigUpdateH\x00R\fconfigUpdateB\t\n" + "\apayload\"Q\n" + "\x0fSupervisorHello\x12\x1d\n" + "\n" + "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x1f\n" + "\vinstance_id\x18\x02 \x01(\tR\n" + - "instanceId\"h\n" + + "instanceId\"\xa5\x01\n" + "\x0fSessionAccepted\x12\x1d\n" + "\n" + "session_id\x18\x01 \x01(\tR\tsessionId\x126\n" + - "\x17heartbeat_interval_secs\x18\x02 \x01(\rR\x15heartbeatIntervalSecs\")\n" + + "\x17heartbeat_interval_secs\x18\x02 \x01(\rR\x15heartbeatIntervalSecs\x12;\n" + + "\tbootstrap\x18\x03 \x01(\v2\x1d.openshell.v1.ConfigBootstrapR\tbootstrap\"\x9f\x02\n" + + "\x0fConfigBootstrap\x12R\n" + + "\x0esandbox_config\x18\x01 \x01(\v2+.openshell.sandbox.v1.SandboxConfigSnapshotR\rsandboxConfig\x12\\\n" + + "\x14provider_environment\x18\x02 \x01(\v2).openshell.v1.ProviderEnvironmentSnapshotR\x13providerEnvironment\x12Z\n" + + "\x10inference_bundle\x18\x03 \x01(\v2/.openshell.inference.v1.InferenceBundleSnapshotR\x0finferenceBundle\"\x82\x03\n" + + "\x15ConfigBootstrapResult\x12;\n" + + "\x06status\x18\x01 \x01(\x0e2#.openshell.v1.ConfigBootstrapStatusR\x06status\x12\x14\n" + + "\x05error\x18\x02 \x01(\tR\x05error\x12V\n" + + "\x16sandbox_config_outcome\x18\x03 \x01(\x0e2 .openshell.v1.ConfigApplyOutcomeR\x14sandboxConfigOutcome\x12b\n" + + "\x1cprovider_environment_outcome\x18\x04 \x01(\x0e2 .openshell.v1.ConfigApplyOutcomeR\x1aproviderEnvironmentOutcome\x12Z\n" + + "\x18inference_bundle_outcome\x18\x05 \x01(\x0e2 .openshell.v1.ConfigApplyOutcomeR\x16inferenceBundleOutcome\"\xe5\x02\n" + + "\fConfigUpdate\x12\x1d\n" + + "\n" + + "request_id\x18\x01 \x01(\tR\trequestId\x12J\n" + + "\x0esandbox_config\x18\x02 \x01(\v2!.openshell.v1.SandboxConfigUpdateH\x00R\rsandboxConfig\x12\\\n" + + "\x14provider_environment\x18\x03 \x01(\v2'.openshell.v1.ProviderEnvironmentUpdateH\x00R\x13providerEnvironment\x12P\n" + + "\x10inference_bundle\x18\x04 \x01(\v2#.openshell.v1.InferenceBundleUpdateH\x00R\x0finferenceBundle\x12-\n" + + "\x12component_sequence\x18\x05 \x01(\x04R\x11componentSequenceB\v\n" + + "\tcomponent\"^\n" + + "\x13SandboxConfigUpdate\x12G\n" + + "\bsnapshot\x18\x01 \x01(\v2+.openshell.sandbox.v1.SandboxConfigSnapshotR\bsnapshot\"b\n" + + "\x19ProviderEnvironmentUpdate\x12E\n" + + "\bsnapshot\x18\x01 \x01(\v2).openshell.v1.ProviderEnvironmentSnapshotR\bsnapshot\"d\n" + + "\x15InferenceBundleUpdate\x12K\n" + + "\bsnapshot\x18\x01 \x01(\v2/.openshell.inference.v1.InferenceBundleSnapshotR\bsnapshot\"\xb4\x01\n" + + "\x12ConfigUpdateResult\x12\x1d\n" + + "\n" + + "request_id\x18\x01 \x01(\tR\trequestId\x12-\n" + + "\x12component_sequence\x18\x02 \x01(\x04R\x11componentSequence\x12:\n" + + "\aoutcome\x18\x03 \x01(\x0e2 .openshell.v1.ConfigApplyOutcomeR\aoutcome\x12\x14\n" + + "\x05error\x18\x04 \x01(\tR\x05error\"9\n" + + "\x16SupervisorRuntimeReady\x12\x1f\n" + + "\vinstance_id\x18\x01 \x01(\tR\n" + + "instanceId\")\n" + "\x0fSessionRejected\x12\x16\n" + "\x06reason\x18\x01 \x01(\tR\x06reason\"\x15\n" + "\x13SupervisorHeartbeat\"\x12\n" + @@ -15066,7 +15794,20 @@ const file_openshell_proto_rawDesc = "" + "\x15POLICY_STATUS_PENDING\x10\x01\x12\x18\n" + "\x14POLICY_STATUS_LOADED\x10\x02\x12\x18\n" + "\x14POLICY_STATUS_FAILED\x10\x03\x12\x1c\n" + - "\x18POLICY_STATUS_SUPERSEDED\x10\x04*\x86\x01\n" + + "\x18POLICY_STATUS_SUPERSEDED\x10\x04*\x87\x01\n" + + "\x15ConfigBootstrapStatus\x12'\n" + + "#CONFIG_BOOTSTRAP_STATUS_UNSPECIFIED\x10\x00\x12!\n" + + "\x1dCONFIG_BOOTSTRAP_STATUS_READY\x10\x01\x12\"\n" + + "\x1eCONFIG_BOOTSTRAP_STATUS_FAILED\x10\x02*\xcc\x02\n" + + "\x12ConfigApplyOutcome\x12$\n" + + " CONFIG_APPLY_OUTCOME_UNSPECIFIED\x10\x00\x12 \n" + + "\x1cCONFIG_APPLY_OUTCOME_APPLIED\x10\x01\x12*\n" + + "&CONFIG_APPLY_OUTCOME_IGNORED_DUPLICATE\x10\x02\x12&\n" + + "\"CONFIG_APPLY_OUTCOME_IGNORED_STALE\x10\x03\x120\n" + + ",CONFIG_APPLY_OUTCOME_RETAINED_LOCAL_OVERRIDE\x10\x04\x12!\n" + + "\x1dCONFIG_APPLY_OUTCOME_DEGRADED\x10\x05\x12\x1f\n" + + "\x1bCONFIG_APPLY_OUTCOME_FAILED\x10\x06\x12$\n" + + " CONFIG_APPLY_OUTCOME_UNSUPPORTED\x10\a*\x86\x01\n" + "\rServiceStatus\x12\x1e\n" + "\x1aSERVICE_STATUS_UNSPECIFIED\x10\x00\x12\x1a\n" + "\x16SERVICE_STATUS_HEALTHY\x10\x01\x12\x1b\n" + @@ -15081,7 +15822,7 @@ const file_openshell_proto_rawDesc = "" + "1PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_RETRY\x10\x01\x12;\n" + "7PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE\x10\x02\x12A\n" + "=PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_FIX_CONFIGURATION\x10\x03\x12;\n" + - "7PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_INVESTIGATE\x10\x042\xacF\n" + + "7PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_INVESTIGATE\x10\x042\x8fE\n" + "\tOpenShell\x12Z\n" + "\x06Health\x12\x1b.openshell.v1.HealthRequest\x1a\x1c.openshell.v1.HealthResponse\"\x15\x82\xb5\x18\x11\n" + "\x0funauthenticated\x12i\n" + @@ -15157,8 +15898,8 @@ const file_openshell_proto_rawDesc = "" + "\x0eDeleteProvider\x12#.openshell.v1.DeleteProviderRequest\x1a$.openshell.v1.DeleteProviderResponse\"#\x82\xb5\x18\x1f\n" + "\x06bearer\x12\x05admin\"\x0eprovider:write\x12\x95\x01\n" + "\x15DeleteProviderProfile\x12*.openshell.v1.DeleteProviderProfileRequest\x1a+.openshell.v1.DeleteProviderProfileResponse\"#\x82\xb5\x18\x1f\n" + - "\x06bearer\x12\x05admin\"\x0eprovider:write\x12\x90\x01\n" + - "\x10GetSandboxConfig\x12-.openshell.sandbox.v1.GetSandboxConfigRequest\x1a..openshell.sandbox.v1.GetSandboxConfigResponse\"\x1d\x82\xb5\x18\x19\n" + + "\x06bearer\x12\x05admin\"\x0eprovider:write\x12\x8d\x01\n" + + "\x10GetSandboxConfig\x12-.openshell.sandbox.v1.GetSandboxConfigRequest\x1a+.openshell.sandbox.v1.SandboxConfigSnapshot\"\x1d\x82\xb5\x18\x19\n" + "\x04dual\x12\x04user\"\vconfig:read\x12\x8c\x01\n" + "\x10GetGatewayConfig\x12-.openshell.sandbox.v1.GetGatewayConfigRequest\x1a..openshell.sandbox.v1.GetGatewayConfigResponse\"\x19\x82\xb5\x18\x15\n" + "\x06bearer\"\vconfig:read\x12v\n" + @@ -15169,8 +15910,6 @@ const file_openshell_proto_rawDesc = "" + "\x13ListSandboxPolicies\x12(.openshell.v1.ListSandboxPoliciesRequest\x1a).openshell.v1.ListSandboxPoliciesResponse\" \x82\xb5\x18\x1c\n" + "\x06bearer\x12\x04user\"\fsandbox:read\x12v\n" + "\x12ReportPolicyStatus\x12'.openshell.v1.ReportPolicyStatusRequest\x1a(.openshell.v1.ReportPolicyStatusResponse\"\r\x82\xb5\x18\t\n" + - "\asandbox\x12\x97\x01\n" + - "\x1dGetSandboxProviderEnvironment\x122.openshell.v1.GetSandboxProviderEnvironmentRequest\x1a3.openshell.v1.GetSandboxProviderEnvironmentResponse\"\r\x82\xb5\x18\t\n" + "\asandbox\x12\x94\x01\n" + "\x1cExchangeProviderSubjectToken\x121.openshell.v1.ExchangeProviderSubjectTokenRequest\x1a2.openshell.v1.ExchangeProviderSubjectTokenResponse\"\r\x82\xb5\x18\t\n" + "\asandbox\x12}\n" + @@ -15235,559 +15974,587 @@ func file_openshell_proto_rawDescGZIP() []byte { return file_openshell_proto_rawDescData } -var file_openshell_proto_enumTypes = make([]protoimpl.EnumInfo, 8) -var file_openshell_proto_msgTypes = make([]protoimpl.MessageInfo, 217) +var file_openshell_proto_enumTypes = make([]protoimpl.EnumInfo, 10) +var file_openshell_proto_msgTypes = make([]protoimpl.MessageInfo, 225) var file_openshell_proto_goTypes = []any{ (SandboxPhase)(0), // 0: openshell.v1.SandboxPhase (ProviderCredentialTokenGrantType)(0), // 1: openshell.v1.ProviderCredentialTokenGrantType (ProviderCredentialRefreshStrategy)(0), // 2: openshell.v1.ProviderCredentialRefreshStrategy (ProviderProfileCategory)(0), // 3: openshell.v1.ProviderProfileCategory (PolicyStatus)(0), // 4: openshell.v1.PolicyStatus - (ServiceStatus)(0), // 5: openshell.v1.ServiceStatus - (WorkspaceRole)(0), // 6: openshell.v1.WorkspaceRole - (ProviderCredentialRefreshRecoveryAction)(0), // 7: openshell.v1.ProviderCredentialRefreshRecoveryAction - (*IssueSandboxTokenRequest)(nil), // 8: openshell.v1.IssueSandboxTokenRequest - (*IssueSandboxTokenResponse)(nil), // 9: openshell.v1.IssueSandboxTokenResponse - (*RefreshSandboxTokenRequest)(nil), // 10: openshell.v1.RefreshSandboxTokenRequest - (*RefreshSandboxTokenResponse)(nil), // 11: openshell.v1.RefreshSandboxTokenResponse - (*HealthRequest)(nil), // 12: openshell.v1.HealthRequest - (*HealthResponse)(nil), // 13: openshell.v1.HealthResponse - (*GetCurrentUserRequest)(nil), // 14: openshell.v1.GetCurrentUserRequest - (*GetCurrentUserResponse)(nil), // 15: openshell.v1.GetCurrentUserResponse - (*GetGatewayInfoRequest)(nil), // 16: openshell.v1.GetGatewayInfoRequest - (*GetGatewayInfoResponse)(nil), // 17: openshell.v1.GetGatewayInfoResponse - (*ComputeDriverInfo)(nil), // 18: openshell.v1.ComputeDriverInfo - (*ComputeDriverCapabilities)(nil), // 19: openshell.v1.ComputeDriverCapabilities - (*Sandbox)(nil), // 20: openshell.v1.Sandbox - (*SandboxSpec)(nil), // 21: openshell.v1.SandboxSpec - (*ResourceRequirements)(nil), // 22: openshell.v1.ResourceRequirements - (*GpuResourceRequirements)(nil), // 23: openshell.v1.GpuResourceRequirements - (*SandboxTemplate)(nil), // 24: openshell.v1.SandboxTemplate - (*SandboxStatus)(nil), // 25: openshell.v1.SandboxStatus - (*SandboxCondition)(nil), // 26: openshell.v1.SandboxCondition - (*PlatformEvent)(nil), // 27: openshell.v1.PlatformEvent - (*CreateSandboxRequest)(nil), // 28: openshell.v1.CreateSandboxRequest - (*GetSandboxRequest)(nil), // 29: openshell.v1.GetSandboxRequest - (*ListSandboxesRequest)(nil), // 30: openshell.v1.ListSandboxesRequest - (*ListSandboxProvidersRequest)(nil), // 31: openshell.v1.ListSandboxProvidersRequest - (*AttachSandboxProviderRequest)(nil), // 32: openshell.v1.AttachSandboxProviderRequest - (*DetachSandboxProviderRequest)(nil), // 33: openshell.v1.DetachSandboxProviderRequest - (*DeleteSandboxRequest)(nil), // 34: openshell.v1.DeleteSandboxRequest - (*StopSandboxRequest)(nil), // 35: openshell.v1.StopSandboxRequest - (*StartSandboxRequest)(nil), // 36: openshell.v1.StartSandboxRequest - (*SandboxResponse)(nil), // 37: openshell.v1.SandboxResponse - (*ListSandboxesResponse)(nil), // 38: openshell.v1.ListSandboxesResponse - (*ListSandboxProvidersResponse)(nil), // 39: openshell.v1.ListSandboxProvidersResponse - (*AttachSandboxProviderResponse)(nil), // 40: openshell.v1.AttachSandboxProviderResponse - (*DetachSandboxProviderResponse)(nil), // 41: openshell.v1.DetachSandboxProviderResponse - (*DeleteSandboxResponse)(nil), // 42: openshell.v1.DeleteSandboxResponse - (*CreateSshSessionRequest)(nil), // 43: openshell.v1.CreateSshSessionRequest - (*CreateSshSessionResponse)(nil), // 44: openshell.v1.CreateSshSessionResponse - (*ExposeServiceRequest)(nil), // 45: openshell.v1.ExposeServiceRequest - (*GetServiceRequest)(nil), // 46: openshell.v1.GetServiceRequest - (*ListServicesRequest)(nil), // 47: openshell.v1.ListServicesRequest - (*ListServicesResponse)(nil), // 48: openshell.v1.ListServicesResponse - (*DeleteServiceRequest)(nil), // 49: openshell.v1.DeleteServiceRequest - (*DeleteServiceResponse)(nil), // 50: openshell.v1.DeleteServiceResponse - (*ServiceEndpoint)(nil), // 51: openshell.v1.ServiceEndpoint - (*ServiceEndpointResponse)(nil), // 52: openshell.v1.ServiceEndpointResponse - (*RevokeSshSessionRequest)(nil), // 53: openshell.v1.RevokeSshSessionRequest - (*RevokeSshSessionResponse)(nil), // 54: openshell.v1.RevokeSshSessionResponse - (*ExecSandboxRequest)(nil), // 55: openshell.v1.ExecSandboxRequest - (*ExecSandboxStdout)(nil), // 56: openshell.v1.ExecSandboxStdout - (*ExecSandboxStderr)(nil), // 57: openshell.v1.ExecSandboxStderr - (*ExecSandboxExit)(nil), // 58: openshell.v1.ExecSandboxExit - (*ExecSandboxEvent)(nil), // 59: openshell.v1.ExecSandboxEvent - (*TcpForwardInit)(nil), // 60: openshell.v1.TcpForwardInit - (*TcpForwardFrame)(nil), // 61: openshell.v1.TcpForwardFrame - (*ExecSandboxInput)(nil), // 62: openshell.v1.ExecSandboxInput - (*ExecSandboxWindowResize)(nil), // 63: openshell.v1.ExecSandboxWindowResize - (*SshSession)(nil), // 64: openshell.v1.SshSession - (*WatchSandboxRequest)(nil), // 65: openshell.v1.WatchSandboxRequest - (*SandboxStreamEvent)(nil), // 66: openshell.v1.SandboxStreamEvent - (*SandboxLogLine)(nil), // 67: openshell.v1.SandboxLogLine - (*SandboxStreamWarning)(nil), // 68: openshell.v1.SandboxStreamWarning - (*CreateProviderRequest)(nil), // 69: openshell.v1.CreateProviderRequest - (*GetProviderRequest)(nil), // 70: openshell.v1.GetProviderRequest - (*ListProvidersRequest)(nil), // 71: openshell.v1.ListProvidersRequest - (*UpdateProviderRequest)(nil), // 72: openshell.v1.UpdateProviderRequest - (*DeleteProviderRequest)(nil), // 73: openshell.v1.DeleteProviderRequest - (*ProviderResponse)(nil), // 74: openshell.v1.ProviderResponse - (*ListProvidersResponse)(nil), // 75: openshell.v1.ListProvidersResponse - (*ListProviderProfilesRequest)(nil), // 76: openshell.v1.ListProviderProfilesRequest - (*GetProviderProfileRequest)(nil), // 77: openshell.v1.GetProviderProfileRequest - (*ProviderProfileImportItem)(nil), // 78: openshell.v1.ProviderProfileImportItem - (*ProviderProfileDiagnostic)(nil), // 79: openshell.v1.ProviderProfileDiagnostic - (*ProviderCredentialTokenGrantAudienceOverride)(nil), // 80: openshell.v1.ProviderCredentialTokenGrantAudienceOverride - (*ProviderCredentialTokenGrantSubjectToken)(nil), // 81: openshell.v1.ProviderCredentialTokenGrantSubjectToken - (*ProviderCredentialTokenGrant)(nil), // 82: openshell.v1.ProviderCredentialTokenGrant - (*ProviderProfileCredential)(nil), // 83: openshell.v1.ProviderProfileCredential - (*ProviderCredentialRefreshMaterial)(nil), // 84: openshell.v1.ProviderCredentialRefreshMaterial - (*ProviderCredentialRefreshOutput)(nil), // 85: openshell.v1.ProviderCredentialRefreshOutput - (*ProviderCredentialRefresh)(nil), // 86: openshell.v1.ProviderCredentialRefresh - (*ProviderCredentialRefreshStatus)(nil), // 87: openshell.v1.ProviderCredentialRefreshStatus - (*ProviderProfileDiscovery)(nil), // 88: openshell.v1.ProviderProfileDiscovery - (*StoredProviderCredentialRefreshState)(nil), // 89: openshell.v1.StoredProviderCredentialRefreshState - (*StoredRefreshMaterialDeletion)(nil), // 90: openshell.v1.StoredRefreshMaterialDeletion - (*GetProviderRefreshStatusRequest)(nil), // 91: openshell.v1.GetProviderRefreshStatusRequest - (*GetProviderRefreshStatusResponse)(nil), // 92: openshell.v1.GetProviderRefreshStatusResponse - (*ConfigureProviderRefreshRequest)(nil), // 93: openshell.v1.ConfigureProviderRefreshRequest - (*ConfigureProviderRefreshResponse)(nil), // 94: openshell.v1.ConfigureProviderRefreshResponse - (*RotateProviderCredentialRequest)(nil), // 95: openshell.v1.RotateProviderCredentialRequest - (*RotateProviderCredentialResponse)(nil), // 96: openshell.v1.RotateProviderCredentialResponse - (*DeleteProviderRefreshRequest)(nil), // 97: openshell.v1.DeleteProviderRefreshRequest - (*DeleteProviderRefreshResponse)(nil), // 98: openshell.v1.DeleteProviderRefreshResponse - (*ProviderProfile)(nil), // 99: openshell.v1.ProviderProfile - (*StoredProviderProfile)(nil), // 100: openshell.v1.StoredProviderProfile - (*ProviderProfileResponse)(nil), // 101: openshell.v1.ProviderProfileResponse - (*ListProviderProfilesResponse)(nil), // 102: openshell.v1.ListProviderProfilesResponse - (*ImportProviderProfilesRequest)(nil), // 103: openshell.v1.ImportProviderProfilesRequest - (*ImportProviderProfilesResponse)(nil), // 104: openshell.v1.ImportProviderProfilesResponse - (*UpdateProviderProfilesRequest)(nil), // 105: openshell.v1.UpdateProviderProfilesRequest - (*UpdateProviderProfilesResponse)(nil), // 106: openshell.v1.UpdateProviderProfilesResponse - (*LintProviderProfilesRequest)(nil), // 107: openshell.v1.LintProviderProfilesRequest - (*LintProviderProfilesResponse)(nil), // 108: openshell.v1.LintProviderProfilesResponse - (*DeleteProviderResponse)(nil), // 109: openshell.v1.DeleteProviderResponse - (*DeleteProviderProfileRequest)(nil), // 110: openshell.v1.DeleteProviderProfileRequest - (*DeleteProviderProfileResponse)(nil), // 111: openshell.v1.DeleteProviderProfileResponse - (*GetSandboxProviderEnvironmentRequest)(nil), // 112: openshell.v1.GetSandboxProviderEnvironmentRequest - (*StaticCredentialEndpointBinding)(nil), // 113: openshell.v1.StaticCredentialEndpointBinding - (*StaticCredentialBinding)(nil), // 114: openshell.v1.StaticCredentialBinding - (*GetSandboxProviderEnvironmentResponse)(nil), // 115: openshell.v1.GetSandboxProviderEnvironmentResponse - (*ExchangeProviderSubjectTokenRequest)(nil), // 116: openshell.v1.ExchangeProviderSubjectTokenRequest - (*ExchangeProviderSubjectTokenResponse)(nil), // 117: openshell.v1.ExchangeProviderSubjectTokenResponse - (*UpdateConfigRequest)(nil), // 118: openshell.v1.UpdateConfigRequest - (*PolicyMergeOperation)(nil), // 119: openshell.v1.PolicyMergeOperation - (*AddNetworkRule)(nil), // 120: openshell.v1.AddNetworkRule - (*RemoveNetworkEndpoint)(nil), // 121: openshell.v1.RemoveNetworkEndpoint - (*RemoveNetworkRule)(nil), // 122: openshell.v1.RemoveNetworkRule - (*AddDenyRules)(nil), // 123: openshell.v1.AddDenyRules - (*AddAllowRules)(nil), // 124: openshell.v1.AddAllowRules - (*RemoveNetworkBinary)(nil), // 125: openshell.v1.RemoveNetworkBinary - (*UpdateConfigResponse)(nil), // 126: openshell.v1.UpdateConfigResponse - (*GetSandboxPolicyStatusRequest)(nil), // 127: openshell.v1.GetSandboxPolicyStatusRequest - (*GetSandboxPolicyStatusResponse)(nil), // 128: openshell.v1.GetSandboxPolicyStatusResponse - (*ListSandboxPoliciesRequest)(nil), // 129: openshell.v1.ListSandboxPoliciesRequest - (*ListSandboxPoliciesResponse)(nil), // 130: openshell.v1.ListSandboxPoliciesResponse - (*ReportPolicyStatusRequest)(nil), // 131: openshell.v1.ReportPolicyStatusRequest - (*ReportPolicyStatusResponse)(nil), // 132: openshell.v1.ReportPolicyStatusResponse - (*SandboxPolicyRevision)(nil), // 133: openshell.v1.SandboxPolicyRevision - (*GetSandboxLogsRequest)(nil), // 134: openshell.v1.GetSandboxLogsRequest - (*PushSandboxLogsRequest)(nil), // 135: openshell.v1.PushSandboxLogsRequest - (*PushSandboxLogsResponse)(nil), // 136: openshell.v1.PushSandboxLogsResponse - (*GetSandboxLogsResponse)(nil), // 137: openshell.v1.GetSandboxLogsResponse - (*SupervisorMessage)(nil), // 138: openshell.v1.SupervisorMessage - (*GatewayMessage)(nil), // 139: openshell.v1.GatewayMessage - (*SupervisorHello)(nil), // 140: openshell.v1.SupervisorHello - (*SessionAccepted)(nil), // 141: openshell.v1.SessionAccepted - (*SessionRejected)(nil), // 142: openshell.v1.SessionRejected - (*SupervisorHeartbeat)(nil), // 143: openshell.v1.SupervisorHeartbeat - (*GatewayHeartbeat)(nil), // 144: openshell.v1.GatewayHeartbeat - (*ReportMainProcessExitRequest)(nil), // 145: openshell.v1.ReportMainProcessExitRequest - (*ReportMainProcessExitResponse)(nil), // 146: openshell.v1.ReportMainProcessExitResponse - (*RelayOpen)(nil), // 147: openshell.v1.RelayOpen - (*SshRelayTarget)(nil), // 148: openshell.v1.SshRelayTarget - (*TcpRelayTarget)(nil), // 149: openshell.v1.TcpRelayTarget - (*RelayInit)(nil), // 150: openshell.v1.RelayInit - (*RelayFrame)(nil), // 151: openshell.v1.RelayFrame - (*RelayOpenResult)(nil), // 152: openshell.v1.RelayOpenResult - (*RelayClose)(nil), // 153: openshell.v1.RelayClose - (*L7RequestSample)(nil), // 154: openshell.v1.L7RequestSample - (*DenialSummary)(nil), // 155: openshell.v1.DenialSummary - (*DenialGroupCount)(nil), // 156: openshell.v1.DenialGroupCount - (*NetworkActivitySummary)(nil), // 157: openshell.v1.NetworkActivitySummary - (*PolicyChunk)(nil), // 158: openshell.v1.PolicyChunk - (*DraftPolicyUpdate)(nil), // 159: openshell.v1.DraftPolicyUpdate - (*SubmitPolicyAnalysisRequest)(nil), // 160: openshell.v1.SubmitPolicyAnalysisRequest - (*SubmitPolicyAnalysisResponse)(nil), // 161: openshell.v1.SubmitPolicyAnalysisResponse - (*GetDraftPolicyRequest)(nil), // 162: openshell.v1.GetDraftPolicyRequest - (*GetDraftPolicyResponse)(nil), // 163: openshell.v1.GetDraftPolicyResponse - (*ApproveDraftChunkRequest)(nil), // 164: openshell.v1.ApproveDraftChunkRequest - (*ApproveDraftChunkResponse)(nil), // 165: openshell.v1.ApproveDraftChunkResponse - (*RejectDraftChunkRequest)(nil), // 166: openshell.v1.RejectDraftChunkRequest - (*RejectDraftChunkResponse)(nil), // 167: openshell.v1.RejectDraftChunkResponse - (*DraftChunkApproval)(nil), // 168: openshell.v1.DraftChunkApproval - (*ApproveAllDraftChunksRequest)(nil), // 169: openshell.v1.ApproveAllDraftChunksRequest - (*ApproveAllDraftChunksResponse)(nil), // 170: openshell.v1.ApproveAllDraftChunksResponse - (*EditDraftChunkRequest)(nil), // 171: openshell.v1.EditDraftChunkRequest - (*EditDraftChunkResponse)(nil), // 172: openshell.v1.EditDraftChunkResponse - (*UndoDraftChunkRequest)(nil), // 173: openshell.v1.UndoDraftChunkRequest - (*UndoDraftChunkResponse)(nil), // 174: openshell.v1.UndoDraftChunkResponse - (*ClearDraftChunksRequest)(nil), // 175: openshell.v1.ClearDraftChunksRequest - (*ClearDraftChunksResponse)(nil), // 176: openshell.v1.ClearDraftChunksResponse - (*GetDraftHistoryRequest)(nil), // 177: openshell.v1.GetDraftHistoryRequest - (*DraftHistoryEntry)(nil), // 178: openshell.v1.DraftHistoryEntry - (*GetDraftHistoryResponse)(nil), // 179: openshell.v1.GetDraftHistoryResponse - (*PolicyRevisionPayload)(nil), // 180: openshell.v1.PolicyRevisionPayload - (*DraftChunkPayload)(nil), // 181: openshell.v1.DraftChunkPayload - (*StoredPolicyRevision)(nil), // 182: openshell.v1.StoredPolicyRevision - (*StoredDraftChunk)(nil), // 183: openshell.v1.StoredDraftChunk - (*CreateWorkspaceRequest)(nil), // 184: openshell.v1.CreateWorkspaceRequest - (*CreateWorkspaceResponse)(nil), // 185: openshell.v1.CreateWorkspaceResponse - (*GetWorkspaceRequest)(nil), // 186: openshell.v1.GetWorkspaceRequest - (*GetWorkspaceResponse)(nil), // 187: openshell.v1.GetWorkspaceResponse - (*ListWorkspacesRequest)(nil), // 188: openshell.v1.ListWorkspacesRequest - (*ListWorkspacesResponse)(nil), // 189: openshell.v1.ListWorkspacesResponse - (*DeleteWorkspaceRequest)(nil), // 190: openshell.v1.DeleteWorkspaceRequest - (*DeleteWorkspaceResponse)(nil), // 191: openshell.v1.DeleteWorkspaceResponse - (*WorkspaceMember)(nil), // 192: openshell.v1.WorkspaceMember - (*AddWorkspaceMemberRequest)(nil), // 193: openshell.v1.AddWorkspaceMemberRequest - (*AddWorkspaceMemberResponse)(nil), // 194: openshell.v1.AddWorkspaceMemberResponse - (*RemoveWorkspaceMemberRequest)(nil), // 195: openshell.v1.RemoveWorkspaceMemberRequest - (*RemoveWorkspaceMemberResponse)(nil), // 196: openshell.v1.RemoveWorkspaceMemberResponse - (*ListWorkspaceMembersRequest)(nil), // 197: openshell.v1.ListWorkspaceMembersRequest - (*ListWorkspaceMembersResponse)(nil), // 198: openshell.v1.ListWorkspaceMembersResponse - (*ExtensionServiceCredential)(nil), // 199: openshell.v1.ExtensionServiceCredential - nil, // 200: openshell.v1.SandboxSpec.EnvironmentEntry - nil, // 201: openshell.v1.SandboxTemplate.LabelsEntry - nil, // 202: openshell.v1.SandboxTemplate.AnnotationsEntry - nil, // 203: openshell.v1.SandboxTemplate.EnvironmentEntry - nil, // 204: openshell.v1.PlatformEvent.MetadataEntry - nil, // 205: openshell.v1.CreateSandboxRequest.LabelsEntry - nil, // 206: openshell.v1.CreateSandboxRequest.AnnotationsEntry - nil, // 207: openshell.v1.ExecSandboxRequest.EnvironmentEntry - nil, // 208: openshell.v1.SandboxLogLine.FieldsEntry - nil, // 209: openshell.v1.UpdateProviderRequest.CredentialExpiresAtMsEntry - nil, // 210: openshell.v1.StoredProviderCredentialRefreshState.MaterialEntry - nil, // 211: openshell.v1.StoredProviderCredentialRefreshState.AdditionalOutputKeysEntry - nil, // 212: openshell.v1.StoredProviderCredentialRefreshState.SecretMaterialHandlesEntry - nil, // 213: openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry - nil, // 214: openshell.v1.ProviderProfile.AnnotationsEntry - nil, // 215: openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry - nil, // 216: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpiresAtMsEntry - nil, // 217: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry - nil, // 218: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry - nil, // 219: openshell.v1.UpdateConfigRequest.AnnotationsEntry - nil, // 220: openshell.v1.UpdateConfigResponse.AnnotationsEntry - nil, // 221: openshell.v1.SandboxPolicyRevision.ProvenanceEntry - nil, // 222: openshell.v1.PolicyRevisionPayload.ProvenanceEntry - nil, // 223: openshell.v1.StoredPolicyRevision.ProvenanceEntry - nil, // 224: openshell.v1.CreateWorkspaceRequest.LabelsEntry - (*datamodelv1.ObjectMeta)(nil), // 225: openshell.datamodel.v1.ObjectMeta - (*sandboxv1.SandboxPolicy)(nil), // 226: openshell.sandbox.v1.SandboxPolicy - (*structpb.Struct)(nil), // 227: google.protobuf.Struct - (*datamodelv1.Provider)(nil), // 228: openshell.datamodel.v1.Provider - (*datamodelv1.CredentialHandle)(nil), // 229: openshell.datamodel.v1.CredentialHandle - (*sandboxv1.NetworkEndpoint)(nil), // 230: openshell.sandbox.v1.NetworkEndpoint - (*sandboxv1.NetworkBinary)(nil), // 231: openshell.sandbox.v1.NetworkBinary - (*sandboxv1.SettingValue)(nil), // 232: openshell.sandbox.v1.SettingValue - (*sandboxv1.NetworkPolicyRule)(nil), // 233: openshell.sandbox.v1.NetworkPolicyRule - (*sandboxv1.L7DenyRule)(nil), // 234: openshell.sandbox.v1.L7DenyRule - (*sandboxv1.L7Rule)(nil), // 235: openshell.sandbox.v1.L7Rule - (*datamodelv1.Workspace)(nil), // 236: openshell.datamodel.v1.Workspace - (*sandboxv1.GetSandboxConfigRequest)(nil), // 237: openshell.sandbox.v1.GetSandboxConfigRequest - (*sandboxv1.GetGatewayConfigRequest)(nil), // 238: openshell.sandbox.v1.GetGatewayConfigRequest - (*sandboxv1.GetSandboxConfigResponse)(nil), // 239: openshell.sandbox.v1.GetSandboxConfigResponse - (*sandboxv1.GetGatewayConfigResponse)(nil), // 240: openshell.sandbox.v1.GetGatewayConfigResponse + (ConfigBootstrapStatus)(0), // 5: openshell.v1.ConfigBootstrapStatus + (ConfigApplyOutcome)(0), // 6: openshell.v1.ConfigApplyOutcome + (ServiceStatus)(0), // 7: openshell.v1.ServiceStatus + (WorkspaceRole)(0), // 8: openshell.v1.WorkspaceRole + (ProviderCredentialRefreshRecoveryAction)(0), // 9: openshell.v1.ProviderCredentialRefreshRecoveryAction + (*IssueSandboxTokenRequest)(nil), // 10: openshell.v1.IssueSandboxTokenRequest + (*IssueSandboxTokenResponse)(nil), // 11: openshell.v1.IssueSandboxTokenResponse + (*RefreshSandboxTokenRequest)(nil), // 12: openshell.v1.RefreshSandboxTokenRequest + (*RefreshSandboxTokenResponse)(nil), // 13: openshell.v1.RefreshSandboxTokenResponse + (*HealthRequest)(nil), // 14: openshell.v1.HealthRequest + (*HealthResponse)(nil), // 15: openshell.v1.HealthResponse + (*GetCurrentUserRequest)(nil), // 16: openshell.v1.GetCurrentUserRequest + (*GetCurrentUserResponse)(nil), // 17: openshell.v1.GetCurrentUserResponse + (*GetGatewayInfoRequest)(nil), // 18: openshell.v1.GetGatewayInfoRequest + (*GetGatewayInfoResponse)(nil), // 19: openshell.v1.GetGatewayInfoResponse + (*ComputeDriverInfo)(nil), // 20: openshell.v1.ComputeDriverInfo + (*ComputeDriverCapabilities)(nil), // 21: openshell.v1.ComputeDriverCapabilities + (*Sandbox)(nil), // 22: openshell.v1.Sandbox + (*SandboxSpec)(nil), // 23: openshell.v1.SandboxSpec + (*ResourceRequirements)(nil), // 24: openshell.v1.ResourceRequirements + (*GpuResourceRequirements)(nil), // 25: openshell.v1.GpuResourceRequirements + (*SandboxTemplate)(nil), // 26: openshell.v1.SandboxTemplate + (*SandboxStatus)(nil), // 27: openshell.v1.SandboxStatus + (*SandboxCondition)(nil), // 28: openshell.v1.SandboxCondition + (*PlatformEvent)(nil), // 29: openshell.v1.PlatformEvent + (*CreateSandboxRequest)(nil), // 30: openshell.v1.CreateSandboxRequest + (*GetSandboxRequest)(nil), // 31: openshell.v1.GetSandboxRequest + (*ListSandboxesRequest)(nil), // 32: openshell.v1.ListSandboxesRequest + (*ListSandboxProvidersRequest)(nil), // 33: openshell.v1.ListSandboxProvidersRequest + (*AttachSandboxProviderRequest)(nil), // 34: openshell.v1.AttachSandboxProviderRequest + (*DetachSandboxProviderRequest)(nil), // 35: openshell.v1.DetachSandboxProviderRequest + (*DeleteSandboxRequest)(nil), // 36: openshell.v1.DeleteSandboxRequest + (*StopSandboxRequest)(nil), // 37: openshell.v1.StopSandboxRequest + (*StartSandboxRequest)(nil), // 38: openshell.v1.StartSandboxRequest + (*SandboxResponse)(nil), // 39: openshell.v1.SandboxResponse + (*ListSandboxesResponse)(nil), // 40: openshell.v1.ListSandboxesResponse + (*ListSandboxProvidersResponse)(nil), // 41: openshell.v1.ListSandboxProvidersResponse + (*AttachSandboxProviderResponse)(nil), // 42: openshell.v1.AttachSandboxProviderResponse + (*DetachSandboxProviderResponse)(nil), // 43: openshell.v1.DetachSandboxProviderResponse + (*DeleteSandboxResponse)(nil), // 44: openshell.v1.DeleteSandboxResponse + (*CreateSshSessionRequest)(nil), // 45: openshell.v1.CreateSshSessionRequest + (*CreateSshSessionResponse)(nil), // 46: openshell.v1.CreateSshSessionResponse + (*ExposeServiceRequest)(nil), // 47: openshell.v1.ExposeServiceRequest + (*GetServiceRequest)(nil), // 48: openshell.v1.GetServiceRequest + (*ListServicesRequest)(nil), // 49: openshell.v1.ListServicesRequest + (*ListServicesResponse)(nil), // 50: openshell.v1.ListServicesResponse + (*DeleteServiceRequest)(nil), // 51: openshell.v1.DeleteServiceRequest + (*DeleteServiceResponse)(nil), // 52: openshell.v1.DeleteServiceResponse + (*ServiceEndpoint)(nil), // 53: openshell.v1.ServiceEndpoint + (*ServiceEndpointResponse)(nil), // 54: openshell.v1.ServiceEndpointResponse + (*RevokeSshSessionRequest)(nil), // 55: openshell.v1.RevokeSshSessionRequest + (*RevokeSshSessionResponse)(nil), // 56: openshell.v1.RevokeSshSessionResponse + (*ExecSandboxRequest)(nil), // 57: openshell.v1.ExecSandboxRequest + (*ExecSandboxStdout)(nil), // 58: openshell.v1.ExecSandboxStdout + (*ExecSandboxStderr)(nil), // 59: openshell.v1.ExecSandboxStderr + (*ExecSandboxExit)(nil), // 60: openshell.v1.ExecSandboxExit + (*ExecSandboxEvent)(nil), // 61: openshell.v1.ExecSandboxEvent + (*TcpForwardInit)(nil), // 62: openshell.v1.TcpForwardInit + (*TcpForwardFrame)(nil), // 63: openshell.v1.TcpForwardFrame + (*ExecSandboxInput)(nil), // 64: openshell.v1.ExecSandboxInput + (*ExecSandboxWindowResize)(nil), // 65: openshell.v1.ExecSandboxWindowResize + (*SshSession)(nil), // 66: openshell.v1.SshSession + (*WatchSandboxRequest)(nil), // 67: openshell.v1.WatchSandboxRequest + (*SandboxStreamEvent)(nil), // 68: openshell.v1.SandboxStreamEvent + (*SandboxLogLine)(nil), // 69: openshell.v1.SandboxLogLine + (*SandboxStreamWarning)(nil), // 70: openshell.v1.SandboxStreamWarning + (*CreateProviderRequest)(nil), // 71: openshell.v1.CreateProviderRequest + (*GetProviderRequest)(nil), // 72: openshell.v1.GetProviderRequest + (*ListProvidersRequest)(nil), // 73: openshell.v1.ListProvidersRequest + (*UpdateProviderRequest)(nil), // 74: openshell.v1.UpdateProviderRequest + (*DeleteProviderRequest)(nil), // 75: openshell.v1.DeleteProviderRequest + (*ProviderResponse)(nil), // 76: openshell.v1.ProviderResponse + (*ListProvidersResponse)(nil), // 77: openshell.v1.ListProvidersResponse + (*ListProviderProfilesRequest)(nil), // 78: openshell.v1.ListProviderProfilesRequest + (*GetProviderProfileRequest)(nil), // 79: openshell.v1.GetProviderProfileRequest + (*ProviderProfileImportItem)(nil), // 80: openshell.v1.ProviderProfileImportItem + (*ProviderProfileDiagnostic)(nil), // 81: openshell.v1.ProviderProfileDiagnostic + (*ProviderCredentialTokenGrantAudienceOverride)(nil), // 82: openshell.v1.ProviderCredentialTokenGrantAudienceOverride + (*ProviderCredentialTokenGrantSubjectToken)(nil), // 83: openshell.v1.ProviderCredentialTokenGrantSubjectToken + (*ProviderCredentialTokenGrant)(nil), // 84: openshell.v1.ProviderCredentialTokenGrant + (*ProviderProfileCredential)(nil), // 85: openshell.v1.ProviderProfileCredential + (*ProviderCredentialRefreshMaterial)(nil), // 86: openshell.v1.ProviderCredentialRefreshMaterial + (*ProviderCredentialRefreshOutput)(nil), // 87: openshell.v1.ProviderCredentialRefreshOutput + (*ProviderCredentialRefresh)(nil), // 88: openshell.v1.ProviderCredentialRefresh + (*ProviderCredentialRefreshStatus)(nil), // 89: openshell.v1.ProviderCredentialRefreshStatus + (*ProviderProfileDiscovery)(nil), // 90: openshell.v1.ProviderProfileDiscovery + (*StoredProviderCredentialRefreshState)(nil), // 91: openshell.v1.StoredProviderCredentialRefreshState + (*StoredRefreshMaterialDeletion)(nil), // 92: openshell.v1.StoredRefreshMaterialDeletion + (*GetProviderRefreshStatusRequest)(nil), // 93: openshell.v1.GetProviderRefreshStatusRequest + (*GetProviderRefreshStatusResponse)(nil), // 94: openshell.v1.GetProviderRefreshStatusResponse + (*ConfigureProviderRefreshRequest)(nil), // 95: openshell.v1.ConfigureProviderRefreshRequest + (*ConfigureProviderRefreshResponse)(nil), // 96: openshell.v1.ConfigureProviderRefreshResponse + (*RotateProviderCredentialRequest)(nil), // 97: openshell.v1.RotateProviderCredentialRequest + (*RotateProviderCredentialResponse)(nil), // 98: openshell.v1.RotateProviderCredentialResponse + (*DeleteProviderRefreshRequest)(nil), // 99: openshell.v1.DeleteProviderRefreshRequest + (*DeleteProviderRefreshResponse)(nil), // 100: openshell.v1.DeleteProviderRefreshResponse + (*ProviderProfile)(nil), // 101: openshell.v1.ProviderProfile + (*StoredProviderProfile)(nil), // 102: openshell.v1.StoredProviderProfile + (*ProviderProfileResponse)(nil), // 103: openshell.v1.ProviderProfileResponse + (*ListProviderProfilesResponse)(nil), // 104: openshell.v1.ListProviderProfilesResponse + (*ImportProviderProfilesRequest)(nil), // 105: openshell.v1.ImportProviderProfilesRequest + (*ImportProviderProfilesResponse)(nil), // 106: openshell.v1.ImportProviderProfilesResponse + (*UpdateProviderProfilesRequest)(nil), // 107: openshell.v1.UpdateProviderProfilesRequest + (*UpdateProviderProfilesResponse)(nil), // 108: openshell.v1.UpdateProviderProfilesResponse + (*LintProviderProfilesRequest)(nil), // 109: openshell.v1.LintProviderProfilesRequest + (*LintProviderProfilesResponse)(nil), // 110: openshell.v1.LintProviderProfilesResponse + (*DeleteProviderResponse)(nil), // 111: openshell.v1.DeleteProviderResponse + (*DeleteProviderProfileRequest)(nil), // 112: openshell.v1.DeleteProviderProfileRequest + (*DeleteProviderProfileResponse)(nil), // 113: openshell.v1.DeleteProviderProfileResponse + (*GetSandboxProviderEnvironmentRequest)(nil), // 114: openshell.v1.GetSandboxProviderEnvironmentRequest + (*StaticCredentialEndpointBinding)(nil), // 115: openshell.v1.StaticCredentialEndpointBinding + (*StaticCredentialBinding)(nil), // 116: openshell.v1.StaticCredentialBinding + (*ProviderEnvironmentSnapshot)(nil), // 117: openshell.v1.ProviderEnvironmentSnapshot + (*ExchangeProviderSubjectTokenRequest)(nil), // 118: openshell.v1.ExchangeProviderSubjectTokenRequest + (*ExchangeProviderSubjectTokenResponse)(nil), // 119: openshell.v1.ExchangeProviderSubjectTokenResponse + (*UpdateConfigRequest)(nil), // 120: openshell.v1.UpdateConfigRequest + (*PolicyMergeOperation)(nil), // 121: openshell.v1.PolicyMergeOperation + (*AddNetworkRule)(nil), // 122: openshell.v1.AddNetworkRule + (*RemoveNetworkEndpoint)(nil), // 123: openshell.v1.RemoveNetworkEndpoint + (*RemoveNetworkRule)(nil), // 124: openshell.v1.RemoveNetworkRule + (*AddDenyRules)(nil), // 125: openshell.v1.AddDenyRules + (*AddAllowRules)(nil), // 126: openshell.v1.AddAllowRules + (*RemoveNetworkBinary)(nil), // 127: openshell.v1.RemoveNetworkBinary + (*UpdateConfigResponse)(nil), // 128: openshell.v1.UpdateConfigResponse + (*GetSandboxPolicyStatusRequest)(nil), // 129: openshell.v1.GetSandboxPolicyStatusRequest + (*GetSandboxPolicyStatusResponse)(nil), // 130: openshell.v1.GetSandboxPolicyStatusResponse + (*ListSandboxPoliciesRequest)(nil), // 131: openshell.v1.ListSandboxPoliciesRequest + (*ListSandboxPoliciesResponse)(nil), // 132: openshell.v1.ListSandboxPoliciesResponse + (*ReportPolicyStatusRequest)(nil), // 133: openshell.v1.ReportPolicyStatusRequest + (*ReportPolicyStatusResponse)(nil), // 134: openshell.v1.ReportPolicyStatusResponse + (*SandboxPolicyRevision)(nil), // 135: openshell.v1.SandboxPolicyRevision + (*GetSandboxLogsRequest)(nil), // 136: openshell.v1.GetSandboxLogsRequest + (*PushSandboxLogsRequest)(nil), // 137: openshell.v1.PushSandboxLogsRequest + (*PushSandboxLogsResponse)(nil), // 138: openshell.v1.PushSandboxLogsResponse + (*GetSandboxLogsResponse)(nil), // 139: openshell.v1.GetSandboxLogsResponse + (*SupervisorMessage)(nil), // 140: openshell.v1.SupervisorMessage + (*GatewayMessage)(nil), // 141: openshell.v1.GatewayMessage + (*SupervisorHello)(nil), // 142: openshell.v1.SupervisorHello + (*SessionAccepted)(nil), // 143: openshell.v1.SessionAccepted + (*ConfigBootstrap)(nil), // 144: openshell.v1.ConfigBootstrap + (*ConfigBootstrapResult)(nil), // 145: openshell.v1.ConfigBootstrapResult + (*ConfigUpdate)(nil), // 146: openshell.v1.ConfigUpdate + (*SandboxConfigUpdate)(nil), // 147: openshell.v1.SandboxConfigUpdate + (*ProviderEnvironmentUpdate)(nil), // 148: openshell.v1.ProviderEnvironmentUpdate + (*InferenceBundleUpdate)(nil), // 149: openshell.v1.InferenceBundleUpdate + (*ConfigUpdateResult)(nil), // 150: openshell.v1.ConfigUpdateResult + (*SupervisorRuntimeReady)(nil), // 151: openshell.v1.SupervisorRuntimeReady + (*SessionRejected)(nil), // 152: openshell.v1.SessionRejected + (*SupervisorHeartbeat)(nil), // 153: openshell.v1.SupervisorHeartbeat + (*GatewayHeartbeat)(nil), // 154: openshell.v1.GatewayHeartbeat + (*ReportMainProcessExitRequest)(nil), // 155: openshell.v1.ReportMainProcessExitRequest + (*ReportMainProcessExitResponse)(nil), // 156: openshell.v1.ReportMainProcessExitResponse + (*RelayOpen)(nil), // 157: openshell.v1.RelayOpen + (*SshRelayTarget)(nil), // 158: openshell.v1.SshRelayTarget + (*TcpRelayTarget)(nil), // 159: openshell.v1.TcpRelayTarget + (*RelayInit)(nil), // 160: openshell.v1.RelayInit + (*RelayFrame)(nil), // 161: openshell.v1.RelayFrame + (*RelayOpenResult)(nil), // 162: openshell.v1.RelayOpenResult + (*RelayClose)(nil), // 163: openshell.v1.RelayClose + (*L7RequestSample)(nil), // 164: openshell.v1.L7RequestSample + (*DenialSummary)(nil), // 165: openshell.v1.DenialSummary + (*DenialGroupCount)(nil), // 166: openshell.v1.DenialGroupCount + (*NetworkActivitySummary)(nil), // 167: openshell.v1.NetworkActivitySummary + (*PolicyChunk)(nil), // 168: openshell.v1.PolicyChunk + (*DraftPolicyUpdate)(nil), // 169: openshell.v1.DraftPolicyUpdate + (*SubmitPolicyAnalysisRequest)(nil), // 170: openshell.v1.SubmitPolicyAnalysisRequest + (*SubmitPolicyAnalysisResponse)(nil), // 171: openshell.v1.SubmitPolicyAnalysisResponse + (*GetDraftPolicyRequest)(nil), // 172: openshell.v1.GetDraftPolicyRequest + (*GetDraftPolicyResponse)(nil), // 173: openshell.v1.GetDraftPolicyResponse + (*ApproveDraftChunkRequest)(nil), // 174: openshell.v1.ApproveDraftChunkRequest + (*ApproveDraftChunkResponse)(nil), // 175: openshell.v1.ApproveDraftChunkResponse + (*RejectDraftChunkRequest)(nil), // 176: openshell.v1.RejectDraftChunkRequest + (*RejectDraftChunkResponse)(nil), // 177: openshell.v1.RejectDraftChunkResponse + (*DraftChunkApproval)(nil), // 178: openshell.v1.DraftChunkApproval + (*ApproveAllDraftChunksRequest)(nil), // 179: openshell.v1.ApproveAllDraftChunksRequest + (*ApproveAllDraftChunksResponse)(nil), // 180: openshell.v1.ApproveAllDraftChunksResponse + (*EditDraftChunkRequest)(nil), // 181: openshell.v1.EditDraftChunkRequest + (*EditDraftChunkResponse)(nil), // 182: openshell.v1.EditDraftChunkResponse + (*UndoDraftChunkRequest)(nil), // 183: openshell.v1.UndoDraftChunkRequest + (*UndoDraftChunkResponse)(nil), // 184: openshell.v1.UndoDraftChunkResponse + (*ClearDraftChunksRequest)(nil), // 185: openshell.v1.ClearDraftChunksRequest + (*ClearDraftChunksResponse)(nil), // 186: openshell.v1.ClearDraftChunksResponse + (*GetDraftHistoryRequest)(nil), // 187: openshell.v1.GetDraftHistoryRequest + (*DraftHistoryEntry)(nil), // 188: openshell.v1.DraftHistoryEntry + (*GetDraftHistoryResponse)(nil), // 189: openshell.v1.GetDraftHistoryResponse + (*PolicyRevisionPayload)(nil), // 190: openshell.v1.PolicyRevisionPayload + (*DraftChunkPayload)(nil), // 191: openshell.v1.DraftChunkPayload + (*StoredPolicyRevision)(nil), // 192: openshell.v1.StoredPolicyRevision + (*StoredDraftChunk)(nil), // 193: openshell.v1.StoredDraftChunk + (*CreateWorkspaceRequest)(nil), // 194: openshell.v1.CreateWorkspaceRequest + (*CreateWorkspaceResponse)(nil), // 195: openshell.v1.CreateWorkspaceResponse + (*GetWorkspaceRequest)(nil), // 196: openshell.v1.GetWorkspaceRequest + (*GetWorkspaceResponse)(nil), // 197: openshell.v1.GetWorkspaceResponse + (*ListWorkspacesRequest)(nil), // 198: openshell.v1.ListWorkspacesRequest + (*ListWorkspacesResponse)(nil), // 199: openshell.v1.ListWorkspacesResponse + (*DeleteWorkspaceRequest)(nil), // 200: openshell.v1.DeleteWorkspaceRequest + (*DeleteWorkspaceResponse)(nil), // 201: openshell.v1.DeleteWorkspaceResponse + (*WorkspaceMember)(nil), // 202: openshell.v1.WorkspaceMember + (*AddWorkspaceMemberRequest)(nil), // 203: openshell.v1.AddWorkspaceMemberRequest + (*AddWorkspaceMemberResponse)(nil), // 204: openshell.v1.AddWorkspaceMemberResponse + (*RemoveWorkspaceMemberRequest)(nil), // 205: openshell.v1.RemoveWorkspaceMemberRequest + (*RemoveWorkspaceMemberResponse)(nil), // 206: openshell.v1.RemoveWorkspaceMemberResponse + (*ListWorkspaceMembersRequest)(nil), // 207: openshell.v1.ListWorkspaceMembersRequest + (*ListWorkspaceMembersResponse)(nil), // 208: openshell.v1.ListWorkspaceMembersResponse + (*ExtensionServiceCredential)(nil), // 209: openshell.v1.ExtensionServiceCredential + nil, // 210: openshell.v1.SandboxSpec.EnvironmentEntry + nil, // 211: openshell.v1.SandboxTemplate.LabelsEntry + nil, // 212: openshell.v1.SandboxTemplate.AnnotationsEntry + nil, // 213: openshell.v1.SandboxTemplate.EnvironmentEntry + nil, // 214: openshell.v1.PlatformEvent.MetadataEntry + nil, // 215: openshell.v1.CreateSandboxRequest.LabelsEntry + nil, // 216: openshell.v1.CreateSandboxRequest.AnnotationsEntry + nil, // 217: openshell.v1.ExecSandboxRequest.EnvironmentEntry + nil, // 218: openshell.v1.SandboxLogLine.FieldsEntry + nil, // 219: openshell.v1.UpdateProviderRequest.CredentialExpiresAtMsEntry + nil, // 220: openshell.v1.StoredProviderCredentialRefreshState.MaterialEntry + nil, // 221: openshell.v1.StoredProviderCredentialRefreshState.AdditionalOutputKeysEntry + nil, // 222: openshell.v1.StoredProviderCredentialRefreshState.SecretMaterialHandlesEntry + nil, // 223: openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry + nil, // 224: openshell.v1.ProviderProfile.AnnotationsEntry + nil, // 225: openshell.v1.ProviderEnvironmentSnapshot.EnvironmentEntry + nil, // 226: openshell.v1.ProviderEnvironmentSnapshot.CredentialExpiresAtMsEntry + nil, // 227: openshell.v1.ProviderEnvironmentSnapshot.DynamicCredentialsEntry + nil, // 228: openshell.v1.ProviderEnvironmentSnapshot.StaticCredentialBindingsEntry + nil, // 229: openshell.v1.UpdateConfigRequest.AnnotationsEntry + nil, // 230: openshell.v1.UpdateConfigResponse.AnnotationsEntry + nil, // 231: openshell.v1.SandboxPolicyRevision.ProvenanceEntry + nil, // 232: openshell.v1.PolicyRevisionPayload.ProvenanceEntry + nil, // 233: openshell.v1.StoredPolicyRevision.ProvenanceEntry + nil, // 234: openshell.v1.CreateWorkspaceRequest.LabelsEntry + (*datamodelv1.ObjectMeta)(nil), // 235: openshell.datamodel.v1.ObjectMeta + (*sandboxv1.SandboxPolicy)(nil), // 236: openshell.sandbox.v1.SandboxPolicy + (*structpb.Struct)(nil), // 237: google.protobuf.Struct + (*datamodelv1.Provider)(nil), // 238: openshell.datamodel.v1.Provider + (*datamodelv1.CredentialHandle)(nil), // 239: openshell.datamodel.v1.CredentialHandle + (*sandboxv1.NetworkEndpoint)(nil), // 240: openshell.sandbox.v1.NetworkEndpoint + (*sandboxv1.NetworkBinary)(nil), // 241: openshell.sandbox.v1.NetworkBinary + (*sandboxv1.SettingValue)(nil), // 242: openshell.sandbox.v1.SettingValue + (*sandboxv1.NetworkPolicyRule)(nil), // 243: openshell.sandbox.v1.NetworkPolicyRule + (*sandboxv1.L7DenyRule)(nil), // 244: openshell.sandbox.v1.L7DenyRule + (*sandboxv1.L7Rule)(nil), // 245: openshell.sandbox.v1.L7Rule + (*sandboxv1.SandboxConfigSnapshot)(nil), // 246: openshell.sandbox.v1.SandboxConfigSnapshot + (*inferencev1.InferenceBundleSnapshot)(nil), // 247: openshell.inference.v1.InferenceBundleSnapshot + (*datamodelv1.Workspace)(nil), // 248: openshell.datamodel.v1.Workspace + (*sandboxv1.GetSandboxConfigRequest)(nil), // 249: openshell.sandbox.v1.GetSandboxConfigRequest + (*sandboxv1.GetGatewayConfigRequest)(nil), // 250: openshell.sandbox.v1.GetGatewayConfigRequest + (*sandboxv1.GetGatewayConfigResponse)(nil), // 251: openshell.sandbox.v1.GetGatewayConfigResponse } var file_openshell_proto_depIdxs = []int32{ - 199, // 0: openshell.v1.RefreshSandboxTokenResponse.extension_credentials:type_name -> openshell.v1.ExtensionServiceCredential - 5, // 1: openshell.v1.HealthResponse.status:type_name -> openshell.v1.ServiceStatus - 5, // 2: openshell.v1.GetGatewayInfoResponse.status:type_name -> openshell.v1.ServiceStatus - 18, // 3: openshell.v1.GetGatewayInfoResponse.compute_drivers:type_name -> openshell.v1.ComputeDriverInfo - 19, // 4: openshell.v1.ComputeDriverInfo.capabilities:type_name -> openshell.v1.ComputeDriverCapabilities - 225, // 5: openshell.v1.Sandbox.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 21, // 6: openshell.v1.Sandbox.spec:type_name -> openshell.v1.SandboxSpec - 25, // 7: openshell.v1.Sandbox.status:type_name -> openshell.v1.SandboxStatus - 200, // 8: openshell.v1.SandboxSpec.environment:type_name -> openshell.v1.SandboxSpec.EnvironmentEntry - 24, // 9: openshell.v1.SandboxSpec.template:type_name -> openshell.v1.SandboxTemplate - 226, // 10: openshell.v1.SandboxSpec.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 22, // 11: openshell.v1.SandboxSpec.resource_requirements:type_name -> openshell.v1.ResourceRequirements - 23, // 12: openshell.v1.ResourceRequirements.gpu:type_name -> openshell.v1.GpuResourceRequirements - 201, // 13: openshell.v1.SandboxTemplate.labels:type_name -> openshell.v1.SandboxTemplate.LabelsEntry - 202, // 14: openshell.v1.SandboxTemplate.annotations:type_name -> openshell.v1.SandboxTemplate.AnnotationsEntry - 203, // 15: openshell.v1.SandboxTemplate.environment:type_name -> openshell.v1.SandboxTemplate.EnvironmentEntry - 227, // 16: openshell.v1.SandboxTemplate.resources:type_name -> google.protobuf.Struct - 227, // 17: openshell.v1.SandboxTemplate.driver_config:type_name -> google.protobuf.Struct - 26, // 18: openshell.v1.SandboxStatus.conditions:type_name -> openshell.v1.SandboxCondition + 209, // 0: openshell.v1.RefreshSandboxTokenResponse.extension_credentials:type_name -> openshell.v1.ExtensionServiceCredential + 7, // 1: openshell.v1.HealthResponse.status:type_name -> openshell.v1.ServiceStatus + 7, // 2: openshell.v1.GetGatewayInfoResponse.status:type_name -> openshell.v1.ServiceStatus + 20, // 3: openshell.v1.GetGatewayInfoResponse.compute_drivers:type_name -> openshell.v1.ComputeDriverInfo + 21, // 4: openshell.v1.ComputeDriverInfo.capabilities:type_name -> openshell.v1.ComputeDriverCapabilities + 235, // 5: openshell.v1.Sandbox.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 23, // 6: openshell.v1.Sandbox.spec:type_name -> openshell.v1.SandboxSpec + 27, // 7: openshell.v1.Sandbox.status:type_name -> openshell.v1.SandboxStatus + 210, // 8: openshell.v1.SandboxSpec.environment:type_name -> openshell.v1.SandboxSpec.EnvironmentEntry + 26, // 9: openshell.v1.SandboxSpec.template:type_name -> openshell.v1.SandboxTemplate + 236, // 10: openshell.v1.SandboxSpec.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 24, // 11: openshell.v1.SandboxSpec.resource_requirements:type_name -> openshell.v1.ResourceRequirements + 25, // 12: openshell.v1.ResourceRequirements.gpu:type_name -> openshell.v1.GpuResourceRequirements + 211, // 13: openshell.v1.SandboxTemplate.labels:type_name -> openshell.v1.SandboxTemplate.LabelsEntry + 212, // 14: openshell.v1.SandboxTemplate.annotations:type_name -> openshell.v1.SandboxTemplate.AnnotationsEntry + 213, // 15: openshell.v1.SandboxTemplate.environment:type_name -> openshell.v1.SandboxTemplate.EnvironmentEntry + 237, // 16: openshell.v1.SandboxTemplate.resources:type_name -> google.protobuf.Struct + 237, // 17: openshell.v1.SandboxTemplate.driver_config:type_name -> google.protobuf.Struct + 28, // 18: openshell.v1.SandboxStatus.conditions:type_name -> openshell.v1.SandboxCondition 0, // 19: openshell.v1.SandboxStatus.phase:type_name -> openshell.v1.SandboxPhase - 204, // 20: openshell.v1.PlatformEvent.metadata:type_name -> openshell.v1.PlatformEvent.MetadataEntry - 21, // 21: openshell.v1.CreateSandboxRequest.spec:type_name -> openshell.v1.SandboxSpec - 205, // 22: openshell.v1.CreateSandboxRequest.labels:type_name -> openshell.v1.CreateSandboxRequest.LabelsEntry - 206, // 23: openshell.v1.CreateSandboxRequest.annotations:type_name -> openshell.v1.CreateSandboxRequest.AnnotationsEntry - 20, // 24: openshell.v1.SandboxResponse.sandbox:type_name -> openshell.v1.Sandbox - 20, // 25: openshell.v1.ListSandboxesResponse.sandboxes:type_name -> openshell.v1.Sandbox - 228, // 26: openshell.v1.ListSandboxProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider - 20, // 27: openshell.v1.AttachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox - 20, // 28: openshell.v1.DetachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox - 52, // 29: openshell.v1.ListServicesResponse.services:type_name -> openshell.v1.ServiceEndpointResponse - 225, // 30: openshell.v1.ServiceEndpoint.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 51, // 31: openshell.v1.ServiceEndpointResponse.endpoint:type_name -> openshell.v1.ServiceEndpoint - 207, // 32: openshell.v1.ExecSandboxRequest.environment:type_name -> openshell.v1.ExecSandboxRequest.EnvironmentEntry - 56, // 33: openshell.v1.ExecSandboxEvent.stdout:type_name -> openshell.v1.ExecSandboxStdout - 57, // 34: openshell.v1.ExecSandboxEvent.stderr:type_name -> openshell.v1.ExecSandboxStderr - 58, // 35: openshell.v1.ExecSandboxEvent.exit:type_name -> openshell.v1.ExecSandboxExit - 148, // 36: openshell.v1.TcpForwardInit.ssh:type_name -> openshell.v1.SshRelayTarget - 149, // 37: openshell.v1.TcpForwardInit.tcp:type_name -> openshell.v1.TcpRelayTarget - 60, // 38: openshell.v1.TcpForwardFrame.init:type_name -> openshell.v1.TcpForwardInit - 55, // 39: openshell.v1.ExecSandboxInput.start:type_name -> openshell.v1.ExecSandboxRequest - 63, // 40: openshell.v1.ExecSandboxInput.resize:type_name -> openshell.v1.ExecSandboxWindowResize - 225, // 41: openshell.v1.SshSession.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 20, // 42: openshell.v1.SandboxStreamEvent.sandbox:type_name -> openshell.v1.Sandbox - 67, // 43: openshell.v1.SandboxStreamEvent.log:type_name -> openshell.v1.SandboxLogLine - 27, // 44: openshell.v1.SandboxStreamEvent.event:type_name -> openshell.v1.PlatformEvent - 68, // 45: openshell.v1.SandboxStreamEvent.warning:type_name -> openshell.v1.SandboxStreamWarning - 159, // 46: openshell.v1.SandboxStreamEvent.draft_policy_update:type_name -> openshell.v1.DraftPolicyUpdate - 208, // 47: openshell.v1.SandboxLogLine.fields:type_name -> openshell.v1.SandboxLogLine.FieldsEntry - 228, // 48: openshell.v1.CreateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider - 228, // 49: openshell.v1.UpdateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider - 209, // 50: openshell.v1.UpdateProviderRequest.credential_expires_at_ms:type_name -> openshell.v1.UpdateProviderRequest.CredentialExpiresAtMsEntry - 228, // 51: openshell.v1.ProviderResponse.provider:type_name -> openshell.datamodel.v1.Provider - 228, // 52: openshell.v1.ListProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider - 99, // 53: openshell.v1.ProviderProfileImportItem.profile:type_name -> openshell.v1.ProviderProfile - 80, // 54: openshell.v1.ProviderCredentialTokenGrant.audience_overrides:type_name -> openshell.v1.ProviderCredentialTokenGrantAudienceOverride + 214, // 20: openshell.v1.PlatformEvent.metadata:type_name -> openshell.v1.PlatformEvent.MetadataEntry + 23, // 21: openshell.v1.CreateSandboxRequest.spec:type_name -> openshell.v1.SandboxSpec + 215, // 22: openshell.v1.CreateSandboxRequest.labels:type_name -> openshell.v1.CreateSandboxRequest.LabelsEntry + 216, // 23: openshell.v1.CreateSandboxRequest.annotations:type_name -> openshell.v1.CreateSandboxRequest.AnnotationsEntry + 22, // 24: openshell.v1.SandboxResponse.sandbox:type_name -> openshell.v1.Sandbox + 22, // 25: openshell.v1.ListSandboxesResponse.sandboxes:type_name -> openshell.v1.Sandbox + 238, // 26: openshell.v1.ListSandboxProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider + 22, // 27: openshell.v1.AttachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox + 22, // 28: openshell.v1.DetachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox + 54, // 29: openshell.v1.ListServicesResponse.services:type_name -> openshell.v1.ServiceEndpointResponse + 235, // 30: openshell.v1.ServiceEndpoint.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 53, // 31: openshell.v1.ServiceEndpointResponse.endpoint:type_name -> openshell.v1.ServiceEndpoint + 217, // 32: openshell.v1.ExecSandboxRequest.environment:type_name -> openshell.v1.ExecSandboxRequest.EnvironmentEntry + 58, // 33: openshell.v1.ExecSandboxEvent.stdout:type_name -> openshell.v1.ExecSandboxStdout + 59, // 34: openshell.v1.ExecSandboxEvent.stderr:type_name -> openshell.v1.ExecSandboxStderr + 60, // 35: openshell.v1.ExecSandboxEvent.exit:type_name -> openshell.v1.ExecSandboxExit + 158, // 36: openshell.v1.TcpForwardInit.ssh:type_name -> openshell.v1.SshRelayTarget + 159, // 37: openshell.v1.TcpForwardInit.tcp:type_name -> openshell.v1.TcpRelayTarget + 62, // 38: openshell.v1.TcpForwardFrame.init:type_name -> openshell.v1.TcpForwardInit + 57, // 39: openshell.v1.ExecSandboxInput.start:type_name -> openshell.v1.ExecSandboxRequest + 65, // 40: openshell.v1.ExecSandboxInput.resize:type_name -> openshell.v1.ExecSandboxWindowResize + 235, // 41: openshell.v1.SshSession.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 22, // 42: openshell.v1.SandboxStreamEvent.sandbox:type_name -> openshell.v1.Sandbox + 69, // 43: openshell.v1.SandboxStreamEvent.log:type_name -> openshell.v1.SandboxLogLine + 29, // 44: openshell.v1.SandboxStreamEvent.event:type_name -> openshell.v1.PlatformEvent + 70, // 45: openshell.v1.SandboxStreamEvent.warning:type_name -> openshell.v1.SandboxStreamWarning + 169, // 46: openshell.v1.SandboxStreamEvent.draft_policy_update:type_name -> openshell.v1.DraftPolicyUpdate + 218, // 47: openshell.v1.SandboxLogLine.fields:type_name -> openshell.v1.SandboxLogLine.FieldsEntry + 238, // 48: openshell.v1.CreateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider + 238, // 49: openshell.v1.UpdateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider + 219, // 50: openshell.v1.UpdateProviderRequest.credential_expires_at_ms:type_name -> openshell.v1.UpdateProviderRequest.CredentialExpiresAtMsEntry + 238, // 51: openshell.v1.ProviderResponse.provider:type_name -> openshell.datamodel.v1.Provider + 238, // 52: openshell.v1.ListProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider + 101, // 53: openshell.v1.ProviderProfileImportItem.profile:type_name -> openshell.v1.ProviderProfile + 82, // 54: openshell.v1.ProviderCredentialTokenGrant.audience_overrides:type_name -> openshell.v1.ProviderCredentialTokenGrantAudienceOverride 1, // 55: openshell.v1.ProviderCredentialTokenGrant.grant_type:type_name -> openshell.v1.ProviderCredentialTokenGrantType - 81, // 56: openshell.v1.ProviderCredentialTokenGrant.subject_token:type_name -> openshell.v1.ProviderCredentialTokenGrantSubjectToken - 86, // 57: openshell.v1.ProviderProfileCredential.refresh:type_name -> openshell.v1.ProviderCredentialRefresh - 82, // 58: openshell.v1.ProviderProfileCredential.token_grant:type_name -> openshell.v1.ProviderCredentialTokenGrant + 83, // 56: openshell.v1.ProviderCredentialTokenGrant.subject_token:type_name -> openshell.v1.ProviderCredentialTokenGrantSubjectToken + 88, // 57: openshell.v1.ProviderProfileCredential.refresh:type_name -> openshell.v1.ProviderCredentialRefresh + 84, // 58: openshell.v1.ProviderProfileCredential.token_grant:type_name -> openshell.v1.ProviderCredentialTokenGrant 2, // 59: openshell.v1.ProviderCredentialRefresh.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 84, // 60: openshell.v1.ProviderCredentialRefresh.material:type_name -> openshell.v1.ProviderCredentialRefreshMaterial - 85, // 61: openshell.v1.ProviderCredentialRefresh.additional_outputs:type_name -> openshell.v1.ProviderCredentialRefreshOutput + 86, // 60: openshell.v1.ProviderCredentialRefresh.material:type_name -> openshell.v1.ProviderCredentialRefreshMaterial + 87, // 61: openshell.v1.ProviderCredentialRefresh.additional_outputs:type_name -> openshell.v1.ProviderCredentialRefreshOutput 2, // 62: openshell.v1.ProviderCredentialRefreshStatus.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 7, // 63: openshell.v1.ProviderCredentialRefreshStatus.recovery_action:type_name -> openshell.v1.ProviderCredentialRefreshRecoveryAction - 225, // 64: openshell.v1.StoredProviderCredentialRefreshState.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 9, // 63: openshell.v1.ProviderCredentialRefreshStatus.recovery_action:type_name -> openshell.v1.ProviderCredentialRefreshRecoveryAction + 235, // 64: openshell.v1.StoredProviderCredentialRefreshState.metadata:type_name -> openshell.datamodel.v1.ObjectMeta 2, // 65: openshell.v1.StoredProviderCredentialRefreshState.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 210, // 66: openshell.v1.StoredProviderCredentialRefreshState.material:type_name -> openshell.v1.StoredProviderCredentialRefreshState.MaterialEntry - 211, // 67: openshell.v1.StoredProviderCredentialRefreshState.additional_output_keys:type_name -> openshell.v1.StoredProviderCredentialRefreshState.AdditionalOutputKeysEntry - 212, // 68: openshell.v1.StoredProviderCredentialRefreshState.secret_material_handles:type_name -> openshell.v1.StoredProviderCredentialRefreshState.SecretMaterialHandlesEntry - 90, // 69: openshell.v1.StoredProviderCredentialRefreshState.pending_secret_deletions:type_name -> openshell.v1.StoredRefreshMaterialDeletion - 7, // 70: openshell.v1.StoredProviderCredentialRefreshState.recovery_action:type_name -> openshell.v1.ProviderCredentialRefreshRecoveryAction - 229, // 71: openshell.v1.StoredRefreshMaterialDeletion.handle:type_name -> openshell.datamodel.v1.CredentialHandle - 87, // 72: openshell.v1.GetProviderRefreshStatusResponse.credentials:type_name -> openshell.v1.ProviderCredentialRefreshStatus + 220, // 66: openshell.v1.StoredProviderCredentialRefreshState.material:type_name -> openshell.v1.StoredProviderCredentialRefreshState.MaterialEntry + 221, // 67: openshell.v1.StoredProviderCredentialRefreshState.additional_output_keys:type_name -> openshell.v1.StoredProviderCredentialRefreshState.AdditionalOutputKeysEntry + 222, // 68: openshell.v1.StoredProviderCredentialRefreshState.secret_material_handles:type_name -> openshell.v1.StoredProviderCredentialRefreshState.SecretMaterialHandlesEntry + 92, // 69: openshell.v1.StoredProviderCredentialRefreshState.pending_secret_deletions:type_name -> openshell.v1.StoredRefreshMaterialDeletion + 9, // 70: openshell.v1.StoredProviderCredentialRefreshState.recovery_action:type_name -> openshell.v1.ProviderCredentialRefreshRecoveryAction + 239, // 71: openshell.v1.StoredRefreshMaterialDeletion.handle:type_name -> openshell.datamodel.v1.CredentialHandle + 89, // 72: openshell.v1.GetProviderRefreshStatusResponse.credentials:type_name -> openshell.v1.ProviderCredentialRefreshStatus 2, // 73: openshell.v1.ConfigureProviderRefreshRequest.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 213, // 74: openshell.v1.ConfigureProviderRefreshRequest.material:type_name -> openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry - 87, // 75: openshell.v1.ConfigureProviderRefreshResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 87, // 76: openshell.v1.RotateProviderCredentialResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus + 223, // 74: openshell.v1.ConfigureProviderRefreshRequest.material:type_name -> openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry + 89, // 75: openshell.v1.ConfigureProviderRefreshResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus + 89, // 76: openshell.v1.RotateProviderCredentialResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus 3, // 77: openshell.v1.ProviderProfile.category:type_name -> openshell.v1.ProviderProfileCategory - 83, // 78: openshell.v1.ProviderProfile.credentials:type_name -> openshell.v1.ProviderProfileCredential - 230, // 79: openshell.v1.ProviderProfile.endpoints:type_name -> openshell.sandbox.v1.NetworkEndpoint - 231, // 80: openshell.v1.ProviderProfile.binaries:type_name -> openshell.sandbox.v1.NetworkBinary - 88, // 81: openshell.v1.ProviderProfile.discovery:type_name -> openshell.v1.ProviderProfileDiscovery - 214, // 82: openshell.v1.ProviderProfile.annotations:type_name -> openshell.v1.ProviderProfile.AnnotationsEntry - 225, // 83: openshell.v1.StoredProviderProfile.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 99, // 84: openshell.v1.StoredProviderProfile.profile:type_name -> openshell.v1.ProviderProfile - 99, // 85: openshell.v1.ProviderProfileResponse.profile:type_name -> openshell.v1.ProviderProfile - 99, // 86: openshell.v1.ListProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile - 78, // 87: openshell.v1.ImportProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem - 79, // 88: openshell.v1.ImportProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 99, // 89: openshell.v1.ImportProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile - 78, // 90: openshell.v1.UpdateProviderProfilesRequest.profile:type_name -> openshell.v1.ProviderProfileImportItem - 79, // 91: openshell.v1.UpdateProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 99, // 92: openshell.v1.UpdateProviderProfilesResponse.profile:type_name -> openshell.v1.ProviderProfile - 78, // 93: openshell.v1.LintProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem - 79, // 94: openshell.v1.LintProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 113, // 95: openshell.v1.StaticCredentialBinding.endpoints:type_name -> openshell.v1.StaticCredentialEndpointBinding - 215, // 96: openshell.v1.GetSandboxProviderEnvironmentResponse.environment:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry - 216, // 97: openshell.v1.GetSandboxProviderEnvironmentResponse.credential_expires_at_ms:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpiresAtMsEntry - 217, // 98: openshell.v1.GetSandboxProviderEnvironmentResponse.dynamic_credentials:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry - 218, // 99: openshell.v1.GetSandboxProviderEnvironmentResponse.static_credential_bindings:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry - 226, // 100: openshell.v1.UpdateConfigRequest.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 232, // 101: openshell.v1.UpdateConfigRequest.setting_value:type_name -> openshell.sandbox.v1.SettingValue - 119, // 102: openshell.v1.UpdateConfigRequest.merge_operations:type_name -> openshell.v1.PolicyMergeOperation - 219, // 103: openshell.v1.UpdateConfigRequest.annotations:type_name -> openshell.v1.UpdateConfigRequest.AnnotationsEntry - 120, // 104: openshell.v1.PolicyMergeOperation.add_rule:type_name -> openshell.v1.AddNetworkRule - 121, // 105: openshell.v1.PolicyMergeOperation.remove_endpoint:type_name -> openshell.v1.RemoveNetworkEndpoint - 122, // 106: openshell.v1.PolicyMergeOperation.remove_rule:type_name -> openshell.v1.RemoveNetworkRule - 123, // 107: openshell.v1.PolicyMergeOperation.add_deny_rules:type_name -> openshell.v1.AddDenyRules - 124, // 108: openshell.v1.PolicyMergeOperation.add_allow_rules:type_name -> openshell.v1.AddAllowRules - 125, // 109: openshell.v1.PolicyMergeOperation.remove_binary:type_name -> openshell.v1.RemoveNetworkBinary - 233, // 110: openshell.v1.AddNetworkRule.rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 234, // 111: openshell.v1.AddDenyRules.deny_rules:type_name -> openshell.sandbox.v1.L7DenyRule - 235, // 112: openshell.v1.AddAllowRules.rules:type_name -> openshell.sandbox.v1.L7Rule - 220, // 113: openshell.v1.UpdateConfigResponse.annotations:type_name -> openshell.v1.UpdateConfigResponse.AnnotationsEntry - 133, // 114: openshell.v1.GetSandboxPolicyStatusResponse.revision:type_name -> openshell.v1.SandboxPolicyRevision - 133, // 115: openshell.v1.ListSandboxPoliciesResponse.revisions:type_name -> openshell.v1.SandboxPolicyRevision + 85, // 78: openshell.v1.ProviderProfile.credentials:type_name -> openshell.v1.ProviderProfileCredential + 240, // 79: openshell.v1.ProviderProfile.endpoints:type_name -> openshell.sandbox.v1.NetworkEndpoint + 241, // 80: openshell.v1.ProviderProfile.binaries:type_name -> openshell.sandbox.v1.NetworkBinary + 90, // 81: openshell.v1.ProviderProfile.discovery:type_name -> openshell.v1.ProviderProfileDiscovery + 224, // 82: openshell.v1.ProviderProfile.annotations:type_name -> openshell.v1.ProviderProfile.AnnotationsEntry + 235, // 83: openshell.v1.StoredProviderProfile.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 101, // 84: openshell.v1.StoredProviderProfile.profile:type_name -> openshell.v1.ProviderProfile + 101, // 85: openshell.v1.ProviderProfileResponse.profile:type_name -> openshell.v1.ProviderProfile + 101, // 86: openshell.v1.ListProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile + 80, // 87: openshell.v1.ImportProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem + 81, // 88: openshell.v1.ImportProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic + 101, // 89: openshell.v1.ImportProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile + 80, // 90: openshell.v1.UpdateProviderProfilesRequest.profile:type_name -> openshell.v1.ProviderProfileImportItem + 81, // 91: openshell.v1.UpdateProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic + 101, // 92: openshell.v1.UpdateProviderProfilesResponse.profile:type_name -> openshell.v1.ProviderProfile + 80, // 93: openshell.v1.LintProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem + 81, // 94: openshell.v1.LintProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic + 115, // 95: openshell.v1.StaticCredentialBinding.endpoints:type_name -> openshell.v1.StaticCredentialEndpointBinding + 225, // 96: openshell.v1.ProviderEnvironmentSnapshot.environment:type_name -> openshell.v1.ProviderEnvironmentSnapshot.EnvironmentEntry + 226, // 97: openshell.v1.ProviderEnvironmentSnapshot.credential_expires_at_ms:type_name -> openshell.v1.ProviderEnvironmentSnapshot.CredentialExpiresAtMsEntry + 227, // 98: openshell.v1.ProviderEnvironmentSnapshot.dynamic_credentials:type_name -> openshell.v1.ProviderEnvironmentSnapshot.DynamicCredentialsEntry + 228, // 99: openshell.v1.ProviderEnvironmentSnapshot.static_credential_bindings:type_name -> openshell.v1.ProviderEnvironmentSnapshot.StaticCredentialBindingsEntry + 236, // 100: openshell.v1.UpdateConfigRequest.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 242, // 101: openshell.v1.UpdateConfigRequest.setting_value:type_name -> openshell.sandbox.v1.SettingValue + 121, // 102: openshell.v1.UpdateConfigRequest.merge_operations:type_name -> openshell.v1.PolicyMergeOperation + 229, // 103: openshell.v1.UpdateConfigRequest.annotations:type_name -> openshell.v1.UpdateConfigRequest.AnnotationsEntry + 122, // 104: openshell.v1.PolicyMergeOperation.add_rule:type_name -> openshell.v1.AddNetworkRule + 123, // 105: openshell.v1.PolicyMergeOperation.remove_endpoint:type_name -> openshell.v1.RemoveNetworkEndpoint + 124, // 106: openshell.v1.PolicyMergeOperation.remove_rule:type_name -> openshell.v1.RemoveNetworkRule + 125, // 107: openshell.v1.PolicyMergeOperation.add_deny_rules:type_name -> openshell.v1.AddDenyRules + 126, // 108: openshell.v1.PolicyMergeOperation.add_allow_rules:type_name -> openshell.v1.AddAllowRules + 127, // 109: openshell.v1.PolicyMergeOperation.remove_binary:type_name -> openshell.v1.RemoveNetworkBinary + 243, // 110: openshell.v1.AddNetworkRule.rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 244, // 111: openshell.v1.AddDenyRules.deny_rules:type_name -> openshell.sandbox.v1.L7DenyRule + 245, // 112: openshell.v1.AddAllowRules.rules:type_name -> openshell.sandbox.v1.L7Rule + 230, // 113: openshell.v1.UpdateConfigResponse.annotations:type_name -> openshell.v1.UpdateConfigResponse.AnnotationsEntry + 135, // 114: openshell.v1.GetSandboxPolicyStatusResponse.revision:type_name -> openshell.v1.SandboxPolicyRevision + 135, // 115: openshell.v1.ListSandboxPoliciesResponse.revisions:type_name -> openshell.v1.SandboxPolicyRevision 4, // 116: openshell.v1.ReportPolicyStatusRequest.status:type_name -> openshell.v1.PolicyStatus 4, // 117: openshell.v1.SandboxPolicyRevision.status:type_name -> openshell.v1.PolicyStatus - 226, // 118: openshell.v1.SandboxPolicyRevision.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 221, // 119: openshell.v1.SandboxPolicyRevision.provenance:type_name -> openshell.v1.SandboxPolicyRevision.ProvenanceEntry - 67, // 120: openshell.v1.PushSandboxLogsRequest.logs:type_name -> openshell.v1.SandboxLogLine - 67, // 121: openshell.v1.GetSandboxLogsResponse.logs:type_name -> openshell.v1.SandboxLogLine - 140, // 122: openshell.v1.SupervisorMessage.hello:type_name -> openshell.v1.SupervisorHello - 143, // 123: openshell.v1.SupervisorMessage.heartbeat:type_name -> openshell.v1.SupervisorHeartbeat - 152, // 124: openshell.v1.SupervisorMessage.relay_open_result:type_name -> openshell.v1.RelayOpenResult - 153, // 125: openshell.v1.SupervisorMessage.relay_close:type_name -> openshell.v1.RelayClose - 141, // 126: openshell.v1.GatewayMessage.session_accepted:type_name -> openshell.v1.SessionAccepted - 142, // 127: openshell.v1.GatewayMessage.session_rejected:type_name -> openshell.v1.SessionRejected - 144, // 128: openshell.v1.GatewayMessage.heartbeat:type_name -> openshell.v1.GatewayHeartbeat - 147, // 129: openshell.v1.GatewayMessage.relay_open:type_name -> openshell.v1.RelayOpen - 153, // 130: openshell.v1.GatewayMessage.relay_close:type_name -> openshell.v1.RelayClose - 148, // 131: openshell.v1.RelayOpen.ssh:type_name -> openshell.v1.SshRelayTarget - 149, // 132: openshell.v1.RelayOpen.tcp:type_name -> openshell.v1.TcpRelayTarget - 150, // 133: openshell.v1.RelayFrame.init:type_name -> openshell.v1.RelayInit - 154, // 134: openshell.v1.DenialSummary.l7_request_samples:type_name -> openshell.v1.L7RequestSample - 156, // 135: openshell.v1.NetworkActivitySummary.denials_by_group:type_name -> openshell.v1.DenialGroupCount - 233, // 136: openshell.v1.PolicyChunk.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 226, // 137: openshell.v1.PolicyChunk.current_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 226, // 138: openshell.v1.PolicyChunk.candidate_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 155, // 139: openshell.v1.SubmitPolicyAnalysisRequest.summaries:type_name -> openshell.v1.DenialSummary - 158, // 140: openshell.v1.SubmitPolicyAnalysisRequest.proposed_chunks:type_name -> openshell.v1.PolicyChunk - 157, // 141: openshell.v1.SubmitPolicyAnalysisRequest.network_activity_summaries:type_name -> openshell.v1.NetworkActivitySummary - 158, // 142: openshell.v1.GetDraftPolicyResponse.chunks:type_name -> openshell.v1.PolicyChunk - 168, // 143: openshell.v1.ApproveAllDraftChunksRequest.approvals:type_name -> openshell.v1.DraftChunkApproval - 233, // 144: openshell.v1.EditDraftChunkRequest.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 178, // 145: openshell.v1.GetDraftHistoryResponse.entries:type_name -> openshell.v1.DraftHistoryEntry - 226, // 146: openshell.v1.PolicyRevisionPayload.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 222, // 147: openshell.v1.PolicyRevisionPayload.provenance:type_name -> openshell.v1.PolicyRevisionPayload.ProvenanceEntry - 233, // 148: openshell.v1.DraftChunkPayload.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 226, // 149: openshell.v1.DraftChunkPayload.current_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 226, // 150: openshell.v1.DraftChunkPayload.candidate_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 223, // 151: openshell.v1.StoredPolicyRevision.provenance:type_name -> openshell.v1.StoredPolicyRevision.ProvenanceEntry - 226, // 152: openshell.v1.StoredDraftChunk.current_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 226, // 153: openshell.v1.StoredDraftChunk.candidate_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 224, // 154: openshell.v1.CreateWorkspaceRequest.labels:type_name -> openshell.v1.CreateWorkspaceRequest.LabelsEntry - 236, // 155: openshell.v1.CreateWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace - 236, // 156: openshell.v1.GetWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace - 236, // 157: openshell.v1.ListWorkspacesResponse.workspaces:type_name -> openshell.datamodel.v1.Workspace - 225, // 158: openshell.v1.WorkspaceMember.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 6, // 159: openshell.v1.WorkspaceMember.role:type_name -> openshell.v1.WorkspaceRole - 6, // 160: openshell.v1.AddWorkspaceMemberRequest.role:type_name -> openshell.v1.WorkspaceRole - 192, // 161: openshell.v1.AddWorkspaceMemberResponse.member:type_name -> openshell.v1.WorkspaceMember - 192, // 162: openshell.v1.ListWorkspaceMembersResponse.members:type_name -> openshell.v1.WorkspaceMember - 229, // 163: openshell.v1.StoredProviderCredentialRefreshState.SecretMaterialHandlesEntry.value:type_name -> openshell.datamodel.v1.CredentialHandle - 83, // 164: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential - 114, // 165: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry.value:type_name -> openshell.v1.StaticCredentialBinding - 12, // 166: openshell.v1.OpenShell.Health:input_type -> openshell.v1.HealthRequest - 14, // 167: openshell.v1.OpenShell.GetCurrentUser:input_type -> openshell.v1.GetCurrentUserRequest - 16, // 168: openshell.v1.OpenShell.GetGatewayInfo:input_type -> openshell.v1.GetGatewayInfoRequest - 28, // 169: openshell.v1.OpenShell.CreateSandbox:input_type -> openshell.v1.CreateSandboxRequest - 29, // 170: openshell.v1.OpenShell.GetSandbox:input_type -> openshell.v1.GetSandboxRequest - 30, // 171: openshell.v1.OpenShell.ListSandboxes:input_type -> openshell.v1.ListSandboxesRequest - 31, // 172: openshell.v1.OpenShell.ListSandboxProviders:input_type -> openshell.v1.ListSandboxProvidersRequest - 32, // 173: openshell.v1.OpenShell.AttachSandboxProvider:input_type -> openshell.v1.AttachSandboxProviderRequest - 33, // 174: openshell.v1.OpenShell.DetachSandboxProvider:input_type -> openshell.v1.DetachSandboxProviderRequest - 34, // 175: openshell.v1.OpenShell.DeleteSandbox:input_type -> openshell.v1.DeleteSandboxRequest - 35, // 176: openshell.v1.OpenShell.StopSandbox:input_type -> openshell.v1.StopSandboxRequest - 36, // 177: openshell.v1.OpenShell.StartSandbox:input_type -> openshell.v1.StartSandboxRequest - 43, // 178: openshell.v1.OpenShell.CreateSshSession:input_type -> openshell.v1.CreateSshSessionRequest - 45, // 179: openshell.v1.OpenShell.ExposeService:input_type -> openshell.v1.ExposeServiceRequest - 46, // 180: openshell.v1.OpenShell.GetService:input_type -> openshell.v1.GetServiceRequest - 47, // 181: openshell.v1.OpenShell.ListServices:input_type -> openshell.v1.ListServicesRequest - 49, // 182: openshell.v1.OpenShell.DeleteService:input_type -> openshell.v1.DeleteServiceRequest - 53, // 183: openshell.v1.OpenShell.RevokeSshSession:input_type -> openshell.v1.RevokeSshSessionRequest - 55, // 184: openshell.v1.OpenShell.ExecSandbox:input_type -> openshell.v1.ExecSandboxRequest - 61, // 185: openshell.v1.OpenShell.ForwardTcp:input_type -> openshell.v1.TcpForwardFrame - 62, // 186: openshell.v1.OpenShell.ExecSandboxInteractive:input_type -> openshell.v1.ExecSandboxInput - 69, // 187: openshell.v1.OpenShell.CreateProvider:input_type -> openshell.v1.CreateProviderRequest - 70, // 188: openshell.v1.OpenShell.GetProvider:input_type -> openshell.v1.GetProviderRequest - 71, // 189: openshell.v1.OpenShell.ListProviders:input_type -> openshell.v1.ListProvidersRequest - 76, // 190: openshell.v1.OpenShell.ListProviderProfiles:input_type -> openshell.v1.ListProviderProfilesRequest - 77, // 191: openshell.v1.OpenShell.GetProviderProfile:input_type -> openshell.v1.GetProviderProfileRequest - 103, // 192: openshell.v1.OpenShell.ImportProviderProfiles:input_type -> openshell.v1.ImportProviderProfilesRequest - 105, // 193: openshell.v1.OpenShell.UpdateProviderProfiles:input_type -> openshell.v1.UpdateProviderProfilesRequest - 107, // 194: openshell.v1.OpenShell.LintProviderProfiles:input_type -> openshell.v1.LintProviderProfilesRequest - 72, // 195: openshell.v1.OpenShell.UpdateProvider:input_type -> openshell.v1.UpdateProviderRequest - 91, // 196: openshell.v1.OpenShell.GetProviderRefreshStatus:input_type -> openshell.v1.GetProviderRefreshStatusRequest - 93, // 197: openshell.v1.OpenShell.ConfigureProviderRefresh:input_type -> openshell.v1.ConfigureProviderRefreshRequest - 95, // 198: openshell.v1.OpenShell.RotateProviderCredential:input_type -> openshell.v1.RotateProviderCredentialRequest - 97, // 199: openshell.v1.OpenShell.DeleteProviderRefresh:input_type -> openshell.v1.DeleteProviderRefreshRequest - 73, // 200: openshell.v1.OpenShell.DeleteProvider:input_type -> openshell.v1.DeleteProviderRequest - 110, // 201: openshell.v1.OpenShell.DeleteProviderProfile:input_type -> openshell.v1.DeleteProviderProfileRequest - 237, // 202: openshell.v1.OpenShell.GetSandboxConfig:input_type -> openshell.sandbox.v1.GetSandboxConfigRequest - 238, // 203: openshell.v1.OpenShell.GetGatewayConfig:input_type -> openshell.sandbox.v1.GetGatewayConfigRequest - 118, // 204: openshell.v1.OpenShell.UpdateConfig:input_type -> openshell.v1.UpdateConfigRequest - 127, // 205: openshell.v1.OpenShell.GetSandboxPolicyStatus:input_type -> openshell.v1.GetSandboxPolicyStatusRequest - 129, // 206: openshell.v1.OpenShell.ListSandboxPolicies:input_type -> openshell.v1.ListSandboxPoliciesRequest - 131, // 207: openshell.v1.OpenShell.ReportPolicyStatus:input_type -> openshell.v1.ReportPolicyStatusRequest - 112, // 208: openshell.v1.OpenShell.GetSandboxProviderEnvironment:input_type -> openshell.v1.GetSandboxProviderEnvironmentRequest - 116, // 209: openshell.v1.OpenShell.ExchangeProviderSubjectToken:input_type -> openshell.v1.ExchangeProviderSubjectTokenRequest - 134, // 210: openshell.v1.OpenShell.GetSandboxLogs:input_type -> openshell.v1.GetSandboxLogsRequest - 135, // 211: openshell.v1.OpenShell.PushSandboxLogs:input_type -> openshell.v1.PushSandboxLogsRequest - 138, // 212: openshell.v1.OpenShell.ConnectSupervisor:input_type -> openshell.v1.SupervisorMessage - 145, // 213: openshell.v1.OpenShell.ReportMainProcessExit:input_type -> openshell.v1.ReportMainProcessExitRequest - 151, // 214: openshell.v1.OpenShell.RelayStream:input_type -> openshell.v1.RelayFrame - 65, // 215: openshell.v1.OpenShell.WatchSandbox:input_type -> openshell.v1.WatchSandboxRequest - 160, // 216: openshell.v1.OpenShell.SubmitPolicyAnalysis:input_type -> openshell.v1.SubmitPolicyAnalysisRequest - 162, // 217: openshell.v1.OpenShell.GetDraftPolicy:input_type -> openshell.v1.GetDraftPolicyRequest - 164, // 218: openshell.v1.OpenShell.ApproveDraftChunk:input_type -> openshell.v1.ApproveDraftChunkRequest - 166, // 219: openshell.v1.OpenShell.RejectDraftChunk:input_type -> openshell.v1.RejectDraftChunkRequest - 169, // 220: openshell.v1.OpenShell.ApproveAllDraftChunks:input_type -> openshell.v1.ApproveAllDraftChunksRequest - 171, // 221: openshell.v1.OpenShell.EditDraftChunk:input_type -> openshell.v1.EditDraftChunkRequest - 173, // 222: openshell.v1.OpenShell.UndoDraftChunk:input_type -> openshell.v1.UndoDraftChunkRequest - 175, // 223: openshell.v1.OpenShell.ClearDraftChunks:input_type -> openshell.v1.ClearDraftChunksRequest - 177, // 224: openshell.v1.OpenShell.GetDraftHistory:input_type -> openshell.v1.GetDraftHistoryRequest - 8, // 225: openshell.v1.OpenShell.IssueSandboxToken:input_type -> openshell.v1.IssueSandboxTokenRequest - 10, // 226: openshell.v1.OpenShell.RefreshSandboxToken:input_type -> openshell.v1.RefreshSandboxTokenRequest - 184, // 227: openshell.v1.OpenShell.CreateWorkspace:input_type -> openshell.v1.CreateWorkspaceRequest - 186, // 228: openshell.v1.OpenShell.GetWorkspace:input_type -> openshell.v1.GetWorkspaceRequest - 188, // 229: openshell.v1.OpenShell.ListWorkspaces:input_type -> openshell.v1.ListWorkspacesRequest - 190, // 230: openshell.v1.OpenShell.DeleteWorkspace:input_type -> openshell.v1.DeleteWorkspaceRequest - 193, // 231: openshell.v1.OpenShell.AddWorkspaceMember:input_type -> openshell.v1.AddWorkspaceMemberRequest - 195, // 232: openshell.v1.OpenShell.RemoveWorkspaceMember:input_type -> openshell.v1.RemoveWorkspaceMemberRequest - 197, // 233: openshell.v1.OpenShell.ListWorkspaceMembers:input_type -> openshell.v1.ListWorkspaceMembersRequest - 13, // 234: openshell.v1.OpenShell.Health:output_type -> openshell.v1.HealthResponse - 15, // 235: openshell.v1.OpenShell.GetCurrentUser:output_type -> openshell.v1.GetCurrentUserResponse - 17, // 236: openshell.v1.OpenShell.GetGatewayInfo:output_type -> openshell.v1.GetGatewayInfoResponse - 37, // 237: openshell.v1.OpenShell.CreateSandbox:output_type -> openshell.v1.SandboxResponse - 37, // 238: openshell.v1.OpenShell.GetSandbox:output_type -> openshell.v1.SandboxResponse - 38, // 239: openshell.v1.OpenShell.ListSandboxes:output_type -> openshell.v1.ListSandboxesResponse - 39, // 240: openshell.v1.OpenShell.ListSandboxProviders:output_type -> openshell.v1.ListSandboxProvidersResponse - 40, // 241: openshell.v1.OpenShell.AttachSandboxProvider:output_type -> openshell.v1.AttachSandboxProviderResponse - 41, // 242: openshell.v1.OpenShell.DetachSandboxProvider:output_type -> openshell.v1.DetachSandboxProviderResponse - 42, // 243: openshell.v1.OpenShell.DeleteSandbox:output_type -> openshell.v1.DeleteSandboxResponse - 37, // 244: openshell.v1.OpenShell.StopSandbox:output_type -> openshell.v1.SandboxResponse - 37, // 245: openshell.v1.OpenShell.StartSandbox:output_type -> openshell.v1.SandboxResponse - 44, // 246: openshell.v1.OpenShell.CreateSshSession:output_type -> openshell.v1.CreateSshSessionResponse - 52, // 247: openshell.v1.OpenShell.ExposeService:output_type -> openshell.v1.ServiceEndpointResponse - 52, // 248: openshell.v1.OpenShell.GetService:output_type -> openshell.v1.ServiceEndpointResponse - 48, // 249: openshell.v1.OpenShell.ListServices:output_type -> openshell.v1.ListServicesResponse - 50, // 250: openshell.v1.OpenShell.DeleteService:output_type -> openshell.v1.DeleteServiceResponse - 54, // 251: openshell.v1.OpenShell.RevokeSshSession:output_type -> openshell.v1.RevokeSshSessionResponse - 59, // 252: openshell.v1.OpenShell.ExecSandbox:output_type -> openshell.v1.ExecSandboxEvent - 61, // 253: openshell.v1.OpenShell.ForwardTcp:output_type -> openshell.v1.TcpForwardFrame - 59, // 254: openshell.v1.OpenShell.ExecSandboxInteractive:output_type -> openshell.v1.ExecSandboxEvent - 74, // 255: openshell.v1.OpenShell.CreateProvider:output_type -> openshell.v1.ProviderResponse - 74, // 256: openshell.v1.OpenShell.GetProvider:output_type -> openshell.v1.ProviderResponse - 75, // 257: openshell.v1.OpenShell.ListProviders:output_type -> openshell.v1.ListProvidersResponse - 102, // 258: openshell.v1.OpenShell.ListProviderProfiles:output_type -> openshell.v1.ListProviderProfilesResponse - 101, // 259: openshell.v1.OpenShell.GetProviderProfile:output_type -> openshell.v1.ProviderProfileResponse - 104, // 260: openshell.v1.OpenShell.ImportProviderProfiles:output_type -> openshell.v1.ImportProviderProfilesResponse - 106, // 261: openshell.v1.OpenShell.UpdateProviderProfiles:output_type -> openshell.v1.UpdateProviderProfilesResponse - 108, // 262: openshell.v1.OpenShell.LintProviderProfiles:output_type -> openshell.v1.LintProviderProfilesResponse - 74, // 263: openshell.v1.OpenShell.UpdateProvider:output_type -> openshell.v1.ProviderResponse - 92, // 264: openshell.v1.OpenShell.GetProviderRefreshStatus:output_type -> openshell.v1.GetProviderRefreshStatusResponse - 94, // 265: openshell.v1.OpenShell.ConfigureProviderRefresh:output_type -> openshell.v1.ConfigureProviderRefreshResponse - 96, // 266: openshell.v1.OpenShell.RotateProviderCredential:output_type -> openshell.v1.RotateProviderCredentialResponse - 98, // 267: openshell.v1.OpenShell.DeleteProviderRefresh:output_type -> openshell.v1.DeleteProviderRefreshResponse - 109, // 268: openshell.v1.OpenShell.DeleteProvider:output_type -> openshell.v1.DeleteProviderResponse - 111, // 269: openshell.v1.OpenShell.DeleteProviderProfile:output_type -> openshell.v1.DeleteProviderProfileResponse - 239, // 270: openshell.v1.OpenShell.GetSandboxConfig:output_type -> openshell.sandbox.v1.GetSandboxConfigResponse - 240, // 271: openshell.v1.OpenShell.GetGatewayConfig:output_type -> openshell.sandbox.v1.GetGatewayConfigResponse - 126, // 272: openshell.v1.OpenShell.UpdateConfig:output_type -> openshell.v1.UpdateConfigResponse - 128, // 273: openshell.v1.OpenShell.GetSandboxPolicyStatus:output_type -> openshell.v1.GetSandboxPolicyStatusResponse - 130, // 274: openshell.v1.OpenShell.ListSandboxPolicies:output_type -> openshell.v1.ListSandboxPoliciesResponse - 132, // 275: openshell.v1.OpenShell.ReportPolicyStatus:output_type -> openshell.v1.ReportPolicyStatusResponse - 115, // 276: openshell.v1.OpenShell.GetSandboxProviderEnvironment:output_type -> openshell.v1.GetSandboxProviderEnvironmentResponse - 117, // 277: openshell.v1.OpenShell.ExchangeProviderSubjectToken:output_type -> openshell.v1.ExchangeProviderSubjectTokenResponse - 137, // 278: openshell.v1.OpenShell.GetSandboxLogs:output_type -> openshell.v1.GetSandboxLogsResponse - 136, // 279: openshell.v1.OpenShell.PushSandboxLogs:output_type -> openshell.v1.PushSandboxLogsResponse - 139, // 280: openshell.v1.OpenShell.ConnectSupervisor:output_type -> openshell.v1.GatewayMessage - 146, // 281: openshell.v1.OpenShell.ReportMainProcessExit:output_type -> openshell.v1.ReportMainProcessExitResponse - 151, // 282: openshell.v1.OpenShell.RelayStream:output_type -> openshell.v1.RelayFrame - 66, // 283: openshell.v1.OpenShell.WatchSandbox:output_type -> openshell.v1.SandboxStreamEvent - 161, // 284: openshell.v1.OpenShell.SubmitPolicyAnalysis:output_type -> openshell.v1.SubmitPolicyAnalysisResponse - 163, // 285: openshell.v1.OpenShell.GetDraftPolicy:output_type -> openshell.v1.GetDraftPolicyResponse - 165, // 286: openshell.v1.OpenShell.ApproveDraftChunk:output_type -> openshell.v1.ApproveDraftChunkResponse - 167, // 287: openshell.v1.OpenShell.RejectDraftChunk:output_type -> openshell.v1.RejectDraftChunkResponse - 170, // 288: openshell.v1.OpenShell.ApproveAllDraftChunks:output_type -> openshell.v1.ApproveAllDraftChunksResponse - 172, // 289: openshell.v1.OpenShell.EditDraftChunk:output_type -> openshell.v1.EditDraftChunkResponse - 174, // 290: openshell.v1.OpenShell.UndoDraftChunk:output_type -> openshell.v1.UndoDraftChunkResponse - 176, // 291: openshell.v1.OpenShell.ClearDraftChunks:output_type -> openshell.v1.ClearDraftChunksResponse - 179, // 292: openshell.v1.OpenShell.GetDraftHistory:output_type -> openshell.v1.GetDraftHistoryResponse - 9, // 293: openshell.v1.OpenShell.IssueSandboxToken:output_type -> openshell.v1.IssueSandboxTokenResponse - 11, // 294: openshell.v1.OpenShell.RefreshSandboxToken:output_type -> openshell.v1.RefreshSandboxTokenResponse - 185, // 295: openshell.v1.OpenShell.CreateWorkspace:output_type -> openshell.v1.CreateWorkspaceResponse - 187, // 296: openshell.v1.OpenShell.GetWorkspace:output_type -> openshell.v1.GetWorkspaceResponse - 189, // 297: openshell.v1.OpenShell.ListWorkspaces:output_type -> openshell.v1.ListWorkspacesResponse - 191, // 298: openshell.v1.OpenShell.DeleteWorkspace:output_type -> openshell.v1.DeleteWorkspaceResponse - 194, // 299: openshell.v1.OpenShell.AddWorkspaceMember:output_type -> openshell.v1.AddWorkspaceMemberResponse - 196, // 300: openshell.v1.OpenShell.RemoveWorkspaceMember:output_type -> openshell.v1.RemoveWorkspaceMemberResponse - 198, // 301: openshell.v1.OpenShell.ListWorkspaceMembers:output_type -> openshell.v1.ListWorkspaceMembersResponse - 234, // [234:302] is the sub-list for method output_type - 166, // [166:234] is the sub-list for method input_type - 166, // [166:166] is the sub-list for extension type_name - 166, // [166:166] is the sub-list for extension extendee - 0, // [0:166] is the sub-list for field type_name + 236, // 118: openshell.v1.SandboxPolicyRevision.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 231, // 119: openshell.v1.SandboxPolicyRevision.provenance:type_name -> openshell.v1.SandboxPolicyRevision.ProvenanceEntry + 69, // 120: openshell.v1.PushSandboxLogsRequest.logs:type_name -> openshell.v1.SandboxLogLine + 69, // 121: openshell.v1.GetSandboxLogsResponse.logs:type_name -> openshell.v1.SandboxLogLine + 142, // 122: openshell.v1.SupervisorMessage.hello:type_name -> openshell.v1.SupervisorHello + 153, // 123: openshell.v1.SupervisorMessage.heartbeat:type_name -> openshell.v1.SupervisorHeartbeat + 162, // 124: openshell.v1.SupervisorMessage.relay_open_result:type_name -> openshell.v1.RelayOpenResult + 163, // 125: openshell.v1.SupervisorMessage.relay_close:type_name -> openshell.v1.RelayClose + 145, // 126: openshell.v1.SupervisorMessage.bootstrap_result:type_name -> openshell.v1.ConfigBootstrapResult + 150, // 127: openshell.v1.SupervisorMessage.config_update_result:type_name -> openshell.v1.ConfigUpdateResult + 151, // 128: openshell.v1.SupervisorMessage.runtime_ready:type_name -> openshell.v1.SupervisorRuntimeReady + 143, // 129: openshell.v1.GatewayMessage.session_accepted:type_name -> openshell.v1.SessionAccepted + 152, // 130: openshell.v1.GatewayMessage.session_rejected:type_name -> openshell.v1.SessionRejected + 154, // 131: openshell.v1.GatewayMessage.heartbeat:type_name -> openshell.v1.GatewayHeartbeat + 157, // 132: openshell.v1.GatewayMessage.relay_open:type_name -> openshell.v1.RelayOpen + 163, // 133: openshell.v1.GatewayMessage.relay_close:type_name -> openshell.v1.RelayClose + 146, // 134: openshell.v1.GatewayMessage.config_update:type_name -> openshell.v1.ConfigUpdate + 144, // 135: openshell.v1.SessionAccepted.bootstrap:type_name -> openshell.v1.ConfigBootstrap + 246, // 136: openshell.v1.ConfigBootstrap.sandbox_config:type_name -> openshell.sandbox.v1.SandboxConfigSnapshot + 117, // 137: openshell.v1.ConfigBootstrap.provider_environment:type_name -> openshell.v1.ProviderEnvironmentSnapshot + 247, // 138: openshell.v1.ConfigBootstrap.inference_bundle:type_name -> openshell.inference.v1.InferenceBundleSnapshot + 5, // 139: openshell.v1.ConfigBootstrapResult.status:type_name -> openshell.v1.ConfigBootstrapStatus + 6, // 140: openshell.v1.ConfigBootstrapResult.sandbox_config_outcome:type_name -> openshell.v1.ConfigApplyOutcome + 6, // 141: openshell.v1.ConfigBootstrapResult.provider_environment_outcome:type_name -> openshell.v1.ConfigApplyOutcome + 6, // 142: openshell.v1.ConfigBootstrapResult.inference_bundle_outcome:type_name -> openshell.v1.ConfigApplyOutcome + 147, // 143: openshell.v1.ConfigUpdate.sandbox_config:type_name -> openshell.v1.SandboxConfigUpdate + 148, // 144: openshell.v1.ConfigUpdate.provider_environment:type_name -> openshell.v1.ProviderEnvironmentUpdate + 149, // 145: openshell.v1.ConfigUpdate.inference_bundle:type_name -> openshell.v1.InferenceBundleUpdate + 246, // 146: openshell.v1.SandboxConfigUpdate.snapshot:type_name -> openshell.sandbox.v1.SandboxConfigSnapshot + 117, // 147: openshell.v1.ProviderEnvironmentUpdate.snapshot:type_name -> openshell.v1.ProviderEnvironmentSnapshot + 247, // 148: openshell.v1.InferenceBundleUpdate.snapshot:type_name -> openshell.inference.v1.InferenceBundleSnapshot + 6, // 149: openshell.v1.ConfigUpdateResult.outcome:type_name -> openshell.v1.ConfigApplyOutcome + 158, // 150: openshell.v1.RelayOpen.ssh:type_name -> openshell.v1.SshRelayTarget + 159, // 151: openshell.v1.RelayOpen.tcp:type_name -> openshell.v1.TcpRelayTarget + 160, // 152: openshell.v1.RelayFrame.init:type_name -> openshell.v1.RelayInit + 164, // 153: openshell.v1.DenialSummary.l7_request_samples:type_name -> openshell.v1.L7RequestSample + 166, // 154: openshell.v1.NetworkActivitySummary.denials_by_group:type_name -> openshell.v1.DenialGroupCount + 243, // 155: openshell.v1.PolicyChunk.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 236, // 156: openshell.v1.PolicyChunk.current_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 236, // 157: openshell.v1.PolicyChunk.candidate_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 165, // 158: openshell.v1.SubmitPolicyAnalysisRequest.summaries:type_name -> openshell.v1.DenialSummary + 168, // 159: openshell.v1.SubmitPolicyAnalysisRequest.proposed_chunks:type_name -> openshell.v1.PolicyChunk + 167, // 160: openshell.v1.SubmitPolicyAnalysisRequest.network_activity_summaries:type_name -> openshell.v1.NetworkActivitySummary + 168, // 161: openshell.v1.GetDraftPolicyResponse.chunks:type_name -> openshell.v1.PolicyChunk + 178, // 162: openshell.v1.ApproveAllDraftChunksRequest.approvals:type_name -> openshell.v1.DraftChunkApproval + 243, // 163: openshell.v1.EditDraftChunkRequest.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 188, // 164: openshell.v1.GetDraftHistoryResponse.entries:type_name -> openshell.v1.DraftHistoryEntry + 236, // 165: openshell.v1.PolicyRevisionPayload.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 232, // 166: openshell.v1.PolicyRevisionPayload.provenance:type_name -> openshell.v1.PolicyRevisionPayload.ProvenanceEntry + 243, // 167: openshell.v1.DraftChunkPayload.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule + 236, // 168: openshell.v1.DraftChunkPayload.current_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 236, // 169: openshell.v1.DraftChunkPayload.candidate_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 233, // 170: openshell.v1.StoredPolicyRevision.provenance:type_name -> openshell.v1.StoredPolicyRevision.ProvenanceEntry + 236, // 171: openshell.v1.StoredDraftChunk.current_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 236, // 172: openshell.v1.StoredDraftChunk.candidate_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 234, // 173: openshell.v1.CreateWorkspaceRequest.labels:type_name -> openshell.v1.CreateWorkspaceRequest.LabelsEntry + 248, // 174: openshell.v1.CreateWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace + 248, // 175: openshell.v1.GetWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace + 248, // 176: openshell.v1.ListWorkspacesResponse.workspaces:type_name -> openshell.datamodel.v1.Workspace + 235, // 177: openshell.v1.WorkspaceMember.metadata:type_name -> openshell.datamodel.v1.ObjectMeta + 8, // 178: openshell.v1.WorkspaceMember.role:type_name -> openshell.v1.WorkspaceRole + 8, // 179: openshell.v1.AddWorkspaceMemberRequest.role:type_name -> openshell.v1.WorkspaceRole + 202, // 180: openshell.v1.AddWorkspaceMemberResponse.member:type_name -> openshell.v1.WorkspaceMember + 202, // 181: openshell.v1.ListWorkspaceMembersResponse.members:type_name -> openshell.v1.WorkspaceMember + 239, // 182: openshell.v1.StoredProviderCredentialRefreshState.SecretMaterialHandlesEntry.value:type_name -> openshell.datamodel.v1.CredentialHandle + 85, // 183: openshell.v1.ProviderEnvironmentSnapshot.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential + 116, // 184: openshell.v1.ProviderEnvironmentSnapshot.StaticCredentialBindingsEntry.value:type_name -> openshell.v1.StaticCredentialBinding + 14, // 185: openshell.v1.OpenShell.Health:input_type -> openshell.v1.HealthRequest + 16, // 186: openshell.v1.OpenShell.GetCurrentUser:input_type -> openshell.v1.GetCurrentUserRequest + 18, // 187: openshell.v1.OpenShell.GetGatewayInfo:input_type -> openshell.v1.GetGatewayInfoRequest + 30, // 188: openshell.v1.OpenShell.CreateSandbox:input_type -> openshell.v1.CreateSandboxRequest + 31, // 189: openshell.v1.OpenShell.GetSandbox:input_type -> openshell.v1.GetSandboxRequest + 32, // 190: openshell.v1.OpenShell.ListSandboxes:input_type -> openshell.v1.ListSandboxesRequest + 33, // 191: openshell.v1.OpenShell.ListSandboxProviders:input_type -> openshell.v1.ListSandboxProvidersRequest + 34, // 192: openshell.v1.OpenShell.AttachSandboxProvider:input_type -> openshell.v1.AttachSandboxProviderRequest + 35, // 193: openshell.v1.OpenShell.DetachSandboxProvider:input_type -> openshell.v1.DetachSandboxProviderRequest + 36, // 194: openshell.v1.OpenShell.DeleteSandbox:input_type -> openshell.v1.DeleteSandboxRequest + 37, // 195: openshell.v1.OpenShell.StopSandbox:input_type -> openshell.v1.StopSandboxRequest + 38, // 196: openshell.v1.OpenShell.StartSandbox:input_type -> openshell.v1.StartSandboxRequest + 45, // 197: openshell.v1.OpenShell.CreateSshSession:input_type -> openshell.v1.CreateSshSessionRequest + 47, // 198: openshell.v1.OpenShell.ExposeService:input_type -> openshell.v1.ExposeServiceRequest + 48, // 199: openshell.v1.OpenShell.GetService:input_type -> openshell.v1.GetServiceRequest + 49, // 200: openshell.v1.OpenShell.ListServices:input_type -> openshell.v1.ListServicesRequest + 51, // 201: openshell.v1.OpenShell.DeleteService:input_type -> openshell.v1.DeleteServiceRequest + 55, // 202: openshell.v1.OpenShell.RevokeSshSession:input_type -> openshell.v1.RevokeSshSessionRequest + 57, // 203: openshell.v1.OpenShell.ExecSandbox:input_type -> openshell.v1.ExecSandboxRequest + 63, // 204: openshell.v1.OpenShell.ForwardTcp:input_type -> openshell.v1.TcpForwardFrame + 64, // 205: openshell.v1.OpenShell.ExecSandboxInteractive:input_type -> openshell.v1.ExecSandboxInput + 71, // 206: openshell.v1.OpenShell.CreateProvider:input_type -> openshell.v1.CreateProviderRequest + 72, // 207: openshell.v1.OpenShell.GetProvider:input_type -> openshell.v1.GetProviderRequest + 73, // 208: openshell.v1.OpenShell.ListProviders:input_type -> openshell.v1.ListProvidersRequest + 78, // 209: openshell.v1.OpenShell.ListProviderProfiles:input_type -> openshell.v1.ListProviderProfilesRequest + 79, // 210: openshell.v1.OpenShell.GetProviderProfile:input_type -> openshell.v1.GetProviderProfileRequest + 105, // 211: openshell.v1.OpenShell.ImportProviderProfiles:input_type -> openshell.v1.ImportProviderProfilesRequest + 107, // 212: openshell.v1.OpenShell.UpdateProviderProfiles:input_type -> openshell.v1.UpdateProviderProfilesRequest + 109, // 213: openshell.v1.OpenShell.LintProviderProfiles:input_type -> openshell.v1.LintProviderProfilesRequest + 74, // 214: openshell.v1.OpenShell.UpdateProvider:input_type -> openshell.v1.UpdateProviderRequest + 93, // 215: openshell.v1.OpenShell.GetProviderRefreshStatus:input_type -> openshell.v1.GetProviderRefreshStatusRequest + 95, // 216: openshell.v1.OpenShell.ConfigureProviderRefresh:input_type -> openshell.v1.ConfigureProviderRefreshRequest + 97, // 217: openshell.v1.OpenShell.RotateProviderCredential:input_type -> openshell.v1.RotateProviderCredentialRequest + 99, // 218: openshell.v1.OpenShell.DeleteProviderRefresh:input_type -> openshell.v1.DeleteProviderRefreshRequest + 75, // 219: openshell.v1.OpenShell.DeleteProvider:input_type -> openshell.v1.DeleteProviderRequest + 112, // 220: openshell.v1.OpenShell.DeleteProviderProfile:input_type -> openshell.v1.DeleteProviderProfileRequest + 249, // 221: openshell.v1.OpenShell.GetSandboxConfig:input_type -> openshell.sandbox.v1.GetSandboxConfigRequest + 250, // 222: openshell.v1.OpenShell.GetGatewayConfig:input_type -> openshell.sandbox.v1.GetGatewayConfigRequest + 120, // 223: openshell.v1.OpenShell.UpdateConfig:input_type -> openshell.v1.UpdateConfigRequest + 129, // 224: openshell.v1.OpenShell.GetSandboxPolicyStatus:input_type -> openshell.v1.GetSandboxPolicyStatusRequest + 131, // 225: openshell.v1.OpenShell.ListSandboxPolicies:input_type -> openshell.v1.ListSandboxPoliciesRequest + 133, // 226: openshell.v1.OpenShell.ReportPolicyStatus:input_type -> openshell.v1.ReportPolicyStatusRequest + 118, // 227: openshell.v1.OpenShell.ExchangeProviderSubjectToken:input_type -> openshell.v1.ExchangeProviderSubjectTokenRequest + 136, // 228: openshell.v1.OpenShell.GetSandboxLogs:input_type -> openshell.v1.GetSandboxLogsRequest + 137, // 229: openshell.v1.OpenShell.PushSandboxLogs:input_type -> openshell.v1.PushSandboxLogsRequest + 140, // 230: openshell.v1.OpenShell.ConnectSupervisor:input_type -> openshell.v1.SupervisorMessage + 155, // 231: openshell.v1.OpenShell.ReportMainProcessExit:input_type -> openshell.v1.ReportMainProcessExitRequest + 161, // 232: openshell.v1.OpenShell.RelayStream:input_type -> openshell.v1.RelayFrame + 67, // 233: openshell.v1.OpenShell.WatchSandbox:input_type -> openshell.v1.WatchSandboxRequest + 170, // 234: openshell.v1.OpenShell.SubmitPolicyAnalysis:input_type -> openshell.v1.SubmitPolicyAnalysisRequest + 172, // 235: openshell.v1.OpenShell.GetDraftPolicy:input_type -> openshell.v1.GetDraftPolicyRequest + 174, // 236: openshell.v1.OpenShell.ApproveDraftChunk:input_type -> openshell.v1.ApproveDraftChunkRequest + 176, // 237: openshell.v1.OpenShell.RejectDraftChunk:input_type -> openshell.v1.RejectDraftChunkRequest + 179, // 238: openshell.v1.OpenShell.ApproveAllDraftChunks:input_type -> openshell.v1.ApproveAllDraftChunksRequest + 181, // 239: openshell.v1.OpenShell.EditDraftChunk:input_type -> openshell.v1.EditDraftChunkRequest + 183, // 240: openshell.v1.OpenShell.UndoDraftChunk:input_type -> openshell.v1.UndoDraftChunkRequest + 185, // 241: openshell.v1.OpenShell.ClearDraftChunks:input_type -> openshell.v1.ClearDraftChunksRequest + 187, // 242: openshell.v1.OpenShell.GetDraftHistory:input_type -> openshell.v1.GetDraftHistoryRequest + 10, // 243: openshell.v1.OpenShell.IssueSandboxToken:input_type -> openshell.v1.IssueSandboxTokenRequest + 12, // 244: openshell.v1.OpenShell.RefreshSandboxToken:input_type -> openshell.v1.RefreshSandboxTokenRequest + 194, // 245: openshell.v1.OpenShell.CreateWorkspace:input_type -> openshell.v1.CreateWorkspaceRequest + 196, // 246: openshell.v1.OpenShell.GetWorkspace:input_type -> openshell.v1.GetWorkspaceRequest + 198, // 247: openshell.v1.OpenShell.ListWorkspaces:input_type -> openshell.v1.ListWorkspacesRequest + 200, // 248: openshell.v1.OpenShell.DeleteWorkspace:input_type -> openshell.v1.DeleteWorkspaceRequest + 203, // 249: openshell.v1.OpenShell.AddWorkspaceMember:input_type -> openshell.v1.AddWorkspaceMemberRequest + 205, // 250: openshell.v1.OpenShell.RemoveWorkspaceMember:input_type -> openshell.v1.RemoveWorkspaceMemberRequest + 207, // 251: openshell.v1.OpenShell.ListWorkspaceMembers:input_type -> openshell.v1.ListWorkspaceMembersRequest + 15, // 252: openshell.v1.OpenShell.Health:output_type -> openshell.v1.HealthResponse + 17, // 253: openshell.v1.OpenShell.GetCurrentUser:output_type -> openshell.v1.GetCurrentUserResponse + 19, // 254: openshell.v1.OpenShell.GetGatewayInfo:output_type -> openshell.v1.GetGatewayInfoResponse + 39, // 255: openshell.v1.OpenShell.CreateSandbox:output_type -> openshell.v1.SandboxResponse + 39, // 256: openshell.v1.OpenShell.GetSandbox:output_type -> openshell.v1.SandboxResponse + 40, // 257: openshell.v1.OpenShell.ListSandboxes:output_type -> openshell.v1.ListSandboxesResponse + 41, // 258: openshell.v1.OpenShell.ListSandboxProviders:output_type -> openshell.v1.ListSandboxProvidersResponse + 42, // 259: openshell.v1.OpenShell.AttachSandboxProvider:output_type -> openshell.v1.AttachSandboxProviderResponse + 43, // 260: openshell.v1.OpenShell.DetachSandboxProvider:output_type -> openshell.v1.DetachSandboxProviderResponse + 44, // 261: openshell.v1.OpenShell.DeleteSandbox:output_type -> openshell.v1.DeleteSandboxResponse + 39, // 262: openshell.v1.OpenShell.StopSandbox:output_type -> openshell.v1.SandboxResponse + 39, // 263: openshell.v1.OpenShell.StartSandbox:output_type -> openshell.v1.SandboxResponse + 46, // 264: openshell.v1.OpenShell.CreateSshSession:output_type -> openshell.v1.CreateSshSessionResponse + 54, // 265: openshell.v1.OpenShell.ExposeService:output_type -> openshell.v1.ServiceEndpointResponse + 54, // 266: openshell.v1.OpenShell.GetService:output_type -> openshell.v1.ServiceEndpointResponse + 50, // 267: openshell.v1.OpenShell.ListServices:output_type -> openshell.v1.ListServicesResponse + 52, // 268: openshell.v1.OpenShell.DeleteService:output_type -> openshell.v1.DeleteServiceResponse + 56, // 269: openshell.v1.OpenShell.RevokeSshSession:output_type -> openshell.v1.RevokeSshSessionResponse + 61, // 270: openshell.v1.OpenShell.ExecSandbox:output_type -> openshell.v1.ExecSandboxEvent + 63, // 271: openshell.v1.OpenShell.ForwardTcp:output_type -> openshell.v1.TcpForwardFrame + 61, // 272: openshell.v1.OpenShell.ExecSandboxInteractive:output_type -> openshell.v1.ExecSandboxEvent + 76, // 273: openshell.v1.OpenShell.CreateProvider:output_type -> openshell.v1.ProviderResponse + 76, // 274: openshell.v1.OpenShell.GetProvider:output_type -> openshell.v1.ProviderResponse + 77, // 275: openshell.v1.OpenShell.ListProviders:output_type -> openshell.v1.ListProvidersResponse + 104, // 276: openshell.v1.OpenShell.ListProviderProfiles:output_type -> openshell.v1.ListProviderProfilesResponse + 103, // 277: openshell.v1.OpenShell.GetProviderProfile:output_type -> openshell.v1.ProviderProfileResponse + 106, // 278: openshell.v1.OpenShell.ImportProviderProfiles:output_type -> openshell.v1.ImportProviderProfilesResponse + 108, // 279: openshell.v1.OpenShell.UpdateProviderProfiles:output_type -> openshell.v1.UpdateProviderProfilesResponse + 110, // 280: openshell.v1.OpenShell.LintProviderProfiles:output_type -> openshell.v1.LintProviderProfilesResponse + 76, // 281: openshell.v1.OpenShell.UpdateProvider:output_type -> openshell.v1.ProviderResponse + 94, // 282: openshell.v1.OpenShell.GetProviderRefreshStatus:output_type -> openshell.v1.GetProviderRefreshStatusResponse + 96, // 283: openshell.v1.OpenShell.ConfigureProviderRefresh:output_type -> openshell.v1.ConfigureProviderRefreshResponse + 98, // 284: openshell.v1.OpenShell.RotateProviderCredential:output_type -> openshell.v1.RotateProviderCredentialResponse + 100, // 285: openshell.v1.OpenShell.DeleteProviderRefresh:output_type -> openshell.v1.DeleteProviderRefreshResponse + 111, // 286: openshell.v1.OpenShell.DeleteProvider:output_type -> openshell.v1.DeleteProviderResponse + 113, // 287: openshell.v1.OpenShell.DeleteProviderProfile:output_type -> openshell.v1.DeleteProviderProfileResponse + 246, // 288: openshell.v1.OpenShell.GetSandboxConfig:output_type -> openshell.sandbox.v1.SandboxConfigSnapshot + 251, // 289: openshell.v1.OpenShell.GetGatewayConfig:output_type -> openshell.sandbox.v1.GetGatewayConfigResponse + 128, // 290: openshell.v1.OpenShell.UpdateConfig:output_type -> openshell.v1.UpdateConfigResponse + 130, // 291: openshell.v1.OpenShell.GetSandboxPolicyStatus:output_type -> openshell.v1.GetSandboxPolicyStatusResponse + 132, // 292: openshell.v1.OpenShell.ListSandboxPolicies:output_type -> openshell.v1.ListSandboxPoliciesResponse + 134, // 293: openshell.v1.OpenShell.ReportPolicyStatus:output_type -> openshell.v1.ReportPolicyStatusResponse + 119, // 294: openshell.v1.OpenShell.ExchangeProviderSubjectToken:output_type -> openshell.v1.ExchangeProviderSubjectTokenResponse + 139, // 295: openshell.v1.OpenShell.GetSandboxLogs:output_type -> openshell.v1.GetSandboxLogsResponse + 138, // 296: openshell.v1.OpenShell.PushSandboxLogs:output_type -> openshell.v1.PushSandboxLogsResponse + 141, // 297: openshell.v1.OpenShell.ConnectSupervisor:output_type -> openshell.v1.GatewayMessage + 156, // 298: openshell.v1.OpenShell.ReportMainProcessExit:output_type -> openshell.v1.ReportMainProcessExitResponse + 161, // 299: openshell.v1.OpenShell.RelayStream:output_type -> openshell.v1.RelayFrame + 68, // 300: openshell.v1.OpenShell.WatchSandbox:output_type -> openshell.v1.SandboxStreamEvent + 171, // 301: openshell.v1.OpenShell.SubmitPolicyAnalysis:output_type -> openshell.v1.SubmitPolicyAnalysisResponse + 173, // 302: openshell.v1.OpenShell.GetDraftPolicy:output_type -> openshell.v1.GetDraftPolicyResponse + 175, // 303: openshell.v1.OpenShell.ApproveDraftChunk:output_type -> openshell.v1.ApproveDraftChunkResponse + 177, // 304: openshell.v1.OpenShell.RejectDraftChunk:output_type -> openshell.v1.RejectDraftChunkResponse + 180, // 305: openshell.v1.OpenShell.ApproveAllDraftChunks:output_type -> openshell.v1.ApproveAllDraftChunksResponse + 182, // 306: openshell.v1.OpenShell.EditDraftChunk:output_type -> openshell.v1.EditDraftChunkResponse + 184, // 307: openshell.v1.OpenShell.UndoDraftChunk:output_type -> openshell.v1.UndoDraftChunkResponse + 186, // 308: openshell.v1.OpenShell.ClearDraftChunks:output_type -> openshell.v1.ClearDraftChunksResponse + 189, // 309: openshell.v1.OpenShell.GetDraftHistory:output_type -> openshell.v1.GetDraftHistoryResponse + 11, // 310: openshell.v1.OpenShell.IssueSandboxToken:output_type -> openshell.v1.IssueSandboxTokenResponse + 13, // 311: openshell.v1.OpenShell.RefreshSandboxToken:output_type -> openshell.v1.RefreshSandboxTokenResponse + 195, // 312: openshell.v1.OpenShell.CreateWorkspace:output_type -> openshell.v1.CreateWorkspaceResponse + 197, // 313: openshell.v1.OpenShell.GetWorkspace:output_type -> openshell.v1.GetWorkspaceResponse + 199, // 314: openshell.v1.OpenShell.ListWorkspaces:output_type -> openshell.v1.ListWorkspacesResponse + 201, // 315: openshell.v1.OpenShell.DeleteWorkspace:output_type -> openshell.v1.DeleteWorkspaceResponse + 204, // 316: openshell.v1.OpenShell.AddWorkspaceMember:output_type -> openshell.v1.AddWorkspaceMemberResponse + 206, // 317: openshell.v1.OpenShell.RemoveWorkspaceMember:output_type -> openshell.v1.RemoveWorkspaceMemberResponse + 208, // 318: openshell.v1.OpenShell.ListWorkspaceMembers:output_type -> openshell.v1.ListWorkspaceMembersResponse + 252, // [252:319] is the sub-list for method output_type + 185, // [185:252] is the sub-list for method input_type + 185, // [185:185] is the sub-list for extension type_name + 185, // [185:185] is the sub-list for extension extendee + 0, // [0:185] is the sub-list for field type_name } func init() { file_openshell_proto_init() } @@ -15837,6 +16604,9 @@ func file_openshell_proto_init() { (*SupervisorMessage_Heartbeat)(nil), (*SupervisorMessage_RelayOpenResult)(nil), (*SupervisorMessage_RelayClose)(nil), + (*SupervisorMessage_BootstrapResult)(nil), + (*SupervisorMessage_ConfigUpdateResult)(nil), + (*SupervisorMessage_RuntimeReady)(nil), } file_openshell_proto_msgTypes[131].OneofWrappers = []any{ (*GatewayMessage_SessionAccepted)(nil), @@ -15844,24 +16614,30 @@ func file_openshell_proto_init() { (*GatewayMessage_Heartbeat)(nil), (*GatewayMessage_RelayOpen)(nil), (*GatewayMessage_RelayClose)(nil), + (*GatewayMessage_ConfigUpdate)(nil), + } + file_openshell_proto_msgTypes[136].OneofWrappers = []any{ + (*ConfigUpdate_SandboxConfig)(nil), + (*ConfigUpdate_ProviderEnvironment)(nil), + (*ConfigUpdate_InferenceBundle)(nil), } - file_openshell_proto_msgTypes[139].OneofWrappers = []any{ + file_openshell_proto_msgTypes[147].OneofWrappers = []any{ (*RelayOpen_Ssh)(nil), (*RelayOpen_Tcp)(nil), } - file_openshell_proto_msgTypes[143].OneofWrappers = []any{ + file_openshell_proto_msgTypes[151].OneofWrappers = []any{ (*RelayFrame_Init)(nil), (*RelayFrame_Data)(nil), } - file_openshell_proto_msgTypes[174].OneofWrappers = []any{} - file_openshell_proto_msgTypes[175].OneofWrappers = []any{} + file_openshell_proto_msgTypes[182].OneofWrappers = []any{} + file_openshell_proto_msgTypes[183].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_openshell_proto_rawDesc), len(file_openshell_proto_rawDesc)), - NumEnums: 8, - NumMessages: 217, + NumEnums: 10, + NumMessages: 225, NumExtensions: 0, NumServices: 1, }, diff --git a/sdk/go/proto/openshellv1/openshell_grpc.pb.go b/sdk/go/proto/openshellv1/openshell_grpc.pb.go index 663c09aed5..34fc3a388e 100644 --- a/sdk/go/proto/openshellv1/openshell_grpc.pb.go +++ b/sdk/go/proto/openshellv1/openshell_grpc.pb.go @@ -23,74 +23,73 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - OpenShell_Health_FullMethodName = "/openshell.v1.OpenShell/Health" - OpenShell_GetCurrentUser_FullMethodName = "/openshell.v1.OpenShell/GetCurrentUser" - OpenShell_GetGatewayInfo_FullMethodName = "/openshell.v1.OpenShell/GetGatewayInfo" - OpenShell_CreateSandbox_FullMethodName = "/openshell.v1.OpenShell/CreateSandbox" - OpenShell_GetSandbox_FullMethodName = "/openshell.v1.OpenShell/GetSandbox" - OpenShell_ListSandboxes_FullMethodName = "/openshell.v1.OpenShell/ListSandboxes" - OpenShell_ListSandboxProviders_FullMethodName = "/openshell.v1.OpenShell/ListSandboxProviders" - OpenShell_AttachSandboxProvider_FullMethodName = "/openshell.v1.OpenShell/AttachSandboxProvider" - OpenShell_DetachSandboxProvider_FullMethodName = "/openshell.v1.OpenShell/DetachSandboxProvider" - OpenShell_DeleteSandbox_FullMethodName = "/openshell.v1.OpenShell/DeleteSandbox" - OpenShell_StopSandbox_FullMethodName = "/openshell.v1.OpenShell/StopSandbox" - OpenShell_StartSandbox_FullMethodName = "/openshell.v1.OpenShell/StartSandbox" - OpenShell_CreateSshSession_FullMethodName = "/openshell.v1.OpenShell/CreateSshSession" - OpenShell_ExposeService_FullMethodName = "/openshell.v1.OpenShell/ExposeService" - OpenShell_GetService_FullMethodName = "/openshell.v1.OpenShell/GetService" - OpenShell_ListServices_FullMethodName = "/openshell.v1.OpenShell/ListServices" - OpenShell_DeleteService_FullMethodName = "/openshell.v1.OpenShell/DeleteService" - OpenShell_RevokeSshSession_FullMethodName = "/openshell.v1.OpenShell/RevokeSshSession" - OpenShell_ExecSandbox_FullMethodName = "/openshell.v1.OpenShell/ExecSandbox" - OpenShell_ForwardTcp_FullMethodName = "/openshell.v1.OpenShell/ForwardTcp" - OpenShell_ExecSandboxInteractive_FullMethodName = "/openshell.v1.OpenShell/ExecSandboxInteractive" - OpenShell_CreateProvider_FullMethodName = "/openshell.v1.OpenShell/CreateProvider" - OpenShell_GetProvider_FullMethodName = "/openshell.v1.OpenShell/GetProvider" - OpenShell_ListProviders_FullMethodName = "/openshell.v1.OpenShell/ListProviders" - OpenShell_ListProviderProfiles_FullMethodName = "/openshell.v1.OpenShell/ListProviderProfiles" - OpenShell_GetProviderProfile_FullMethodName = "/openshell.v1.OpenShell/GetProviderProfile" - OpenShell_ImportProviderProfiles_FullMethodName = "/openshell.v1.OpenShell/ImportProviderProfiles" - OpenShell_UpdateProviderProfiles_FullMethodName = "/openshell.v1.OpenShell/UpdateProviderProfiles" - OpenShell_LintProviderProfiles_FullMethodName = "/openshell.v1.OpenShell/LintProviderProfiles" - OpenShell_UpdateProvider_FullMethodName = "/openshell.v1.OpenShell/UpdateProvider" - OpenShell_GetProviderRefreshStatus_FullMethodName = "/openshell.v1.OpenShell/GetProviderRefreshStatus" - OpenShell_ConfigureProviderRefresh_FullMethodName = "/openshell.v1.OpenShell/ConfigureProviderRefresh" - OpenShell_RotateProviderCredential_FullMethodName = "/openshell.v1.OpenShell/RotateProviderCredential" - OpenShell_DeleteProviderRefresh_FullMethodName = "/openshell.v1.OpenShell/DeleteProviderRefresh" - OpenShell_DeleteProvider_FullMethodName = "/openshell.v1.OpenShell/DeleteProvider" - OpenShell_DeleteProviderProfile_FullMethodName = "/openshell.v1.OpenShell/DeleteProviderProfile" - OpenShell_GetSandboxConfig_FullMethodName = "/openshell.v1.OpenShell/GetSandboxConfig" - OpenShell_GetGatewayConfig_FullMethodName = "/openshell.v1.OpenShell/GetGatewayConfig" - OpenShell_UpdateConfig_FullMethodName = "/openshell.v1.OpenShell/UpdateConfig" - OpenShell_GetSandboxPolicyStatus_FullMethodName = "/openshell.v1.OpenShell/GetSandboxPolicyStatus" - OpenShell_ListSandboxPolicies_FullMethodName = "/openshell.v1.OpenShell/ListSandboxPolicies" - OpenShell_ReportPolicyStatus_FullMethodName = "/openshell.v1.OpenShell/ReportPolicyStatus" - OpenShell_GetSandboxProviderEnvironment_FullMethodName = "/openshell.v1.OpenShell/GetSandboxProviderEnvironment" - OpenShell_ExchangeProviderSubjectToken_FullMethodName = "/openshell.v1.OpenShell/ExchangeProviderSubjectToken" - OpenShell_GetSandboxLogs_FullMethodName = "/openshell.v1.OpenShell/GetSandboxLogs" - OpenShell_PushSandboxLogs_FullMethodName = "/openshell.v1.OpenShell/PushSandboxLogs" - OpenShell_ConnectSupervisor_FullMethodName = "/openshell.v1.OpenShell/ConnectSupervisor" - OpenShell_ReportMainProcessExit_FullMethodName = "/openshell.v1.OpenShell/ReportMainProcessExit" - OpenShell_RelayStream_FullMethodName = "/openshell.v1.OpenShell/RelayStream" - OpenShell_WatchSandbox_FullMethodName = "/openshell.v1.OpenShell/WatchSandbox" - OpenShell_SubmitPolicyAnalysis_FullMethodName = "/openshell.v1.OpenShell/SubmitPolicyAnalysis" - OpenShell_GetDraftPolicy_FullMethodName = "/openshell.v1.OpenShell/GetDraftPolicy" - OpenShell_ApproveDraftChunk_FullMethodName = "/openshell.v1.OpenShell/ApproveDraftChunk" - OpenShell_RejectDraftChunk_FullMethodName = "/openshell.v1.OpenShell/RejectDraftChunk" - OpenShell_ApproveAllDraftChunks_FullMethodName = "/openshell.v1.OpenShell/ApproveAllDraftChunks" - OpenShell_EditDraftChunk_FullMethodName = "/openshell.v1.OpenShell/EditDraftChunk" - OpenShell_UndoDraftChunk_FullMethodName = "/openshell.v1.OpenShell/UndoDraftChunk" - OpenShell_ClearDraftChunks_FullMethodName = "/openshell.v1.OpenShell/ClearDraftChunks" - OpenShell_GetDraftHistory_FullMethodName = "/openshell.v1.OpenShell/GetDraftHistory" - OpenShell_IssueSandboxToken_FullMethodName = "/openshell.v1.OpenShell/IssueSandboxToken" - OpenShell_RefreshSandboxToken_FullMethodName = "/openshell.v1.OpenShell/RefreshSandboxToken" - OpenShell_CreateWorkspace_FullMethodName = "/openshell.v1.OpenShell/CreateWorkspace" - OpenShell_GetWorkspace_FullMethodName = "/openshell.v1.OpenShell/GetWorkspace" - OpenShell_ListWorkspaces_FullMethodName = "/openshell.v1.OpenShell/ListWorkspaces" - OpenShell_DeleteWorkspace_FullMethodName = "/openshell.v1.OpenShell/DeleteWorkspace" - OpenShell_AddWorkspaceMember_FullMethodName = "/openshell.v1.OpenShell/AddWorkspaceMember" - OpenShell_RemoveWorkspaceMember_FullMethodName = "/openshell.v1.OpenShell/RemoveWorkspaceMember" - OpenShell_ListWorkspaceMembers_FullMethodName = "/openshell.v1.OpenShell/ListWorkspaceMembers" + OpenShell_Health_FullMethodName = "/openshell.v1.OpenShell/Health" + OpenShell_GetCurrentUser_FullMethodName = "/openshell.v1.OpenShell/GetCurrentUser" + OpenShell_GetGatewayInfo_FullMethodName = "/openshell.v1.OpenShell/GetGatewayInfo" + OpenShell_CreateSandbox_FullMethodName = "/openshell.v1.OpenShell/CreateSandbox" + OpenShell_GetSandbox_FullMethodName = "/openshell.v1.OpenShell/GetSandbox" + OpenShell_ListSandboxes_FullMethodName = "/openshell.v1.OpenShell/ListSandboxes" + OpenShell_ListSandboxProviders_FullMethodName = "/openshell.v1.OpenShell/ListSandboxProviders" + OpenShell_AttachSandboxProvider_FullMethodName = "/openshell.v1.OpenShell/AttachSandboxProvider" + OpenShell_DetachSandboxProvider_FullMethodName = "/openshell.v1.OpenShell/DetachSandboxProvider" + OpenShell_DeleteSandbox_FullMethodName = "/openshell.v1.OpenShell/DeleteSandbox" + OpenShell_StopSandbox_FullMethodName = "/openshell.v1.OpenShell/StopSandbox" + OpenShell_StartSandbox_FullMethodName = "/openshell.v1.OpenShell/StartSandbox" + OpenShell_CreateSshSession_FullMethodName = "/openshell.v1.OpenShell/CreateSshSession" + OpenShell_ExposeService_FullMethodName = "/openshell.v1.OpenShell/ExposeService" + OpenShell_GetService_FullMethodName = "/openshell.v1.OpenShell/GetService" + OpenShell_ListServices_FullMethodName = "/openshell.v1.OpenShell/ListServices" + OpenShell_DeleteService_FullMethodName = "/openshell.v1.OpenShell/DeleteService" + OpenShell_RevokeSshSession_FullMethodName = "/openshell.v1.OpenShell/RevokeSshSession" + OpenShell_ExecSandbox_FullMethodName = "/openshell.v1.OpenShell/ExecSandbox" + OpenShell_ForwardTcp_FullMethodName = "/openshell.v1.OpenShell/ForwardTcp" + OpenShell_ExecSandboxInteractive_FullMethodName = "/openshell.v1.OpenShell/ExecSandboxInteractive" + OpenShell_CreateProvider_FullMethodName = "/openshell.v1.OpenShell/CreateProvider" + OpenShell_GetProvider_FullMethodName = "/openshell.v1.OpenShell/GetProvider" + OpenShell_ListProviders_FullMethodName = "/openshell.v1.OpenShell/ListProviders" + OpenShell_ListProviderProfiles_FullMethodName = "/openshell.v1.OpenShell/ListProviderProfiles" + OpenShell_GetProviderProfile_FullMethodName = "/openshell.v1.OpenShell/GetProviderProfile" + OpenShell_ImportProviderProfiles_FullMethodName = "/openshell.v1.OpenShell/ImportProviderProfiles" + OpenShell_UpdateProviderProfiles_FullMethodName = "/openshell.v1.OpenShell/UpdateProviderProfiles" + OpenShell_LintProviderProfiles_FullMethodName = "/openshell.v1.OpenShell/LintProviderProfiles" + OpenShell_UpdateProvider_FullMethodName = "/openshell.v1.OpenShell/UpdateProvider" + OpenShell_GetProviderRefreshStatus_FullMethodName = "/openshell.v1.OpenShell/GetProviderRefreshStatus" + OpenShell_ConfigureProviderRefresh_FullMethodName = "/openshell.v1.OpenShell/ConfigureProviderRefresh" + OpenShell_RotateProviderCredential_FullMethodName = "/openshell.v1.OpenShell/RotateProviderCredential" + OpenShell_DeleteProviderRefresh_FullMethodName = "/openshell.v1.OpenShell/DeleteProviderRefresh" + OpenShell_DeleteProvider_FullMethodName = "/openshell.v1.OpenShell/DeleteProvider" + OpenShell_DeleteProviderProfile_FullMethodName = "/openshell.v1.OpenShell/DeleteProviderProfile" + OpenShell_GetSandboxConfig_FullMethodName = "/openshell.v1.OpenShell/GetSandboxConfig" + OpenShell_GetGatewayConfig_FullMethodName = "/openshell.v1.OpenShell/GetGatewayConfig" + OpenShell_UpdateConfig_FullMethodName = "/openshell.v1.OpenShell/UpdateConfig" + OpenShell_GetSandboxPolicyStatus_FullMethodName = "/openshell.v1.OpenShell/GetSandboxPolicyStatus" + OpenShell_ListSandboxPolicies_FullMethodName = "/openshell.v1.OpenShell/ListSandboxPolicies" + OpenShell_ReportPolicyStatus_FullMethodName = "/openshell.v1.OpenShell/ReportPolicyStatus" + OpenShell_ExchangeProviderSubjectToken_FullMethodName = "/openshell.v1.OpenShell/ExchangeProviderSubjectToken" + OpenShell_GetSandboxLogs_FullMethodName = "/openshell.v1.OpenShell/GetSandboxLogs" + OpenShell_PushSandboxLogs_FullMethodName = "/openshell.v1.OpenShell/PushSandboxLogs" + OpenShell_ConnectSupervisor_FullMethodName = "/openshell.v1.OpenShell/ConnectSupervisor" + OpenShell_ReportMainProcessExit_FullMethodName = "/openshell.v1.OpenShell/ReportMainProcessExit" + OpenShell_RelayStream_FullMethodName = "/openshell.v1.OpenShell/RelayStream" + OpenShell_WatchSandbox_FullMethodName = "/openshell.v1.OpenShell/WatchSandbox" + OpenShell_SubmitPolicyAnalysis_FullMethodName = "/openshell.v1.OpenShell/SubmitPolicyAnalysis" + OpenShell_GetDraftPolicy_FullMethodName = "/openshell.v1.OpenShell/GetDraftPolicy" + OpenShell_ApproveDraftChunk_FullMethodName = "/openshell.v1.OpenShell/ApproveDraftChunk" + OpenShell_RejectDraftChunk_FullMethodName = "/openshell.v1.OpenShell/RejectDraftChunk" + OpenShell_ApproveAllDraftChunks_FullMethodName = "/openshell.v1.OpenShell/ApproveAllDraftChunks" + OpenShell_EditDraftChunk_FullMethodName = "/openshell.v1.OpenShell/EditDraftChunk" + OpenShell_UndoDraftChunk_FullMethodName = "/openshell.v1.OpenShell/UndoDraftChunk" + OpenShell_ClearDraftChunks_FullMethodName = "/openshell.v1.OpenShell/ClearDraftChunks" + OpenShell_GetDraftHistory_FullMethodName = "/openshell.v1.OpenShell/GetDraftHistory" + OpenShell_IssueSandboxToken_FullMethodName = "/openshell.v1.OpenShell/IssueSandboxToken" + OpenShell_RefreshSandboxToken_FullMethodName = "/openshell.v1.OpenShell/RefreshSandboxToken" + OpenShell_CreateWorkspace_FullMethodName = "/openshell.v1.OpenShell/CreateWorkspace" + OpenShell_GetWorkspace_FullMethodName = "/openshell.v1.OpenShell/GetWorkspace" + OpenShell_ListWorkspaces_FullMethodName = "/openshell.v1.OpenShell/ListWorkspaces" + OpenShell_DeleteWorkspace_FullMethodName = "/openshell.v1.OpenShell/DeleteWorkspace" + OpenShell_AddWorkspaceMember_FullMethodName = "/openshell.v1.OpenShell/AddWorkspaceMember" + OpenShell_RemoveWorkspaceMember_FullMethodName = "/openshell.v1.OpenShell/RemoveWorkspaceMember" + OpenShell_ListWorkspaceMembers_FullMethodName = "/openshell.v1.OpenShell/ListWorkspaceMembers" ) // OpenShellClient is the client API for OpenShell service. @@ -180,8 +179,9 @@ type OpenShellClient interface { DeleteProvider(ctx context.Context, in *DeleteProviderRequest, opts ...grpc.CallOption) (*DeleteProviderResponse, error) // Delete a custom provider type profile by id. DeleteProviderProfile(ctx context.Context, in *DeleteProviderProfileRequest, opts ...grpc.CallOption) (*DeleteProviderProfileResponse, error) - // Get sandbox settings by id (called by sandbox entrypoint and poll loop). - GetSandboxConfig(ctx context.Context, in *sandboxv1.GetSandboxConfigRequest, opts ...grpc.CallOption) (*sandboxv1.GetSandboxConfigResponse, error) + // Get sandbox settings by id. This remains a public read API; supervisors + // receive the same snapshot through ConnectSupervisor. + GetSandboxConfig(ctx context.Context, in *sandboxv1.GetSandboxConfigRequest, opts ...grpc.CallOption) (*sandboxv1.SandboxConfigSnapshot, error) // Get gateway-global settings (read-only feature flags; any authenticated // user may read these so the CLI and TUI can discover capabilities like // providers_v2_enabled without requiring Platform Admin). @@ -199,8 +199,6 @@ type OpenShellClient interface { ListSandboxPolicies(ctx context.Context, in *ListSandboxPoliciesRequest, opts ...grpc.CallOption) (*ListSandboxPoliciesResponse, error) // Report policy load result (called by sandbox after reload attempt). ReportPolicyStatus(ctx context.Context, in *ReportPolicyStatusRequest, opts ...grpc.CallOption) (*ReportPolicyStatusResponse, error) - // Get provider environment for a sandbox (called by sandbox supervisor at startup). - GetSandboxProviderEnvironment(ctx context.Context, in *GetSandboxProviderEnvironmentRequest, opts ...grpc.CallOption) (*GetSandboxProviderEnvironmentResponse, error) // Exchange a stored provider subject token for an intermediate token scoped // to the calling supervisor's SPIFFE identity. ExchangeProviderSubjectToken(ctx context.Context, in *ExchangeProviderSubjectTokenRequest, opts ...grpc.CallOption) (*ExchangeProviderSubjectTokenResponse, error) @@ -667,9 +665,9 @@ func (c *openShellClient) DeleteProviderProfile(ctx context.Context, in *DeleteP return out, nil } -func (c *openShellClient) GetSandboxConfig(ctx context.Context, in *sandboxv1.GetSandboxConfigRequest, opts ...grpc.CallOption) (*sandboxv1.GetSandboxConfigResponse, error) { +func (c *openShellClient) GetSandboxConfig(ctx context.Context, in *sandboxv1.GetSandboxConfigRequest, opts ...grpc.CallOption) (*sandboxv1.SandboxConfigSnapshot, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(sandboxv1.GetSandboxConfigResponse) + out := new(sandboxv1.SandboxConfigSnapshot) err := c.cc.Invoke(ctx, OpenShell_GetSandboxConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err @@ -727,16 +725,6 @@ func (c *openShellClient) ReportPolicyStatus(ctx context.Context, in *ReportPoli return out, nil } -func (c *openShellClient) GetSandboxProviderEnvironment(ctx context.Context, in *GetSandboxProviderEnvironmentRequest, opts ...grpc.CallOption) (*GetSandboxProviderEnvironmentResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetSandboxProviderEnvironmentResponse) - err := c.cc.Invoke(ctx, OpenShell_GetSandboxProviderEnvironment_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *openShellClient) ExchangeProviderSubjectToken(ctx context.Context, in *ExchangeProviderSubjectTokenRequest, opts ...grpc.CallOption) (*ExchangeProviderSubjectTokenResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ExchangeProviderSubjectTokenResponse) @@ -1092,8 +1080,9 @@ type OpenShellServer interface { DeleteProvider(context.Context, *DeleteProviderRequest) (*DeleteProviderResponse, error) // Delete a custom provider type profile by id. DeleteProviderProfile(context.Context, *DeleteProviderProfileRequest) (*DeleteProviderProfileResponse, error) - // Get sandbox settings by id (called by sandbox entrypoint and poll loop). - GetSandboxConfig(context.Context, *sandboxv1.GetSandboxConfigRequest) (*sandboxv1.GetSandboxConfigResponse, error) + // Get sandbox settings by id. This remains a public read API; supervisors + // receive the same snapshot through ConnectSupervisor. + GetSandboxConfig(context.Context, *sandboxv1.GetSandboxConfigRequest) (*sandboxv1.SandboxConfigSnapshot, error) // Get gateway-global settings (read-only feature flags; any authenticated // user may read these so the CLI and TUI can discover capabilities like // providers_v2_enabled without requiring Platform Admin). @@ -1111,8 +1100,6 @@ type OpenShellServer interface { ListSandboxPolicies(context.Context, *ListSandboxPoliciesRequest) (*ListSandboxPoliciesResponse, error) // Report policy load result (called by sandbox after reload attempt). ReportPolicyStatus(context.Context, *ReportPolicyStatusRequest) (*ReportPolicyStatusResponse, error) - // Get provider environment for a sandbox (called by sandbox supervisor at startup). - GetSandboxProviderEnvironment(context.Context, *GetSandboxProviderEnvironmentRequest) (*GetSandboxProviderEnvironmentResponse, error) // Exchange a stored provider subject token for an intermediate token scoped // to the calling supervisor's SPIFFE identity. ExchangeProviderSubjectToken(context.Context, *ExchangeProviderSubjectTokenRequest) (*ExchangeProviderSubjectTokenResponse, error) @@ -1312,7 +1299,7 @@ func (UnimplementedOpenShellServer) DeleteProvider(context.Context, *DeleteProvi func (UnimplementedOpenShellServer) DeleteProviderProfile(context.Context, *DeleteProviderProfileRequest) (*DeleteProviderProfileResponse, error) { return nil, status.Error(codes.Unimplemented, "method DeleteProviderProfile not implemented") } -func (UnimplementedOpenShellServer) GetSandboxConfig(context.Context, *sandboxv1.GetSandboxConfigRequest) (*sandboxv1.GetSandboxConfigResponse, error) { +func (UnimplementedOpenShellServer) GetSandboxConfig(context.Context, *sandboxv1.GetSandboxConfigRequest) (*sandboxv1.SandboxConfigSnapshot, error) { return nil, status.Error(codes.Unimplemented, "method GetSandboxConfig not implemented") } func (UnimplementedOpenShellServer) GetGatewayConfig(context.Context, *sandboxv1.GetGatewayConfigRequest) (*sandboxv1.GetGatewayConfigResponse, error) { @@ -1330,9 +1317,6 @@ func (UnimplementedOpenShellServer) ListSandboxPolicies(context.Context, *ListSa func (UnimplementedOpenShellServer) ReportPolicyStatus(context.Context, *ReportPolicyStatusRequest) (*ReportPolicyStatusResponse, error) { return nil, status.Error(codes.Unimplemented, "method ReportPolicyStatus not implemented") } -func (UnimplementedOpenShellServer) GetSandboxProviderEnvironment(context.Context, *GetSandboxProviderEnvironmentRequest) (*GetSandboxProviderEnvironmentResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetSandboxProviderEnvironment not implemented") -} func (UnimplementedOpenShellServer) ExchangeProviderSubjectToken(context.Context, *ExchangeProviderSubjectTokenRequest) (*ExchangeProviderSubjectTokenResponse, error) { return nil, status.Error(codes.Unimplemented, "method ExchangeProviderSubjectToken not implemented") } @@ -2156,24 +2140,6 @@ func _OpenShell_ReportPolicyStatus_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } -func _OpenShell_GetSandboxProviderEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetSandboxProviderEnvironmentRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetSandboxProviderEnvironment(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetSandboxProviderEnvironment_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetSandboxProviderEnvironment(ctx, req.(*GetSandboxProviderEnvironmentRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _OpenShell_ExchangeProviderSubjectToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ExchangeProviderSubjectTokenRequest) if err := dec(in); err != nil { @@ -2747,10 +2713,6 @@ var OpenShell_ServiceDesc = grpc.ServiceDesc{ MethodName: "ReportPolicyStatus", Handler: _OpenShell_ReportPolicyStatus_Handler, }, - { - MethodName: "GetSandboxProviderEnvironment", - Handler: _OpenShell_GetSandboxProviderEnvironment_Handler, - }, { MethodName: "ExchangeProviderSubjectToken", Handler: _OpenShell_ExchangeProviderSubjectToken_Handler, diff --git a/sdk/go/proto/sandboxv1/sandbox.pb.go b/sdk/go/proto/sandboxv1/sandbox.pb.go index 8da143ebaa..9f9f566b46 100644 --- a/sdk/go/proto/sandboxv1/sandbox.pb.go +++ b/sdk/go/proto/sandboxv1/sandbox.pb.go @@ -75,7 +75,7 @@ func (SettingScope) EnumDescriptor() ([]byte, []int) { return file_sandbox_proto_rawDescGZIP(), []int{0} } -// Source used for the policy payload in GetSandboxConfigResponse. +// Source used for the policy payload in SandboxConfigSnapshot. type PolicySource int32 const ( @@ -1790,8 +1790,8 @@ func (x *EffectiveSetting) GetScope() SettingScope { return SettingScope_SETTING_SCOPE_UNSPECIFIED } -// Response containing effective sandbox settings and policy. -type GetSandboxConfigResponse struct { +// Complete effective sandbox settings and policy snapshot. +type SandboxConfigSnapshot struct { state protoimpl.MessageState `protogen:"open.v1"` // The sandbox policy configuration. Policy *SandboxPolicy `protobuf:"bytes,1,opt,name=policy,proto3" json:"policy,omitempty"` @@ -1830,20 +1830,20 @@ type GetSandboxConfigResponse struct { sizeCache protoimpl.SizeCache } -func (x *GetSandboxConfigResponse) Reset() { - *x = GetSandboxConfigResponse{} +func (x *SandboxConfigSnapshot) Reset() { + *x = SandboxConfigSnapshot{} mi := &file_sandbox_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *GetSandboxConfigResponse) String() string { +func (x *SandboxConfigSnapshot) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetSandboxConfigResponse) ProtoMessage() {} +func (*SandboxConfigSnapshot) ProtoMessage() {} -func (x *GetSandboxConfigResponse) ProtoReflect() protoreflect.Message { +func (x *SandboxConfigSnapshot) ProtoReflect() protoreflect.Message { mi := &file_sandbox_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1855,89 +1855,89 @@ func (x *GetSandboxConfigResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetSandboxConfigResponse.ProtoReflect.Descriptor instead. -func (*GetSandboxConfigResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use SandboxConfigSnapshot.ProtoReflect.Descriptor instead. +func (*SandboxConfigSnapshot) Descriptor() ([]byte, []int) { return file_sandbox_proto_rawDescGZIP(), []int{21} } -func (x *GetSandboxConfigResponse) GetPolicy() *SandboxPolicy { +func (x *SandboxConfigSnapshot) GetPolicy() *SandboxPolicy { if x != nil { return x.Policy } return nil } -func (x *GetSandboxConfigResponse) GetVersion() uint32 { +func (x *SandboxConfigSnapshot) GetVersion() uint32 { if x != nil { return x.Version } return 0 } -func (x *GetSandboxConfigResponse) GetPolicyHash() string { +func (x *SandboxConfigSnapshot) GetPolicyHash() string { if x != nil { return x.PolicyHash } return "" } -func (x *GetSandboxConfigResponse) GetSettings() map[string]*EffectiveSetting { +func (x *SandboxConfigSnapshot) GetSettings() map[string]*EffectiveSetting { if x != nil { return x.Settings } return nil } -func (x *GetSandboxConfigResponse) GetConfigRevision() uint64 { +func (x *SandboxConfigSnapshot) GetConfigRevision() uint64 { if x != nil { return x.ConfigRevision } return 0 } -func (x *GetSandboxConfigResponse) GetPolicySource() PolicySource { +func (x *SandboxConfigSnapshot) GetPolicySource() PolicySource { if x != nil { return x.PolicySource } return PolicySource_POLICY_SOURCE_UNSPECIFIED } -func (x *GetSandboxConfigResponse) GetGlobalPolicyVersion() uint32 { +func (x *SandboxConfigSnapshot) GetGlobalPolicyVersion() uint32 { if x != nil { return x.GlobalPolicyVersion } return 0 } -func (x *GetSandboxConfigResponse) GetProviderEnvRevision() uint64 { +func (x *SandboxConfigSnapshot) GetProviderEnvRevision() uint64 { if x != nil { return x.ProviderEnvRevision } return 0 } -func (x *GetSandboxConfigResponse) GetSupervisorMiddlewareServices() []*SupervisorMiddlewareService { +func (x *SandboxConfigSnapshot) GetSupervisorMiddlewareServices() []*SupervisorMiddlewareService { if x != nil { return x.SupervisorMiddlewareServices } return nil } -func (x *GetSandboxConfigResponse) GetWorkspace() string { +func (x *SandboxConfigSnapshot) GetWorkspace() string { if x != nil { return x.Workspace } return "" } -func (x *GetSandboxConfigResponse) GetPolicyValidationFailureMode() string { +func (x *SandboxConfigSnapshot) GetPolicyValidationFailureMode() string { if x != nil { return x.PolicyValidationFailureMode } return "" } -func (x *GetSandboxConfigResponse) GetExtensionAuthenticationEnabled() bool { +func (x *SandboxConfigSnapshot) GetExtensionAuthenticationEnabled() bool { if x != nil { return x.ExtensionAuthenticationEnabled } @@ -2208,13 +2208,13 @@ const file_sandbox_proto_rawDesc = "" + "\x05value\"\x86\x01\n" + "\x10EffectiveSetting\x128\n" + "\x05value\x18\x01 \x01(\v2\".openshell.sandbox.v1.SettingValueR\x05value\x128\n" + - "\x05scope\x18\x02 \x01(\x0e2\".openshell.sandbox.v1.SettingScopeR\x05scope\"\xd1\x06\n" + - "\x18GetSandboxConfigResponse\x12;\n" + + "\x05scope\x18\x02 \x01(\x0e2\".openshell.sandbox.v1.SettingScopeR\x05scope\"\xcb\x06\n" + + "\x15SandboxConfigSnapshot\x12;\n" + "\x06policy\x18\x01 \x01(\v2#.openshell.sandbox.v1.SandboxPolicyR\x06policy\x12\x18\n" + "\aversion\x18\x02 \x01(\rR\aversion\x12\x1f\n" + "\vpolicy_hash\x18\x03 \x01(\tR\n" + - "policyHash\x12X\n" + - "\bsettings\x18\x04 \x03(\v2<.openshell.sandbox.v1.GetSandboxConfigResponse.SettingsEntryR\bsettings\x12'\n" + + "policyHash\x12U\n" + + "\bsettings\x18\x04 \x03(\v29.openshell.sandbox.v1.SandboxConfigSnapshot.SettingsEntryR\bsettings\x12'\n" + "\x0fconfig_revision\x18\x05 \x01(\x04R\x0econfigRevision\x12G\n" + "\rpolicy_source\x18\x06 \x01(\x0e2\".openshell.sandbox.v1.PolicySourceR\fpolicySource\x122\n" + "\x15global_policy_version\x18\a \x01(\rR\x13globalPolicyVersion\x122\n" + @@ -2282,7 +2282,7 @@ var file_sandbox_proto_goTypes = []any{ (*GetGatewayConfigResponse)(nil), // 20: openshell.sandbox.v1.GetGatewayConfigResponse (*SettingValue)(nil), // 21: openshell.sandbox.v1.SettingValue (*EffectiveSetting)(nil), // 22: openshell.sandbox.v1.EffectiveSetting - (*GetSandboxConfigResponse)(nil), // 23: openshell.sandbox.v1.GetSandboxConfigResponse + (*SandboxConfigSnapshot)(nil), // 23: openshell.sandbox.v1.SandboxConfigSnapshot (*SupervisorMiddlewareService)(nil), // 24: openshell.sandbox.v1.SupervisorMiddlewareService nil, // 25: openshell.sandbox.v1.SandboxPolicy.NetworkPoliciesEntry nil, // 26: openshell.sandbox.v1.SandboxPolicy.NetworkMiddlewaresEntry @@ -2292,7 +2292,7 @@ var file_sandbox_proto_goTypes = []any{ nil, // 30: openshell.sandbox.v1.L7Allow.QueryEntry nil, // 31: openshell.sandbox.v1.L7Allow.ParamsEntry nil, // 32: openshell.sandbox.v1.GetGatewayConfigResponse.SettingsEntry - nil, // 33: openshell.sandbox.v1.GetSandboxConfigResponse.SettingsEntry + nil, // 33: openshell.sandbox.v1.SandboxConfigSnapshot.SettingsEntry (*structpb.Struct)(nil), // 34: google.protobuf.Struct } var file_sandbox_proto_depIdxs = []int32{ @@ -2318,10 +2318,10 @@ var file_sandbox_proto_depIdxs = []int32{ 32, // 19: openshell.sandbox.v1.GetGatewayConfigResponse.settings:type_name -> openshell.sandbox.v1.GetGatewayConfigResponse.SettingsEntry 21, // 20: openshell.sandbox.v1.EffectiveSetting.value:type_name -> openshell.sandbox.v1.SettingValue 0, // 21: openshell.sandbox.v1.EffectiveSetting.scope:type_name -> openshell.sandbox.v1.SettingScope - 2, // 22: openshell.sandbox.v1.GetSandboxConfigResponse.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 33, // 23: openshell.sandbox.v1.GetSandboxConfigResponse.settings:type_name -> openshell.sandbox.v1.GetSandboxConfigResponse.SettingsEntry - 1, // 24: openshell.sandbox.v1.GetSandboxConfigResponse.policy_source:type_name -> openshell.sandbox.v1.PolicySource - 24, // 25: openshell.sandbox.v1.GetSandboxConfigResponse.supervisor_middleware_services:type_name -> openshell.sandbox.v1.SupervisorMiddlewareService + 2, // 22: openshell.sandbox.v1.SandboxConfigSnapshot.policy:type_name -> openshell.sandbox.v1.SandboxPolicy + 33, // 23: openshell.sandbox.v1.SandboxConfigSnapshot.settings:type_name -> openshell.sandbox.v1.SandboxConfigSnapshot.SettingsEntry + 1, // 24: openshell.sandbox.v1.SandboxConfigSnapshot.policy_source:type_name -> openshell.sandbox.v1.PolicySource + 24, // 25: openshell.sandbox.v1.SandboxConfigSnapshot.supervisor_middleware_services:type_name -> openshell.sandbox.v1.SupervisorMiddlewareService 6, // 26: openshell.sandbox.v1.SandboxPolicy.NetworkPoliciesEntry.value:type_name -> openshell.sandbox.v1.NetworkPolicyRule 7, // 27: openshell.sandbox.v1.SandboxPolicy.NetworkMiddlewaresEntry.value:type_name -> openshell.sandbox.v1.NetworkMiddlewareConfig 12, // 28: openshell.sandbox.v1.NetworkEndpoint.GraphqlPersistedQueriesEntry.value:type_name -> openshell.sandbox.v1.GraphqlOperation @@ -2330,7 +2330,7 @@ var file_sandbox_proto_depIdxs = []int32{ 16, // 31: openshell.sandbox.v1.L7Allow.QueryEntry.value:type_name -> openshell.sandbox.v1.L7QueryMatcher 16, // 32: openshell.sandbox.v1.L7Allow.ParamsEntry.value:type_name -> openshell.sandbox.v1.L7QueryMatcher 21, // 33: openshell.sandbox.v1.GetGatewayConfigResponse.SettingsEntry.value:type_name -> openshell.sandbox.v1.SettingValue - 22, // 34: openshell.sandbox.v1.GetSandboxConfigResponse.SettingsEntry.value:type_name -> openshell.sandbox.v1.EffectiveSetting + 22, // 34: openshell.sandbox.v1.SandboxConfigSnapshot.SettingsEntry.value:type_name -> openshell.sandbox.v1.EffectiveSetting 35, // [35:35] is the sub-list for method output_type 35, // [35:35] is the sub-list for method input_type 35, // [35:35] is the sub-list for extension type_name diff --git a/sdk/typescript/buf.gen.yaml b/sdk/typescript/buf.gen.yaml index 757f0bd73e..570ede58f4 100644 --- a/sdk/typescript/buf.gen.yaml +++ b/sdk/typescript/buf.gen.yaml @@ -5,15 +5,15 @@ # validation policy live in the repo-level buf.yaml; this template only drives # generation. buf compiles the module with its own compiler (no protoc) and # runs the connect-es plugin from this package's devDependencies. Limited to -# the client-surface closure so we don't emit the unused inference/compute/test -# protos; well-known types resolve through @bufbuild/protobuf/wkt and are not -# generated. +# the client-surface closure so we don't emit unused compute/test protos; +# well-known types resolve through @bufbuild/protobuf/wkt and are not generated. version: v2 clean: true inputs: - directory: ../../proto paths: - ../../proto/openshell.proto + - ../../proto/inference.proto - ../../proto/sandbox.proto - ../../proto/datamodel.proto - ../../proto/options.proto diff --git a/sdk/typescript/src/client.ts b/sdk/typescript/src/client.ts index b6db97943f..f47b351576 100644 --- a/sdk/typescript/src/client.ts +++ b/sdk/typescript/src/client.ts @@ -26,7 +26,7 @@ import { ServiceStatus, type TcpForwardFrameSchema, } from './gen/openshell_pb.js'; -import type { EffectiveSetting, GetSandboxConfigResponse, SandboxPolicy, SettingValue } from './gen/sandbox_pb.js'; +import type { EffectiveSetting, SandboxConfigSnapshot, SandboxPolicy, SettingValue } from './gen/sandbox_pb.js'; import { PolicySource, type SandboxPolicySchema, SettingScope, type SettingValueSchema } from './gen/sandbox_pb.js'; import { validateSshResponse } from './ssh-validate.js'; import { buildTransport, type ConnectOptions } from './transport.js'; @@ -352,7 +352,7 @@ function providerRef(provider: Provider): ProviderRef { }; } -function sandboxConfig(resp: GetSandboxConfigResponse): SandboxConfig { +function sandboxConfig(resp: SandboxConfigSnapshot): SandboxConfig { const settings: Record = {}; for (const [key, setting] of Object.entries(resp.settings)) { settings[key] = effectiveSetting(setting); diff --git a/tasks/scripts/generate_python_proto.py b/tasks/scripts/generate_python_proto.py index b29510a085..3b0224fa8d 100644 --- a/tasks/scripts/generate_python_proto.py +++ b/tasks/scripts/generate_python_proto.py @@ -48,6 +48,10 @@ r"^import datamodel_pb2 as datamodel__pb2$", "from . import datamodel_pb2 as datamodel__pb2", ), + ( + r"^import inference_pb2 as inference__pb2$", + "from . import inference_pb2 as inference__pb2", + ), ( r"^import options_pb2 as options__pb2$", "from . import options_pb2 as options__pb2",