diff --git a/.agents/skills/openshell-cli/SKILL.md b/.agents/skills/openshell-cli/SKILL.md index 69a889a8e..313e23988 100644 --- a/.agents/skills/openshell-cli/SKILL.md +++ b/.agents/skills/openshell-cli/SKILL.md @@ -207,6 +207,18 @@ output. Use `provider refresh rotate`, reconfigure refresh, or delete refresh before returning those keys to manual management. Unrelated provider fields remain updateable. +When OAuth refresh fails, inspect the `RECOVERY` and `FAILURE_CODE` columns from +`provider refresh status`; do not infer the remedy from HTTP status or parse +`LAST_ERROR`. `retry` means the worker will try again, `reauthorize` means the +user must obtain a new OAuth grant and run `provider refresh configure`, +`fix_configuration` means an operator must repair the OAuth client, scopes, or +administrator policy, and `investigate` means the issuer returned an +unrecognized response. The gateway parks `reauthorize` records until a manual +rotate or reconfiguration. It retries +`fix_configuration` records hourly so externally repaired configuration can +recover without rapid token-endpoint traffic. The existing access credential +remains usable only until its recorded expiry. + --- ## Workflow 3: Sandbox Lifecycle diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 3e6a97287..f17695304 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -157,6 +157,12 @@ jobs: podman_package_version: "5.7.0+ds2-3build1" conmon_package_version: "2.1.13+ds1-2" cmd: "env -u OPENSHELL_GATEWAY_BIN mise run --no-deps --skip-deps e2e:podman:external-driver" + - suite: provider-refresh-keycloak + runner: ubuntu-26.04 + podman_major: "5" + podman_package_version: "5.7.0+ds2-3build1" + conmon_package_version: "2.1.13+ds1-2" + cmd: "mise run --no-deps --skip-deps e2e:provider-refresh-keycloak" env: IMAGE_TAG: ${{ inputs.image-tag }} MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/architecture/gateway.md b/architecture/gateway.md index f7c80d1ed..4cf98cc41 100644 --- a/architecture/gateway.md +++ b/architecture/gateway.md @@ -343,6 +343,15 @@ Multi-replica deployments can use that default with a shared database and shared key-encryption key, or opt into an external backend such as Vault or Kubernetes Secrets. +OAuth refresh failures retain a gateway-owned recovery classification alongside +the refresh state. The gateway reads only a bounded error response and maps +recognized OAuth codes to retry, reauthorization, configuration repair, or +investigation without persisting issuer-controlled descriptions. Terminal +reauthorization failures remain parked until a manual retry or explicit refresh +reconfiguration. Configuration failures retry hourly so an externally repaired +clock, policy, or stored credential can recover without rapid endpoint traffic; +short-lived credentials still fail closed at their recorded expiry. + Credential handles remain bound to the driver that created them. Before the 0.1.0 compatibility boundary, gateways do not migrate inline refresh material or move handles between credential drivers; operators reconfigure affected diff --git a/crates/openshell-cli/src/run.rs b/crates/openshell-cli/src/run.rs index af8b7d5fd..0a0b21a7f 100644 --- a/crates/openshell-cli/src/run.rs +++ b/crates/openshell-cli/src/run.rs @@ -46,7 +46,8 @@ use openshell_core::proto::{ GetSandboxRequest, GetServiceRequest, GpuResourceRequirements, ImportProviderProfilesRequest, LintProviderProfilesRequest, ListProviderProfilesRequest, ListProvidersRequest, ListSandboxPoliciesRequest, ListSandboxProvidersRequest, ListSandboxesRequest, - ListServicesRequest, PolicySource, PolicyStatus, Provider, ProviderCredentialRefreshStatus, + ListServicesRequest, PolicySource, PolicyStatus, Provider, + ProviderCredentialRefreshRecoveryAction, ProviderCredentialRefreshStatus, ProviderCredentialRefreshStrategy, ProviderCredentialTokenGrantType, ProviderProfile, ProviderProfileDiagnostic, ProviderProfileImportItem, RejectDraftChunkRequest, ResourceRequirements, RevokeSshSessionRequest, RotateProviderCredentialRequest, Sandbox, @@ -4338,14 +4339,16 @@ pub async fn provider_refresh_status( fn refresh_status_header() -> String { format!( - "{:<24} {:<28} {:<28} {:<18} {:<20} {:<20} {:<20} {}", + "{:<24} {:<28} {:<28} {:<24} {:<18} {:<20} {:<20} {:<20} {:<44} {}", "PROVIDER".bold(), "CREDENTIAL_KEY".bold(), "STRATEGY".bold(), "STATUS".bold(), + "RECOVERY".bold(), "EXPIRES_AT".bold(), "NEXT_REFRESH".bold(), "LAST_REFRESH".bold(), + "FAILURE_CODE".bold(), "LAST_ERROR".bold(), ) } @@ -4497,19 +4500,43 @@ fn print_refresh_status_row(status: &ProviderCredentialRefreshStatus) { fn refresh_status_row(status: &ProviderCredentialRefreshStatus) -> String { let strategy = ProviderCredentialRefreshStrategy::try_from(status.strategy) .unwrap_or(ProviderCredentialRefreshStrategy::Unspecified); + let recovery_action = ProviderCredentialRefreshRecoveryAction::try_from(status.recovery_action) + .unwrap_or(ProviderCredentialRefreshRecoveryAction::Unspecified); format!( - "{:<24} {:<28} {:<28} {:<18} {:<20} {:<20} {:<20} {}", + "{:<24} {:<28} {:<28} {:<24} {:<18} {:<20} {:<20} {:<20} {:<44} {}", status.provider_name, status.credential_key, provider_refresh_strategy_name(strategy), status.status, + provider_refresh_recovery_action_name(recovery_action), format_optional_epoch_ms(status.expires_at_ms), - format_optional_epoch_ms(status.next_refresh_at_ms), + format_refresh_next_at_ms(status.next_refresh_at_ms), format_optional_epoch_ms(status.last_refresh_at_ms), + status.failure_code, truncate_status_field(&status.last_error, 72), ) } +fn format_refresh_next_at_ms(next_refresh_at_ms: i64) -> String { + if next_refresh_at_ms == i64::MAX { + "-".to_string() + } else { + format_optional_epoch_ms(next_refresh_at_ms) + } +} + +fn provider_refresh_recovery_action_name( + action: ProviderCredentialRefreshRecoveryAction, +) -> &'static str { + match action { + ProviderCredentialRefreshRecoveryAction::Retry => "retry", + ProviderCredentialRefreshRecoveryAction::Reauthorize => "reauthorize", + ProviderCredentialRefreshRecoveryAction::FixConfiguration => "fix_configuration", + ProviderCredentialRefreshRecoveryAction::Investigate => "investigate", + ProviderCredentialRefreshRecoveryAction::Unspecified => "-", + } +} + fn provider_refresh_strategy_name(strategy: ProviderCredentialRefreshStrategy) -> &'static str { match strategy { ProviderCredentialRefreshStrategy::Static => "static", @@ -7403,10 +7430,11 @@ mod tests { }; use openshell_core::proto::{ GetSandboxConfigResponse, GpuResourceRequirements, PolicySource, PolicyStatus, Provider, - ProviderCredentialRefresh, ProviderCredentialRefreshStatus, - ProviderCredentialRefreshStrategy, ProviderCredentialTokenGrant, ProviderProfile, - ProviderProfileCredential, ResourceRequirements, Sandbox, SandboxCondition, SandboxPhase, - SandboxPolicyRevision, SandboxStatus, datamodel::v1::ObjectMeta, + ProviderCredentialRefresh, ProviderCredentialRefreshRecoveryAction, + ProviderCredentialRefreshStatus, ProviderCredentialRefreshStrategy, + ProviderCredentialTokenGrant, ProviderProfile, ProviderProfileCredential, + ResourceRequirements, Sandbox, SandboxCondition, SandboxPhase, SandboxPolicyRevision, + SandboxStatus, datamodel::v1::ObjectMeta, }; #[test] @@ -7645,6 +7673,8 @@ mod tests { let header = refresh_status_header(); assert!(header.contains("NEXT_REFRESH")); assert!(header.contains("LAST_REFRESH")); + assert!(header.contains("RECOVERY")); + assert!(header.contains("FAILURE_CODE")); assert!(header.contains("LAST_ERROR")); let row = refresh_status_row(&ProviderCredentialRefreshStatus { @@ -7654,17 +7684,24 @@ mod tests { strategy: ProviderCredentialRefreshStrategy::Oauth2ClientCredentials as i32, status: "error".to_string(), expires_at_ms: 1_767_225_600_000, - next_refresh_at_ms: 1_767_225_660_000, + next_refresh_at_ms: i64::MAX, last_refresh_at_ms: 1_767_225_000_000, last_error: "token endpoint returned a very long error message that should be truncated for table readability" .to_string(), + recovery_action: ProviderCredentialRefreshRecoveryAction::Reauthorize as i32, + failure_code: "oauth_rotated_refresh_token_handle_missing".to_string(), + provider_error_subtype: "invalid_rapt".to_string(), + last_error_at_ms: 1_767_225_000_000, }); assert!(row.contains("my-graph")); assert!(row.contains("MS_GRAPH_ACCESS_TOKEN")); assert!(row.contains("oauth2_client_credentials")); assert!(row.contains("error")); + assert!(row.contains("reauthorize")); + assert!(row.contains("oauth_rotated_refresh_token_handle_missing")); assert!(row.contains("2026-01-01 00:00:00")); + assert!(!row.contains("292278994")); assert!(row.contains("...")); } diff --git a/crates/openshell-cli/tests/provider_commands_integration.rs b/crates/openshell-cli/tests/provider_commands_integration.rs index c349bbdbb..2c71e0b39 100644 --- a/crates/openshell-cli/tests/provider_commands_integration.rs +++ b/crates/openshell-cli/tests/provider_commands_integration.rs @@ -750,6 +750,10 @@ impl OpenShell for TestOpenShell { next_refresh_at_ms: 0, last_refresh_at_ms: 0, last_error: String::new(), + recovery_action: 0, + failure_code: String::new(), + provider_error_subtype: String::new(), + last_error_at_ms: 0, }; drop(providers); self.state diff --git a/crates/openshell-server/src/grpc/provider.rs b/crates/openshell-server/src/grpc/provider.rs index e20edc055..c8c8149a8 100644 --- a/crates/openshell-server/src/grpc/provider.rs +++ b/crates/openshell-server/src/grpc/provider.rs @@ -4755,10 +4755,11 @@ mod tests { ImportProviderProfilesRequest, L7Allow, L7Rule, LintProviderProfilesRequest, ListProviderProfilesRequest, ListProvidersRequest, NetworkBinary, NetworkEndpoint, NetworkPolicyRule, ProviderCredentialRefresh, ProviderCredentialRefreshMaterial, - ProviderCredentialTokenGrant, ProviderCredentialTokenGrantAudienceOverride, - ProviderProfile, ProviderProfileCategory, ProviderProfileCredential, - ProviderProfileImportItem, RotateProviderCredentialRequest, Sandbox, SandboxPolicy, - SandboxSpec, StoredProviderProfile, UpdateProviderProfilesRequest, UpdateProviderRequest, + ProviderCredentialRefreshRecoveryAction, ProviderCredentialTokenGrant, + ProviderCredentialTokenGrantAudienceOverride, ProviderProfile, ProviderProfileCategory, + ProviderProfileCredential, ProviderProfileImportItem, RotateProviderCredentialRequest, + Sandbox, SandboxPolicy, SandboxSpec, StoredProviderProfile, UpdateProviderProfilesRequest, + UpdateProviderRequest, }; use openshell_core::{ObjectId, ObjectName}; use tonic::{Code, Request}; @@ -11988,7 +11989,16 @@ mod tests { .await .unwrap() .expect("refresh state should exist"); - assert_eq!(refresh_state.status, "error"); + assert_eq!(refresh_state.status, "configuration_required"); + assert_eq!( + refresh_state.recovery_action, + ProviderCredentialRefreshRecoveryAction::FixConfiguration as i32 + ); + assert_eq!(refresh_state.failure_code, "refresh_configuration_invalid"); + assert_eq!( + refresh_state.next_refresh_at_ms - refresh_state.last_error_at_ms, + 60 * 60 * 1000 + ); } #[tokio::test] diff --git a/crates/openshell-server/src/provider_refresh.rs b/crates/openshell-server/src/provider_refresh.rs index f562e1d18..176f86ae2 100644 --- a/crates/openshell-server/src/provider_refresh.rs +++ b/crates/openshell-server/src/provider_refresh.rs @@ -9,7 +9,8 @@ use crate::credentials::RefreshMaterialScope; use crate::persistence::{ObjectType, PersistenceError, Store, WriteCondition, current_time_ms}; use openshell_core::ObjectWorkspace; use openshell_core::proto::{ - CredentialHandle, Provider, ProviderCredentialRefreshStatus, ProviderCredentialRefreshStrategy, + CredentialHandle, Provider, ProviderCredentialRefreshRecoveryAction, + ProviderCredentialRefreshStatus, ProviderCredentialRefreshStrategy, StoredProviderCredentialRefreshState, StoredRefreshMaterialDeletion, }; use openshell_core::{ObjectId, ObjectName, SetResourceVersion}; @@ -17,13 +18,15 @@ use prost::Message; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::time::Duration; -use tonic::Status; +use tonic::{Code, Status}; use tracing::{info, warn}; const DEFAULT_REFRESH_BEFORE_SECONDS: i64 = 300; const DEFAULT_MAX_LIFETIME_SECONDS: i64 = 3600; const REFRESH_ERROR_RETRY_SECONDS: i64 = 60; +const REFRESH_CONFIGURATION_RETRY_SECONDS: i64 = 60 * 60; const REFRESH_WORKER_PAGE_SIZE: u32 = 1000; +const MAX_OAUTH_ERROR_RESPONSE_BYTES: usize = 8 * 1024; pub fn refresh_material_scope( state: &StoredProviderCredentialRefreshState, @@ -321,6 +324,10 @@ pub fn refresh_status_from_state( next_refresh_at_ms: state.next_refresh_at_ms, last_refresh_at_ms: state.last_refresh_at_ms, last_error: state.last_error.clone(), + recovery_action: state.recovery_action, + failure_code: state.failure_code.clone(), + provider_error_subtype: state.provider_error_subtype.clone(), + last_error_at_ms: state.last_error_at_ms, } } @@ -385,6 +392,10 @@ pub fn new_refresh_state( authorization_epoch: uuid::Uuid::new_v4().to_string(), secret_material_handles: HashMap::new(), pending_secret_deletions: Vec::new(), + recovery_action: ProviderCredentialRefreshRecoveryAction::Unspecified as i32, + failure_code: String::new(), + provider_error_subtype: String::new(), + last_error_at_ms: 0, }) } @@ -403,6 +414,130 @@ struct TokenResponse { refresh_token: Option, } +#[derive(Debug, Deserialize)] +struct OAuthErrorResponse { + error: String, + #[serde(default, deserialize_with = "deserialize_optional_oauth_string")] + error_subtype: Option, +} + +fn deserialize_optional_oauth_string<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + let value = serde_json::Value::deserialize(deserializer)?; + Ok(value.as_str().map(str::to_owned)) +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum OAuthGrantKind { + UserRefreshToken, + NonInteractive, +} + +#[derive(Debug)] +struct RefreshFailure { + status: Status, + recovery_action: ProviderCredentialRefreshRecoveryAction, + failure_code: &'static str, + provider_error_subtype: Option<&'static str>, + retry_schedule: RefreshRetrySchedule, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum RefreshRetrySchedule { + Short, + Configuration, + Parked, +} + +impl RefreshFailure { + fn retryable(status: Status, failure_code: &'static str) -> Self { + Self { + status, + recovery_action: ProviderCredentialRefreshRecoveryAction::Retry, + failure_code, + provider_error_subtype: None, + retry_schedule: RefreshRetrySchedule::Short, + } + } + + fn investigate(status: Status, failure_code: &'static str) -> Self { + Self { + status, + recovery_action: ProviderCredentialRefreshRecoveryAction::Investigate, + failure_code, + provider_error_subtype: None, + retry_schedule: RefreshRetrySchedule::Short, + } + } + + fn reauthorize( + status: Status, + failure_code: &'static str, + provider_error_subtype: Option<&'static str>, + ) -> Self { + Self { + status, + recovery_action: ProviderCredentialRefreshRecoveryAction::Reauthorize, + failure_code, + provider_error_subtype, + retry_schedule: RefreshRetrySchedule::Parked, + } + } + + fn fix_configuration(status: Status, failure_code: &'static str) -> Self { + Self { + status, + recovery_action: ProviderCredentialRefreshRecoveryAction::FixConfiguration, + failure_code, + provider_error_subtype: None, + retry_schedule: RefreshRetrySchedule::Configuration, + } + } + + fn fix_configuration_with_subtype( + status: Status, + failure_code: &'static str, + provider_error_subtype: &'static str, + ) -> Self { + Self { + status, + recovery_action: ProviderCredentialRefreshRecoveryAction::FixConfiguration, + failure_code, + provider_error_subtype: Some(provider_error_subtype), + retry_schedule: RefreshRetrySchedule::Configuration, + } + } + + fn into_status(self) -> Status { + self.status + } + + fn from_status(status: &Status) -> Self { + Self::from(Status::new(status.code(), status.message().to_string())) + } +} + +impl From for RefreshFailure { + fn from(status: Status) -> Self { + match status.code() { + Code::InvalidArgument + | Code::FailedPrecondition + | Code::PermissionDenied + | Code::Unauthenticated => { + Self::fix_configuration(status, "refresh_configuration_invalid") + } + Code::Unavailable + | Code::DeadlineExceeded + | Code::ResourceExhausted + | Code::Aborted + | Code::Internal => Self::retryable(status, "refresh_failed"), + _ => Self::investigate(status, "refresh_failed"), + } + } +} + #[derive(Debug, Serialize)] struct GoogleServiceAccountClaims<'a> { iss: &'a str, @@ -554,17 +689,51 @@ async fn cleanup_pending_secret_deletions( Ok(new_version) } -async fn persist_refresh_error_state( +async fn persist_retryable_refresh_error_state( store: &Store, state: &mut StoredProviderCredentialRefreshState, expected_version: u64, error: &Status, +) -> Result { + let failure = RefreshFailure::retryable( + Status::new(error.code(), error.message().to_string()), + "refresh_failed", + ); + persist_refresh_failure_state(store, state, expected_version, &failure).await +} + +async fn persist_refresh_failure_state( + store: &Store, + state: &mut StoredProviderCredentialRefreshState, + expected_version: u64, + failure: &RefreshFailure, ) -> Result { let now_ms = current_time_ms(); - state.status = "error".to_string(); - state.last_error = error.message().to_string(); - state.next_refresh_at_ms = - now_ms.saturating_add(REFRESH_ERROR_RETRY_SECONDS.saturating_mul(1000)); + state.status = match failure.recovery_action { + ProviderCredentialRefreshRecoveryAction::Retry + | ProviderCredentialRefreshRecoveryAction::Unspecified => "error", + ProviderCredentialRefreshRecoveryAction::Reauthorize => "reauthorization_required", + ProviderCredentialRefreshRecoveryAction::FixConfiguration => "configuration_required", + ProviderCredentialRefreshRecoveryAction::Investigate => "investigation_required", + } + .to_string(); + state.last_error = failure.status.message().to_string(); + state.recovery_action = failure.recovery_action as i32; + state.failure_code = failure.failure_code.to_string(); + state.provider_error_subtype = failure + .provider_error_subtype + .unwrap_or_default() + .to_string(); + state.last_error_at_ms = now_ms; + state.next_refresh_at_ms = match failure.retry_schedule { + RefreshRetrySchedule::Short => { + now_ms.saturating_add(REFRESH_ERROR_RETRY_SECONDS.saturating_mul(1000)) + } + RefreshRetrySchedule::Configuration => { + now_ms.saturating_add(REFRESH_CONFIGURATION_RETRY_SECONDS.saturating_mul(1000)) + } + RefreshRetrySchedule::Parked => i64::MAX, + }; let new_version = persist_refresh_state_if_current(store, state, expected_version) .await? .ok_or_else(|| { @@ -670,7 +839,8 @@ pub async fn refresh_provider_credential( // Otherwise disabling providers_v2_enabled leaves already-configured refresh // states that the worker and manual rotation keep minting from. if let Err(err) = ensure_refresh_providers_v2_gate(store, &state).await { - persist_refresh_error_state(store, &mut state, expected_version, &err).await?; + let failure = RefreshFailure::from_status(&err); + persist_refresh_failure_state(store, &mut state, expected_version, &failure).await?; warn!( provider = %state.provider_name, credential_key = %state.credential_key, @@ -684,7 +854,7 @@ pub async fn refresh_provider_credential( let mint_result = match resolve_refresh_material(Some(credentials), &state).await { Ok(transient_state) => mint_credential(&transient_state).await, - Err(err) => Err(err), + Err(err) => Err(err.into()), }; match mint_result { Ok(minted) => { @@ -716,19 +886,32 @@ pub async fn refresh_provider_credential( { Ok(handles) => handles, Err(store_err) => { - let err = Status::failed_precondition(format!( - "the OAuth provider rotated the refresh token, but the replacement could not be stored; the grant must be re-authorized: {}", - store_err.message() - )); - persist_refresh_error_state(store, &mut state, expected_version, &err) - .await?; - return Err(err); + let failure = RefreshFailure::reauthorize( + Status::failed_precondition(format!( + "the OAuth provider rotated the refresh token, but the replacement could not be stored; the grant must be re-authorized: {}", + store_err.message() + )), + "oauth_rotated_refresh_token_store_failed", + None, + ); + persist_refresh_failure_state( + store, + &mut state, + expected_version, + &failure, + ) + .await?; + return Err(failure.into_status()); } }; let Some(handle) = staged_refresh_token_handles.get("refresh_token").cloned() else { - let err = Status::failed_precondition( - "the OAuth provider rotated the refresh token, but the credential driver returned no replacement handle; the grant must be re-authorized", + let failure = RefreshFailure::reauthorize( + Status::failed_precondition( + "the OAuth provider rotated the refresh token, but the credential driver returned no replacement handle; the grant must be re-authorized", + ), + "oauth_rotated_refresh_token_handle_missing", + None, ); cleanup_staged_refresh_material_handles( credentials, @@ -736,8 +919,9 @@ pub async fn refresh_provider_credential( &staged_refresh_token_handles, ) .await; - persist_refresh_error_state(store, &mut state, expected_version, &err).await?; - return Err(err); + persist_refresh_failure_state(store, &mut state, expected_version, &failure) + .await?; + return Err(failure.into_status()); }; if let Some(previous) = state .secret_material_handles @@ -757,6 +941,10 @@ pub async fn refresh_provider_credential( state.last_refresh_at_ms = now_ms; state.status = "refreshed".to_string(); state.last_error.clear(); + state.recovery_action = ProviderCredentialRefreshRecoveryAction::Unspecified as i32; + state.failure_code.clear(); + state.provider_error_subtype.clear(); + state.last_error_at_ms = 0; // Claim the refresh generation with a version-matched write BEFORE // touching the provider. It succeeds only if the refresh still holds @@ -800,7 +988,13 @@ pub async fn refresh_provider_credential( // upstream-valid grant. If that also fails, leave the // staged object intact for operator recovery rather than // deleting an irreplaceable rotated token. - persist_refresh_error_state(store, &mut state, expected_version, &err).await?; + persist_retryable_refresh_error_state( + store, + &mut state, + expected_version, + &err, + ) + .await?; return Err(err); } }; @@ -819,7 +1013,8 @@ pub async fn refresh_provider_credential( { // Reflect the failure on the state we just wrote; skip silently // if it was deleted concurrently (it is not recreated). - persist_refresh_error_state(store, &mut state, new_version, &err).await?; + let failure = RefreshFailure::from_status(&err); + persist_refresh_failure_state(store, &mut state, new_version, &failure).await?; warn!( provider = %state.provider_name, credential_key = %state.credential_key, @@ -860,9 +1055,9 @@ pub async fn refresh_provider_credential( } Ok(state) } - Err(err) => { + Err(failure) => { let now_ms = current_time_ms(); - persist_refresh_error_state(store, &mut state, expected_version, &err).await?; + persist_refresh_failure_state(store, &mut state, expected_version, &failure).await?; warn!( provider = %state.provider_name, credential_key = %state.credential_key, @@ -870,10 +1065,12 @@ pub async fn refresh_provider_credential( status = %state.status, next_refresh_at_ms = state.next_refresh_at_ms, seconds_until_refresh = seconds_until_ms(now_ms, state.next_refresh_at_ms), - error = %err, + recovery_action = ?failure.recovery_action, + failure_code = failure.failure_code, + error = %failure.status, "provider credential refresh errored" ); - Err(err) + Err(failure.into_status()) } } } @@ -1106,7 +1303,7 @@ async fn ensure_refresh_providers_v2_gate( async fn mint_credential( state: &StoredProviderCredentialRefreshState, -) -> Result { +) -> Result { let strategy = ProviderCredentialRefreshStrategy::try_from(state.strategy) .unwrap_or(ProviderCredentialRefreshStrategy::Unspecified); match strategy { @@ -1126,13 +1323,14 @@ async fn mint_credential( | ProviderCredentialRefreshStrategy::Static | ProviderCredentialRefreshStrategy::Unspecified => Err(Status::failed_precondition( format!("refresh strategy '{strategy:?}' cannot be minted by the gateway"), - )), + ) + .into()), } } async fn mint_oauth2_refresh_token( state: &StoredProviderCredentialRefreshState, -) -> Result { +) -> Result { let token_url = oauth2_token_url(state)?; let client_id = required_material(&state.material, "client_id")?; let refresh_token = required_material(&state.material, "refresh_token")?; @@ -1149,12 +1347,18 @@ async fn mint_oauth2_refresh_token( form.push(("scope".to_string(), scope)); } - request_token(&token_url, &form, state.max_lifetime_seconds).await + request_token( + &token_url, + &form, + state.max_lifetime_seconds, + OAuthGrantKind::UserRefreshToken, + ) + .await } async fn mint_oauth2_client_credentials( state: &StoredProviderCredentialRefreshState, -) -> Result { +) -> Result { let token_url = oauth2_token_url(state)?; let client_id = required_material(&state.material, "client_id")?; let client_secret = required_material(&state.material, "client_secret")?; @@ -1168,14 +1372,19 @@ async fn mint_oauth2_client_credentials( form.push(("scope".to_string(), scope)); } - request_token(&token_url, &form, state.max_lifetime_seconds).await + request_token( + &token_url, + &form, + state.max_lifetime_seconds, + OAuthGrantKind::NonInteractive, + ) + .await } async fn mint_google_service_account_jwt( state: &StoredProviderCredentialRefreshState, -) -> Result { +) -> Result { crate::install_jsonwebtoken_crypto_provider(); - let token_url = google_token_url(state); let client_email = required_material(&state.material, "client_email")?; let private_key = required_material(&state.material, "private_key")?; @@ -1183,7 +1392,8 @@ async fn mint_google_service_account_jwt( if scopes.is_empty() { return Err(Status::invalid_argument( "google_service_account_jwt requires at least one scope", - )); + ) + .into()); } let now_ms = current_time_ms(); let now_secs = now_ms / 1000; @@ -1216,12 +1426,18 @@ async fn mint_google_service_account_jwt( ), ("assertion".to_string(), assertion), ]; - request_token(&token_url, &form, lifetime_secs).await + request_token( + &token_url, + &form, + lifetime_secs, + OAuthGrantKind::NonInteractive, + ) + .await } async fn mint_aws_sts_assume_role( state: &StoredProviderCredentialRefreshState, -) -> Result { +) -> Result { let role_arn = required_material(&state.material, "role_arn")?; let session_name = material_value(&state.material, &["session_name"]) .unwrap_or_else(|| "openshell-sandbox".to_string()); @@ -1256,13 +1472,15 @@ async fn mint_aws_sts_assume_role( (None, None) if session_token.is_some() => { return Err(Status::invalid_argument( "aws_session_token requires aws_access_key_id and aws_secret_access_key", - )); + ) + .into()); } (None, None) => {} _ => { return Err(Status::invalid_argument( "aws_access_key_id and aws_secret_access_key must both be set or both omitted", - )); + ) + .into()); } } @@ -1346,7 +1564,8 @@ async fn request_token( token_url: &str, form: &[(String, String)], max_lifetime_seconds: i64, -) -> Result { + grant_kind: OAuthGrantKind, +) -> Result { let parsed = reqwest::Url::parse(token_url) .map_err(|_| Status::invalid_argument("token_url must be an absolute URL"))?; match parsed.scheme() { @@ -1355,7 +1574,8 @@ async fn request_token( _ => { return Err(Status::invalid_argument( "token_url must use https, except loopback http for local tests", - )); + ) + .into()); } } @@ -1368,20 +1588,41 @@ async fn request_token( .form(form) .send() .await - .map_err(|e| Status::unavailable(format!("token endpoint request failed: {e}")))?; + .map_err(|error| { + let error_kind = if error.is_timeout() { + "timeout" + } else if error.is_connect() { + "connect" + } else if error.is_request() { + "request" + } else { + "other" + }; + warn!( + error = %error.without_url(), + error_kind, + "OAuth token endpoint request failed" + ); + RefreshFailure::retryable( + Status::unavailable("token endpoint request failed"), + "oauth_token_endpoint_unavailable", + ) + })?; let status = response.status(); if !status.is_success() { - return Err(Status::failed_precondition(format!( - "token endpoint returned HTTP {status}" - ))); + let body = read_bounded_oauth_error_body(response).await; + return Err(classify_oauth_token_error(status, &body, grant_kind)); } - let token = response - .json::() - .await - .map_err(|_| Status::failed_precondition("token endpoint returned invalid JSON"))?; + let token = response.json::().await.map_err(|_| { + RefreshFailure::investigate( + Status::failed_precondition("token endpoint returned invalid JSON"), + "oauth_invalid_success_response", + ) + })?; if token.access_token.trim().is_empty() { - return Err(Status::failed_precondition( - "token endpoint returned empty access_token", + return Err(RefreshFailure::investigate( + Status::failed_precondition("token endpoint returned empty access_token"), + "oauth_empty_access_token", )); } let now_ms = current_time_ms(); @@ -1405,6 +1646,135 @@ async fn request_token( }) } +async fn read_bounded_oauth_error_body(mut response: reqwest::Response) -> Vec { + let mut body = Vec::new(); + while body.len() < MAX_OAUTH_ERROR_RESPONSE_BYTES { + let Ok(Some(chunk)) = response.chunk().await else { + break; + }; + let remaining = MAX_OAUTH_ERROR_RESPONSE_BYTES.saturating_sub(body.len()); + body.extend_from_slice(&chunk[..chunk.len().min(remaining)]); + if chunk.len() >= remaining { + break; + } + } + body +} + +fn classify_oauth_token_error( + status: reqwest::StatusCode, + body: &[u8], + grant_kind: OAuthGrantKind, +) -> RefreshFailure { + if status == reqwest::StatusCode::TOO_MANY_REQUESTS || status.is_server_error() { + return RefreshFailure::retryable( + Status::unavailable(format!("token endpoint returned HTTP {status}")), + "oauth_token_endpoint_retryable", + ); + } + + let Ok(error_response) = serde_json::from_slice::(body) else { + return RefreshFailure::investigate( + Status::failed_precondition(format!( + "token endpoint returned HTTP {status} without a recognized OAuth error" + )), + "oauth_unrecognized_error_response", + ); + }; + + match error_response.error.as_str() { + "invalid_grant" if grant_kind == OAuthGrantKind::UserRefreshToken => { + let subtype = error_response + .error_subtype + .as_deref() + .filter(|subtype| *subtype == "invalid_rapt") + .map(|_| "invalid_rapt"); + let message = if subtype.is_some() { + "OAuth refresh grant requires interactive reauthorization (invalid_grant/invalid_rapt)" + } else { + "OAuth refresh grant is no longer usable (invalid_grant); user reauthorization is required" + }; + RefreshFailure::reauthorize( + Status::failed_precondition(message), + "oauth_invalid_grant", + subtype, + ) + } + "invalid_client" => RefreshFailure::fix_configuration( + Status::failed_precondition( + "OAuth token endpoint rejected the client configuration (invalid_client)", + ), + "oauth_invalid_client", + ), + "unauthorized_client" => RefreshFailure::fix_configuration( + Status::failed_precondition( + "OAuth token endpoint rejected the client grant (unauthorized_client)", + ), + "oauth_unauthorized_client", + ), + "invalid_scope" => RefreshFailure::fix_configuration( + Status::failed_precondition( + "OAuth token endpoint rejected the configured scopes (invalid_scope)", + ), + "oauth_invalid_scope", + ), + "unsupported_grant_type" => RefreshFailure::fix_configuration( + Status::failed_precondition( + "OAuth token endpoint rejected the configured grant type (unsupported_grant_type)", + ), + "oauth_unsupported_grant_type", + ), + "admin_policy_enforced" => RefreshFailure::fix_configuration( + Status::failed_precondition( + "OAuth access is blocked by an administrator policy (admin_policy_enforced)", + ), + "oauth_admin_policy_enforced", + ), + "access_denied" + if error_response.error_subtype.as_deref() == Some("admin_policy_enforced") => + { + RefreshFailure::fix_configuration_with_subtype( + Status::failed_precondition( + "OAuth access is blocked by an administrator policy (access_denied/admin_policy_enforced)", + ), + "oauth_admin_policy_enforced", + "admin_policy_enforced", + ) + } + "access_denied" if grant_kind == OAuthGrantKind::UserRefreshToken => { + RefreshFailure::reauthorize( + Status::failed_precondition( + "OAuth access was denied; user reauthorization is required (access_denied)", + ), + "oauth_access_denied", + None, + ) + } + "access_denied" => RefreshFailure::fix_configuration( + Status::failed_precondition( + "OAuth access was denied for the non-interactive grant (access_denied)", + ), + "oauth_access_denied", + ), + "invalid_grant" => RefreshFailure::fix_configuration( + Status::failed_precondition( + "OAuth token endpoint rejected the non-interactive grant (invalid_grant)", + ), + "oauth_invalid_grant", + ), + "server_error" | "temporarily_unavailable" => RefreshFailure::retryable( + Status::unavailable("OAuth token endpoint reported a temporary failure"), + "oauth_token_endpoint_retryable", + ), + _ => RefreshFailure::investigate( + Status::failed_precondition(format!( + "token endpoint returned HTTP {status} with an unrecognized OAuth error" + )), + "oauth_unrecognized_error", + ), + } +} + pub fn refresh_scopes(state: &StoredProviderCredentialRefreshState) -> Vec { if !state.scopes.is_empty() { return state.scopes.clone(); @@ -1677,11 +2047,13 @@ async fn run_refresh_worker_tick( #[cfg(test)] mod tests { use super::{ - NewRefreshStateConfig, delete_refresh_state_with_credentials, - effective_authorization_epoch, enqueue_pending_secret_deletion, get_refresh_state, - list_all_refresh_states, list_refresh_states_for_provider, new_refresh_state, - put_refresh_state, refresh_material_scope, refresh_provider_credential, refresh_state_name, - refresh_strategy_name, run_refresh_worker_tick, seconds_until_ms, + MAX_OAUTH_ERROR_RESPONSE_BYTES, NewRefreshStateConfig, OAuthGrantKind, RefreshFailure, + RefreshRetrySchedule, Status, classify_oauth_token_error, + delete_refresh_state_with_credentials, effective_authorization_epoch, + enqueue_pending_secret_deletion, get_refresh_state, list_all_refresh_states, + list_refresh_states_for_provider, new_refresh_state, put_refresh_state, + read_bounded_oauth_error_body, refresh_material_scope, refresh_provider_credential, + refresh_state_name, refresh_strategy_name, run_refresh_worker_tick, seconds_until_ms, validate_secret_material_references, }; use crate::credentials::CredentialRuntime; @@ -1689,7 +2061,8 @@ mod tests { use openshell_core::Config; use openshell_core::proto::datamodel::v1::ObjectMeta; use openshell_core::proto::{ - CredentialHandle, Provider, ProviderCredentialRefreshStrategy, Sandbox, SandboxSpec, + CredentialHandle, Provider, ProviderCredentialRefreshRecoveryAction, + ProviderCredentialRefreshStrategy, Sandbox, SandboxSpec, StoredProviderCredentialRefreshState, }; use openshell_core::{ObjectId, ObjectName, ObjectWorkspace}; @@ -1806,6 +2179,37 @@ mod tests { assert_eq!(refresh_strategy_name(i32::MAX), "unspecified"); } + #[test] + fn local_refresh_statuses_default_to_safe_recovery_actions() { + for status in [ + Status::invalid_argument("missing material"), + Status::failed_precondition("strategy cannot be minted"), + Status::permission_denied("client is not allowed"), + ] { + let failure = RefreshFailure::from(status); + assert_eq!( + failure.recovery_action, + ProviderCredentialRefreshRecoveryAction::FixConfiguration + ); + assert_eq!(failure.failure_code, "refresh_configuration_invalid"); + assert_eq!(failure.retry_schedule, RefreshRetrySchedule::Configuration); + } + + let retry = RefreshFailure::from(Status::unavailable("temporary backend outage")); + assert_eq!( + retry.recovery_action, + ProviderCredentialRefreshRecoveryAction::Retry + ); + assert_eq!(retry.retry_schedule, RefreshRetrySchedule::Short); + + let investigate = RefreshFailure::from(Status::unknown("unclassified failure")); + assert_eq!( + investigate.recovery_action, + ProviderCredentialRefreshRecoveryAction::Investigate + ); + assert_eq!(investigate.retry_schedule, RefreshRetrySchedule::Short); + } + #[test] fn pending_secret_deletions_preserve_multiple_generations_for_one_key() { let mut state = StoredProviderCredentialRefreshState::default(); @@ -1862,6 +2266,388 @@ mod tests { validate_secret_material_references(&state).unwrap(); } + #[test] + fn oauth_invalid_grant_requires_user_reauthorization_without_exposing_description() { + let failure = classify_oauth_token_error( + reqwest::StatusCode::BAD_REQUEST, + br#"{"error":"invalid_grant","error_subtype":"invalid_rapt","error_description":"sensitive provider detail"}"#, + OAuthGrantKind::UserRefreshToken, + ); + + assert_eq!( + failure.recovery_action, + ProviderCredentialRefreshRecoveryAction::Reauthorize + ); + assert_eq!(failure.failure_code, "oauth_invalid_grant"); + assert_eq!(failure.provider_error_subtype, Some("invalid_rapt")); + assert_eq!(failure.retry_schedule, RefreshRetrySchedule::Parked); + assert!( + failure + .status + .message() + .contains("interactive reauthorization") + ); + assert!( + !failure + .status + .message() + .contains("sensitive provider detail") + ); + } + + #[test] + fn oauth_invalid_grant_ignores_non_string_optional_subtype() { + let failure = classify_oauth_token_error( + reqwest::StatusCode::BAD_REQUEST, + br#"{"error":"invalid_grant","error_subtype":{"vendor":"value"}}"#, + OAuthGrantKind::UserRefreshToken, + ); + + assert_eq!( + failure.recovery_action, + ProviderCredentialRefreshRecoveryAction::Reauthorize + ); + assert_eq!(failure.failure_code, "oauth_invalid_grant"); + assert_eq!(failure.provider_error_subtype, None); + assert_eq!(failure.retry_schedule, RefreshRetrySchedule::Parked); + } + + #[test] + fn oauth_noninteractive_invalid_grant_requires_configuration_fix() { + let failure = classify_oauth_token_error( + reqwest::StatusCode::BAD_REQUEST, + br#"{"error":"invalid_grant"}"#, + OAuthGrantKind::NonInteractive, + ); + + assert_eq!( + failure.recovery_action, + ProviderCredentialRefreshRecoveryAction::FixConfiguration + ); + assert_eq!(failure.failure_code, "oauth_invalid_grant"); + assert_eq!(failure.retry_schedule, RefreshRetrySchedule::Configuration); + } + + #[test] + fn oauth_admin_policy_subtype_requires_configuration_fix() { + let failure = classify_oauth_token_error( + reqwest::StatusCode::BAD_REQUEST, + br#"{"error":"access_denied","error_subtype":"admin_policy_enforced"}"#, + OAuthGrantKind::UserRefreshToken, + ); + + assert_eq!( + failure.recovery_action, + ProviderCredentialRefreshRecoveryAction::FixConfiguration + ); + assert_eq!(failure.failure_code, "oauth_admin_policy_enforced"); + assert_eq!( + failure.provider_error_subtype, + Some("admin_policy_enforced") + ); + assert_eq!(failure.retry_schedule, RefreshRetrySchedule::Configuration); + } + + #[test] + fn oauth_access_denied_recovery_depends_on_grant_kind() { + let user_failure = classify_oauth_token_error( + reqwest::StatusCode::FORBIDDEN, + br#"{"error":"access_denied"}"#, + OAuthGrantKind::UserRefreshToken, + ); + assert_eq!( + user_failure.recovery_action, + ProviderCredentialRefreshRecoveryAction::Reauthorize + ); + assert_eq!(user_failure.failure_code, "oauth_access_denied"); + assert_eq!(user_failure.retry_schedule, RefreshRetrySchedule::Parked); + + let service_failure = classify_oauth_token_error( + reqwest::StatusCode::FORBIDDEN, + br#"{"error":"access_denied"}"#, + OAuthGrantKind::NonInteractive, + ); + assert_eq!( + service_failure.recovery_action, + ProviderCredentialRefreshRecoveryAction::FixConfiguration + ); + assert_eq!(service_failure.failure_code, "oauth_access_denied"); + assert_eq!( + service_failure.retry_schedule, + RefreshRetrySchedule::Configuration + ); + } + + #[test] + fn oauth_server_failure_remains_retryable() { + let failure = classify_oauth_token_error( + reqwest::StatusCode::SERVICE_UNAVAILABLE, + br#"{"error":"invalid_grant"}"#, + OAuthGrantKind::UserRefreshToken, + ); + + assert_eq!( + failure.recovery_action, + ProviderCredentialRefreshRecoveryAction::Retry + ); + assert_eq!(failure.failure_code, "oauth_token_endpoint_retryable"); + assert_eq!(failure.retry_schedule, RefreshRetrySchedule::Short); + } + + #[test] + fn unrecognized_oauth_error_requests_investigation_without_echoing_body() { + let failure = classify_oauth_token_error( + reqwest::StatusCode::BAD_REQUEST, + br#"{"error":"vendor_secret_error","error_description":"do not expose me"}"#, + OAuthGrantKind::UserRefreshToken, + ); + + assert_eq!( + failure.recovery_action, + ProviderCredentialRefreshRecoveryAction::Investigate + ); + assert_eq!(failure.failure_code, "oauth_unrecognized_error"); + assert_eq!(failure.retry_schedule, RefreshRetrySchedule::Short); + assert!(!failure.status.message().contains("vendor_secret_error")); + assert!(!failure.status.message().contains("do not expose me")); + } + + #[test] + fn html_and_malformed_oauth_errors_are_investigated_without_echoing_body() { + for body in [b"issuer failure".as_slice(), b"{".as_slice()] { + let failure = classify_oauth_token_error( + reqwest::StatusCode::BAD_REQUEST, + body, + OAuthGrantKind::UserRefreshToken, + ); + + assert_eq!( + failure.recovery_action, + ProviderCredentialRefreshRecoveryAction::Investigate + ); + assert_eq!(failure.failure_code, "oauth_unrecognized_error_response"); + assert!(!failure.status.message().contains("issuer failure")); + } + } + + #[tokio::test] + async fn oversized_oauth_error_body_is_bounded_and_investigated() { + let mock_server = MockServer::start().await; + Mock::given(method("GET")) + .and(path("/oversized")) + .respond_with(ResponseTemplate::new(400).set_body_bytes(vec![ + b'x'; + MAX_OAUTH_ERROR_RESPONSE_BYTES + + 512 + ])) + .mount(&mock_server) + .await; + + let response = reqwest::get(format!("{}/oversized", mock_server.uri())) + .await + .unwrap(); + let status = response.status(); + let body = read_bounded_oauth_error_body(response).await; + assert_eq!(body.len(), MAX_OAUTH_ERROR_RESPONSE_BYTES); + + let failure = classify_oauth_token_error(status, &body, OAuthGrantKind::UserRefreshToken); + assert_eq!( + failure.recovery_action, + ProviderCredentialRefreshRecoveryAction::Investigate + ); + assert_eq!(failure.failure_code, "oauth_unrecognized_error_response"); + } + + #[tokio::test] + async fn invalid_local_oauth_configuration_retries_hourly() { + let store = test_store().await; + let provider = provider("invalid-local-config", "outlook"); + store.put_message(&provider).await.unwrap(); + let state = new_refresh_state( + &provider, + "default", + "MS_GRAPH_ACCESS_TOKEN", + NewRefreshStateConfig { + strategy: ProviderCredentialRefreshStrategy::Oauth2RefreshToken, + material: HashMap::from([ + ("client_id".to_string(), "client-id".to_string()), + ("refresh_token".to_string(), "refresh-token".to_string()), + ]), + secret_material_keys: vec!["refresh_token".to_string()], + expires_at_ms: 0, + token_url: "not-an-absolute-url".to_string(), + scopes: Vec::new(), + refresh_before_seconds: 30, + max_lifetime_seconds: 60, + additional_output_keys: HashMap::new(), + }, + ) + .unwrap(); + put_refresh_state(&store, &state).await.unwrap(); + + let err = refresh_provider_credential( + &store, + "default", + &test_credentials(), + None, + "invalid-local-config", + "MS_GRAPH_ACCESS_TOKEN", + ) + .await + .unwrap_err(); + + assert_eq!(err.code(), tonic::Code::InvalidArgument); + let stored = get_refresh_state( + &store, + "default", + provider.object_id(), + "MS_GRAPH_ACCESS_TOKEN", + ) + .await + .unwrap() + .unwrap(); + assert_eq!(stored.status, "configuration_required"); + assert_eq!( + stored.recovery_action, + ProviderCredentialRefreshRecoveryAction::FixConfiguration as i32 + ); + assert_eq!(stored.failure_code, "refresh_configuration_invalid"); + assert_eq!( + stored.next_refresh_at_ms - stored.last_error_at_ms, + 60 * 60 * 1000 + ); + } + + #[tokio::test] + async fn disabled_provider_gate_requires_configuration_and_retries_hourly() { + let store = test_store().await; + let provider = provider("disabled-provider-gate", "aws-s3"); + store.put_message(&provider).await.unwrap(); + let state = new_refresh_state( + &provider, + "default", + "AWS_ACCESS_KEY_ID", + NewRefreshStateConfig { + strategy: ProviderCredentialRefreshStrategy::AwsStsAssumeRole, + material: HashMap::from([( + "role_arn".to_string(), + "arn:aws:iam::123456789012:role/test".to_string(), + )]), + secret_material_keys: Vec::new(), + expires_at_ms: 0, + token_url: String::new(), + scopes: Vec::new(), + refresh_before_seconds: 30, + max_lifetime_seconds: 60, + additional_output_keys: HashMap::new(), + }, + ) + .unwrap(); + put_refresh_state(&store, &state).await.unwrap(); + + let err = refresh_provider_credential( + &store, + "default", + &test_credentials(), + None, + "disabled-provider-gate", + "AWS_ACCESS_KEY_ID", + ) + .await + .unwrap_err(); + + assert_eq!(err.code(), tonic::Code::FailedPrecondition); + let stored = + get_refresh_state(&store, "default", provider.object_id(), "AWS_ACCESS_KEY_ID") + .await + .unwrap() + .unwrap(); + assert_eq!(stored.status, "configuration_required"); + assert_eq!( + stored.recovery_action, + ProviderCredentialRefreshRecoveryAction::FixConfiguration as i32 + ); + assert_eq!(stored.failure_code, "refresh_configuration_invalid"); + assert_eq!( + stored.next_refresh_at_ms - stored.last_error_at_ms, + 60 * 60 * 1000 + ); + } + + #[tokio::test] + async fn oauth_invalid_grant_persists_terminal_reauthorization_status() { + let mock_server = MockServer::start().await; + Mock::given(method("POST")) + .and(path("/token")) + .respond_with(ResponseTemplate::new(400).set_body_json(serde_json::json!({ + "error": "invalid_grant", + "error_subtype": "invalid_rapt", + "error_description": "provider-controlled detail" + }))) + .mount(&mock_server) + .await; + + let store = test_store().await; + let provider = provider("expired-grant", "outlook"); + store.put_message(&provider).await.unwrap(); + let state = new_refresh_state( + &provider, + "default", + "MS_GRAPH_ACCESS_TOKEN", + NewRefreshStateConfig { + strategy: ProviderCredentialRefreshStrategy::Oauth2RefreshToken, + material: HashMap::from([ + ("client_id".to_string(), "client-id".to_string()), + ( + "refresh_token".to_string(), + "expired-refresh-token".to_string(), + ), + ]), + secret_material_keys: vec!["refresh_token".to_string()], + expires_at_ms: 0, + token_url: format!("{}/token", mock_server.uri()), + scopes: Vec::new(), + refresh_before_seconds: 30, + max_lifetime_seconds: 60, + additional_output_keys: HashMap::new(), + }, + ) + .unwrap(); + put_refresh_state(&store, &state).await.unwrap(); + + let err = refresh_provider_credential( + &store, + "default", + &test_credentials(), + None, + "expired-grant", + "MS_GRAPH_ACCESS_TOKEN", + ) + .await + .unwrap_err(); + + assert_eq!(err.code(), tonic::Code::FailedPrecondition); + let stored = get_refresh_state( + &store, + "default", + provider.object_id(), + "MS_GRAPH_ACCESS_TOKEN", + ) + .await + .unwrap() + .unwrap(); + assert_eq!(stored.status, "reauthorization_required"); + assert_eq!(stored.next_refresh_at_ms, i64::MAX); + assert_eq!( + stored.recovery_action, + ProviderCredentialRefreshRecoveryAction::Reauthorize as i32 + ); + assert_eq!(stored.failure_code, "oauth_invalid_grant"); + assert_eq!(stored.provider_error_subtype, "invalid_rapt"); + assert!(stored.last_error_at_ms > 0); + assert!(!stored.last_error.contains("provider-controlled detail")); + } + #[tokio::test] async fn oauth2_client_credentials_refresh_mints_and_persists_access_token() { let mock_server = MockServer::start().await; @@ -1884,7 +2670,7 @@ mod tests { let provider = provider("my-graph", "outlook"); store.put_message(&provider).await.unwrap(); let before_refresh_ms = current_time_ms(); - let state = new_refresh_state( + let mut state = new_refresh_state( &provider, "default", "MS_GRAPH_ACCESS_TOKEN", @@ -1904,6 +2690,12 @@ mod tests { }, ) .unwrap(); + state.status = "investigation_required".to_string(); + state.last_error = "safe prior error".to_string(); + state.recovery_action = ProviderCredentialRefreshRecoveryAction::Investigate as i32; + state.failure_code = "oauth_unrecognized_error".to_string(); + state.provider_error_subtype = "prior_subtype".to_string(); + state.last_error_at_ms = current_time_ms(); put_refresh_state(&store, &state).await.unwrap(); let authorization_epoch = state.authorization_epoch.clone(); let credentials = test_credentials(); @@ -1924,6 +2716,13 @@ mod tests { assert!(refreshed.next_refresh_at_ms > 0); assert!(refreshed.expires_at_ms <= before_refresh_ms + 120_000); assert!(refreshed.last_error.is_empty()); + assert_eq!( + refreshed.recovery_action, + ProviderCredentialRefreshRecoveryAction::Unspecified as i32 + ); + assert!(refreshed.failure_code.is_empty()); + assert!(refreshed.provider_error_subtype.is_empty()); + assert_eq!(refreshed.last_error_at_ms, 0); let stored = store .get_message_by_name::("default", "my-graph") @@ -2135,7 +2934,16 @@ mod tests { .await .unwrap() .unwrap(); - assert_eq!(stored_state.status, "error"); + assert_eq!(stored_state.status, "configuration_required"); + assert_eq!( + stored_state.recovery_action, + ProviderCredentialRefreshRecoveryAction::FixConfiguration as i32 + ); + assert_eq!(stored_state.failure_code, "refresh_configuration_invalid"); + assert_eq!( + stored_state.next_refresh_at_ms - stored_state.last_error_at_ms, + 60 * 60 * 1000 + ); assert!(stored_state.last_error.contains("MS_GRAPH_ACCESS_TOKEN")); let stored_provider = store .get_message_by_name::("default", "refreshing-graph") @@ -2366,7 +3174,7 @@ mod tests { } #[tokio::test] - async fn rotated_refresh_token_store_failure_persists_error_and_backoff() { + async fn rotated_refresh_token_store_failure_requires_reauthorization() { let mock_server = MockServer::start().await; Mock::given(method("POST")) .and(path("/token")) @@ -2415,7 +3223,6 @@ mod tests { state.material.remove("refresh_token"); put_refresh_state(&store, &state).await.unwrap(); credentials.fail_next_store(); - let before = current_time_ms(); let err = refresh_provider_credential( &store, @@ -2439,9 +3246,17 @@ mod tests { .await .unwrap() .unwrap(); - assert_eq!(stored.status, "error"); + assert_eq!(stored.status, "reauthorization_required"); assert!(stored.last_error.contains("must be re-authorized")); - assert!(stored.next_refresh_at_ms > before); + assert_eq!(stored.next_refresh_at_ms, i64::MAX); + assert_eq!( + stored.recovery_action, + ProviderCredentialRefreshRecoveryAction::Reauthorize as i32 + ); + assert_eq!( + stored.failure_code, + "oauth_rotated_refresh_token_store_failed" + ); assert_eq!(credentials.stored_credential_count(), Some(1)); assert_eq!( credentials @@ -2586,6 +3401,58 @@ mod tests { ); } + #[tokio::test] + async fn refresh_worker_skips_parked_reauthorization_state() { + let store = test_store().await; + let provider = provider("parked-refresh", "outlook"); + store.put_message(&provider).await.unwrap(); + let mut state = new_refresh_state( + &provider, + "default", + "MS_GRAPH_ACCESS_TOKEN", + NewRefreshStateConfig { + strategy: ProviderCredentialRefreshStrategy::Oauth2RefreshToken, + material: HashMap::new(), + secret_material_keys: Vec::new(), + expires_at_ms: 0, + token_url: "https://issuer.example/token".to_string(), + scopes: Vec::new(), + refresh_before_seconds: 30, + max_lifetime_seconds: 60, + additional_output_keys: HashMap::new(), + }, + ) + .unwrap(); + state.status = "reauthorization_required".to_string(); + state.last_error = "OAuth refresh grant is no longer usable".to_string(); + state.recovery_action = ProviderCredentialRefreshRecoveryAction::Reauthorize as i32; + state.failure_code = "oauth_invalid_grant".to_string(); + state.last_error_at_ms = current_time_ms(); + state.next_refresh_at_ms = i64::MAX; + put_refresh_state(&store, &state).await.unwrap(); + + run_refresh_worker_tick(&store, Some(&test_credentials()), None) + .await + .unwrap(); + + let stored = get_refresh_state( + &store, + "default", + provider.object_id(), + "MS_GRAPH_ACCESS_TOKEN", + ) + .await + .unwrap() + .unwrap(); + assert_eq!(stored.status, "reauthorization_required"); + assert_eq!(stored.next_refresh_at_ms, i64::MAX); + assert_eq!(stored.failure_code, "oauth_invalid_grant"); + assert_eq!( + stored.recovery_action, + ProviderCredentialRefreshRecoveryAction::Reauthorize as i32 + ); + } + #[tokio::test] async fn refresh_worker_finalizes_tombstoned_refresh_material() { let store = test_store().await; diff --git a/docs/sandboxes/providers-v2.mdx b/docs/sandboxes/providers-v2.mdx index 96ce68ab3..7a0f96b5a 100644 --- a/docs/sandboxes/providers-v2.mdx +++ b/docs/sandboxes/providers-v2.mdx @@ -663,6 +663,21 @@ The gateway sends a complete host, port, and path binding for every emitted stat Refresh configuration is stored separately from the current injectable credential value. Non-secret refresh configuration remains in the refresh-state record. Secret material is resolved from the active credential driver only while the gateway mints a new short-lived token. The gateway writes the token back through credential storage and updates credential expiry metadata. If an OAuth issuer rotates its refresh token, the gateway stages the replacement through credential storage before committing the refresh generation and removes the previous handle afterward. +For OAuth token endpoint failures, the gateway parses the bounded standard OAuth +error response and reports a structured recovery action through `provider +refresh status`. It does not retain provider error descriptions or raw response +bodies. The recovery actions are: + +- `retry`: a network, rate-limit, or temporary issuer failure; the worker retries automatically. +- `reauthorize`: a user refresh grant is no longer usable, such as `invalid_grant`; obtain a new grant and run `provider refresh configure`. +- `fix_configuration`: the OAuth client, scopes, grant type, or administrator policy must be corrected. +- `investigate`: the issuer returned an unrecognized response; the worker retries, but the operator should investigate persistent failures. + +The gateway parks `reauthorize` and `fix_configuration` failures instead of +retrying them every worker tick. A manual `provider refresh rotate` still +attempts the exchange. The current access token remains available only until +its recorded expiry, after which credential resolution fails closed. + Before OpenShell 0.1.0, refresh-state and credential-driver migrations are not supported. Upgrading from a build that stored refresh material inline requires reconfiguring the refresh grant. To change credential drivers, delete or @@ -770,11 +785,22 @@ Check refresh status: openshell provider refresh status my-graph ``` +The status table includes `RECOVERY` and `FAILURE_CODE`. Clients should use the +structured recovery action rather than parsing `LAST_ERROR`. For example, +`oauth_invalid_grant` with recovery action `reauthorize` means the user must +complete OAuth authorization again; `oauth_invalid_client` with +`fix_configuration` means changing the user login alone will not repair the +grant. OpenShell parks `reauthorize` failures until an explicit rotation or +reconfiguration. It retries `fix_configuration` failures hourly so externally +repaired clocks, policies, or credential-store values can recover without +creating rapid token-endpoint traffic. Transient and unrecognized failures use +the existing bounded 60-second retry interval. + The status table reports operational state without printing token values or refresh material: ```text -PROVIDER CREDENTIAL_KEY STRATEGY STATUS EXPIRES_AT NEXT_REFRESH LAST_REFRESH LAST_ERROR -my-graph MS_GRAPH_ACCESS_TOKEN oauth2_refresh_token refreshed 2026-06-01 00:00:00 2026-05-31 23:50:00 2026-05-31 23:00:00 - +PROVIDER CREDENTIAL_KEY STRATEGY STATUS RECOVERY EXPIRES_AT NEXT_REFRESH LAST_REFRESH FAILURE_CODE LAST_ERROR +my-graph MS_GRAPH_ACCESS_TOKEN oauth2_refresh_token reauthorization_required reauthorize 2026-06-01 00:00:00 - 2026-05-31 23:00:00 oauth_invalid_grant OAuth refresh grant is no longer usable ``` When no refresh configuration exists, the CLI distinguishes whole-provider checks from credential-specific checks: diff --git a/e2e/rust/Cargo.toml b/e2e/rust/Cargo.toml index 42f989ce4..44881e168 100644 --- a/e2e/rust/Cargo.toml +++ b/e2e/rust/Cargo.toml @@ -34,6 +34,7 @@ e2e-kubernetes-workspace-operator = ["e2e-kubernetes"] e2e-podman = ["e2e", "e2e-host-gateway", "e2e-local-container-driver"] e2e-podman-gpu = ["e2e-podman", "e2e-gpu"] e2e-oidc-pkce = [] +e2e-provider-refresh-keycloak = [] e2e-vm = ["e2e", "e2e-host-gateway"] [[test]] @@ -41,6 +42,11 @@ name = "oidc_pkce" path = "tests/oidc_pkce.rs" required-features = ["e2e-oidc-pkce"] +[[test]] +name = "provider_refresh_keycloak" +path = "tests/provider_refresh_keycloak.rs" +required-features = ["e2e-provider-refresh-keycloak"] + [[test]] name = "custom_image" path = "tests/custom_image.rs" diff --git a/e2e/rust/tests/provider_refresh_keycloak.rs b/e2e/rust/tests/provider_refresh_keycloak.rs new file mode 100644 index 000000000..57bcb45fb --- /dev/null +++ b/e2e/rust/tests/provider_refresh_keycloak.rs @@ -0,0 +1,417 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![cfg(target_os = "linux")] + +//! End-to-end coverage for provider OAuth refresh recovery against Keycloak. +//! +//! OpenShell itself uses the local gateway's mTLS authentication. Keycloak is +//! only the provider token issuer: the test refreshes a valid grant, revokes +//! its Keycloak session, and verifies that the gateway reports the next +//! refresh as requiring user reauthorization. + +use std::io::Write as _; +use std::process::{Output, Stdio}; + +use openshell_e2e::harness::binary::openshell_cmd; +use serde_json::Value; +use tempfile::{Builder as TempFileBuilder, NamedTempFile}; +use tokio::io::AsyncWriteExt as _; +use tokio::process::Command; + +const PROVIDER_NAME: &str = "e2e-keycloak-refresh"; +const PROFILE_ID: &str = "e2e-keycloak-refresh"; +const CREDENTIAL_KEY: &str = "KEYCLOAK_ACCESS_TOKEN"; + +fn combined_output(output: &Output) -> String { + format!( + "{}{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ) +} + +async fn run_cli(args: &[&str], env: &[(&str, &str)]) -> Result { + openshell_cmd() + .args(args) + .env("NO_COLOR", "1") + .envs(env.iter().copied()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + .await + .map_err(|error| format!("run openshell command: {error}")) +} + +async fn run_cli_success(args: &[&str], env: &[(&str, &str)]) -> Result { + let output = run_cli(args, env).await?; + let combined = combined_output(&output); + if !output.status.success() { + return Err(format!( + "openshell command failed (exit {:?}):\n{combined}", + output.status.code() + )); + } + Ok(combined) +} + +async fn acquire_keycloak_grant( + issuer: &str, + username: &str, + password: &str, +) -> Result<(String, String), String> { + let token_endpoint = format!("{issuer}/protocol/openid-connect/token"); + let username_form = format!("username={username}"); + let password_form = format!("password={password}"); + let output = Command::new("curl") + .args([ + "--fail", + "--silent", + "--show-error", + "--request", + "POST", + &token_endpoint, + "--data-urlencode", + "grant_type=password", + "--data-urlencode", + "client_id=openshell-cli", + "--data-urlencode", + &username_form, + "--data-urlencode", + &password_form, + "--data-urlencode", + "scope=openid", + ]) + .output() + .await + .map_err(|error| format!("request Keycloak grant: {error}"))?; + if !output.status.success() { + return Err(format!( + "Keycloak grant request failed (exit {:?}): {}", + output.status.code(), + String::from_utf8_lossy(&output.stderr) + )); + } + + let response: Value = serde_json::from_slice(&output.stdout) + .map_err(|error| format!("decode Keycloak grant response: {error}"))?; + let access_token = response + .get("access_token") + .and_then(Value::as_str) + .filter(|value| !value.is_empty()) + .ok_or_else(|| "Keycloak grant response omitted access_token".to_string())?; + let refresh_token = response + .get("refresh_token") + .and_then(Value::as_str) + .filter(|value| !value.is_empty()) + .ok_or_else(|| "Keycloak grant response omitted refresh_token".to_string())?; + Ok((access_token.to_string(), refresh_token.to_string())) +} + +async fn revoke_keycloak_grant(issuer: &str, refresh_token: &str) -> Result<(), String> { + let logout_endpoint = format!("{issuer}/protocol/openid-connect/logout"); + let mut child = Command::new("curl") + .args([ + "--fail", + "--silent", + "--show-error", + "--output", + "/dev/null", + "--request", + "POST", + &logout_endpoint, + "--data-urlencode", + "client_id=openshell-cli", + "--data-urlencode", + "refresh_token@-", + ]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .map_err(|error| format!("start Keycloak logout request: {error}"))?; + child + .stdin + .take() + .ok_or_else(|| "Keycloak logout stdin was not piped".to_string())? + .write_all(refresh_token.as_bytes()) + .await + .map_err(|error| format!("write Keycloak logout request: {error}"))?; + let output = child + .wait_with_output() + .await + .map_err(|error| format!("wait for Keycloak logout request: {error}"))?; + if !output.status.success() { + return Err(format!( + "Keycloak logout failed (exit {:?}): {}", + output.status.code(), + String::from_utf8_lossy(&output.stderr) + )); + } + Ok(()) +} + +fn write_profile(issuer: &str) -> Result { + let mut file = TempFileBuilder::new() + .suffix(".yaml") + .tempfile() + .map_err(|error| format!("create provider profile: {error}"))?; + let profile = format!( + r#"id: {PROFILE_ID} +display_name: Keycloak provider refresh E2E +category: other +credentials: + - name: access_token + env_vars: [{CREDENTIAL_KEY}] + required: true + auth_style: bearer + header_name: authorization + refresh: + strategy: oauth2_refresh_token + token_url: {issuer}/protocol/openid-connect/token + scopes: [openid] + refresh_before_seconds: 60 + max_lifetime_seconds: 3600 + material: + - name: client_id + required: true + - name: refresh_token + required: true + secret: true +endpoints: + - host: keycloak.test.invalid + port: 443 + protocol: rest + access: read-only + enforcement: enforce +binaries: + - /usr/bin/curl +"# + ); + file.write_all(profile.as_bytes()) + .map_err(|error| format!("write provider profile: {error}"))?; + file.flush() + .map_err(|error| format!("flush provider profile: {error}"))?; + Ok(file) +} + +async fn delete_provider_resources() { + let _ = run_cli(&["provider", "delete", PROVIDER_NAME], &[]).await; + let _ = run_cli(&["provider", "profile", "delete", PROFILE_ID], &[]).await; +} + +async fn read_providers_v2_setting() -> Result, String> { + let output = run_cli(&["settings", "get", "--global", "--json"], &[]).await?; + if !output.status.success() { + return Err(format!( + "read global settings failed (exit {:?}):\n{}", + output.status.code(), + combined_output(&output) + )); + } + let response: Value = serde_json::from_slice(&output.stdout) + .map_err(|error| format!("decode global settings: {error}"))?; + let value = response + .get("settings") + .and_then(|settings| settings.get("providers_v2_enabled")) + .and_then(Value::as_str) + .filter(|value| *value != "") + .map(ToString::to_string); + Ok(value) +} + +async fn restore_providers_v2_setting(previous: Option<&str>) -> Result<(), String> { + if let Some(value) = previous { + run_cli_success( + &[ + "settings", + "set", + "--global", + "--key", + "providers_v2_enabled", + "--value", + value, + "--yes", + ], + &[], + ) + .await?; + } else { + run_cli_success( + &[ + "settings", + "delete", + "--global", + "--key", + "providers_v2_enabled", + "--yes", + ], + &[], + ) + .await?; + } + Ok(()) +} + +#[tokio::test] +async fn revoked_refresh_grant_requires_user_reauthorization() -> Result<(), String> { + let issuer = std::env::var("OPENSHELL_E2E_OIDC_ISSUER") + .map_err(|_| "OPENSHELL_E2E_OIDC_ISSUER is required".to_string())?; + let username = std::env::var("OPENSHELL_E2E_OIDC_USERNAME") + .map_err(|_| "OPENSHELL_E2E_OIDC_USERNAME is required".to_string())?; + let password = std::env::var("OPENSHELL_E2E_OIDC_PASSWORD") + .map_err(|_| "OPENSHELL_E2E_OIDC_PASSWORD is required".to_string())?; + let previous_providers_v2_setting = read_providers_v2_setting().await?; + let (access_token, refresh_token) = + acquire_keycloak_grant(&issuer, &username, &password).await?; + let profile = write_profile(&issuer)?; + let profile_path = profile.path().to_string_lossy().into_owned(); + + delete_provider_resources().await; + let result = async { + run_cli_success( + &[ + "settings", + "set", + "--global", + "--key", + "providers_v2_enabled", + "--value", + "true", + "--yes", + ], + &[], + ) + .await?; + run_cli_success( + &["provider", "profile", "import", "--file", &profile_path], + &[], + ) + .await?; + run_cli_success( + &[ + "provider", + "create", + "--name", + PROVIDER_NAME, + "--type", + PROFILE_ID, + "--credential", + CREDENTIAL_KEY, + ], + &[(CREDENTIAL_KEY, &access_token)], + ) + .await?; + run_cli_success( + &[ + "provider", + "refresh", + "configure", + PROVIDER_NAME, + "--credential-key", + CREDENTIAL_KEY, + "--strategy", + "oauth2-refresh-token", + "--material", + "client_id=openshell-cli", + "--secret-material-env", + "refresh_token=KEYCLOAK_REFRESH_TOKEN", + ], + &[("KEYCLOAK_REFRESH_TOKEN", &refresh_token)], + ) + .await?; + + run_cli_success( + &[ + "provider", + "refresh", + "rotate", + PROVIDER_NAME, + "--credential-key", + CREDENTIAL_KEY, + ], + &[], + ) + .await?; + let valid_status = run_cli_success( + &[ + "provider", + "refresh", + "status", + PROVIDER_NAME, + "--credential-key", + CREDENTIAL_KEY, + ], + &[], + ) + .await?; + if !valid_status.contains("refreshed") { + return Err(format!( + "valid Keycloak refresh did not reach refreshed state:\n{valid_status}" + )); + } + + revoke_keycloak_grant(&issuer, &refresh_token).await?; + let failed_rotation = run_cli( + &[ + "provider", + "refresh", + "rotate", + PROVIDER_NAME, + "--credential-key", + CREDENTIAL_KEY, + ], + &[], + ) + .await?; + let failed_rotation_output = combined_output(&failed_rotation); + if failed_rotation.status.success() || !failed_rotation_output.contains("invalid_grant") { + return Err(format!( + "revoked Keycloak refresh did not fail with invalid_grant:\n{failed_rotation_output}" + )); + } + + let revoked_status = run_cli_success( + &[ + "provider", + "refresh", + "status", + PROVIDER_NAME, + "--credential-key", + CREDENTIAL_KEY, + ], + &[], + ) + .await?; + for expected in [ + "reauthorization_required", + "reauthorize", + "oauth_invalid_grant", + ] { + if !revoked_status.contains(expected) { + return Err(format!( + "revoked refresh status omitted {expected}:\n{revoked_status}" + )); + } + } + if revoked_status.contains("292278994") { + return Err(format!( + "parked refresh rendered the i64::MAX scheduling sentinel as a date:\n{revoked_status}" + )); + } + Ok(()) + } + .await; + + delete_provider_resources().await; + let cleanup_result = + restore_providers_v2_setting(previous_providers_v2_setting.as_deref()).await; + match (result, cleanup_result) { + (Ok(()), Ok(())) => Ok(()), + (Err(test_error), Ok(())) => Err(test_error), + (Ok(()), Err(cleanup_error)) => Err(cleanup_error), + (Err(test_error), Err(cleanup_error)) => Err(format!( + "{test_error}\ncleanup also failed: {cleanup_error}" + )), + } +} diff --git a/proto/openshell.proto b/proto/openshell.proto index 6f1481b52..246fe0626 100644 --- a/proto/openshell.proto +++ b/proto/openshell.proto @@ -1620,9 +1620,23 @@ message ProviderCredentialRefreshStatus { ProviderCredentialRefreshStrategy strategy = 4; string status = 5; int64 expires_at_ms = 6; + // Next automatic refresh time in Unix epoch milliseconds. A value of + // 9223372036854775807 (int64 max) means no automatic retry is scheduled; + // consumers should render it as unset and use recovery_action to determine + // the required recovery workflow. int64 next_refresh_at_ms = 7; int64 last_refresh_at_ms = 8; string last_error = 9; + ProviderCredentialRefreshRecoveryAction recovery_action = 10; + // Stable gateway-owned failure identifier, for example + // "oauth_invalid_grant". This is not provider-controlled prose and + // incorporates any recognized top-level OAuth error classification. + string failure_code = 11; + // A bounded, recognized provider subtype that refines failure_code; clients + // do not need a separate provider_error field. Unknown provider-controlled + // values are not persisted or returned. + string provider_error_subtype = 12; + int64 last_error_at_ms = 13; } // Provider profile local discovery declaration. @@ -1643,6 +1657,7 @@ message StoredProviderCredentialRefreshState { // are not automatically migrated before OpenShell 0.1.0. repeated string secret_material_keys = 7; int64 expires_at_ms = 8; + // int64 max parks the refresh until an explicit rotation or reconfiguration. int64 next_refresh_at_ms = 9; int64 last_refresh_at_ms = 10; string status = 11; @@ -1671,6 +1686,12 @@ message StoredProviderCredentialRefreshState { // Cleanup is retried by the refresh worker so a gateway crash or temporary // credential-backend outage does not lose the deletion reference. repeated StoredRefreshMaterialDeletion pending_secret_deletions = 20; + // Structured recovery details for the most recent refresh failure. These + // fields contain only gateway-owned codes and recognized bounded values. + ProviderCredentialRefreshRecoveryAction recovery_action = 21; + string failure_code = 22; + string provider_error_subtype = 23; + int64 last_error_at_ms = 24; } message StoredRefreshMaterialDeletion { @@ -2787,6 +2808,18 @@ enum WorkspaceRole { WORKSPACE_ROLE_ADMIN = 2; } +// Stable recovery action for the most recent provider credential refresh +// failure. Kept after the pre-existing enums so adding it does not renumber +// their generated descriptors. Clients should use this field instead of +// parsing last_error. +enum ProviderCredentialRefreshRecoveryAction { + PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_UNSPECIFIED = 0; + PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_RETRY = 1; + PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE = 2; + PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_FIX_CONFIGURATION = 3; + PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_INVESTIGATE = 4; +} + // Workspace membership record. message WorkspaceMember { openshell.datamodel.v1.ObjectMeta metadata = 1; diff --git a/sdk/go/openshell/v1/internal/converter/refresh.go b/sdk/go/openshell/v1/internal/converter/refresh.go index b417718a5..be5ee8d03 100644 --- a/sdk/go/openshell/v1/internal/converter/refresh.go +++ b/sdk/go/openshell/v1/internal/converter/refresh.go @@ -4,6 +4,9 @@ package converter import ( + "math" + "time" + "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" ) @@ -30,6 +33,31 @@ func RefreshStrategyFromProto(s pb.ProviderCredentialRefreshStrategy) types.Refr } } +// --- RefreshRecoveryAction enum mapping --- + +// RefreshRecoveryActionFromProto converts a proto recovery action to the curated SDK type. +func RefreshRecoveryActionFromProto(a pb.ProviderCredentialRefreshRecoveryAction) types.RefreshRecoveryAction { + switch a { + case pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_RETRY: + return types.RefreshRecoveryActionRetry + case pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE: + return types.RefreshRecoveryActionReauthorize + case pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_FIX_CONFIGURATION: + return types.RefreshRecoveryActionFixConfiguration + case pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_INVESTIGATE: + return types.RefreshRecoveryActionInvestigate + default: + return types.RefreshRecoveryActionUnspecified + } +} + +func refreshNextTimeFromMillis(ms int64) time.Time { + if ms == math.MaxInt64 { + return time.Time{} + } + return TimeFromMillis(ms) +} + // RefreshStrategyToProto converts an SDK RefreshStrategy to a proto ProviderCredentialRefreshStrategy. func RefreshStrategyToProto(s types.RefreshStrategy) pb.ProviderCredentialRefreshStrategy { switch s { @@ -58,15 +86,19 @@ func RefreshStatusFromProto(s *pb.ProviderCredentialRefreshStatus) *types.Refres return nil } return &types.RefreshStatus{ - ProviderName: s.GetProviderName(), - ProviderID: s.GetProviderId(), - CredentialKey: s.GetCredentialKey(), - Strategy: RefreshStrategyFromProto(s.GetStrategy()), - Status: s.GetStatus(), - ExpiresAt: TimeFromMillis(s.GetExpiresAtMs()), - NextRefreshAt: TimeFromMillis(s.GetNextRefreshAtMs()), - LastRefreshAt: TimeFromMillis(s.GetLastRefreshAtMs()), - LastError: s.GetLastError(), + ProviderName: s.GetProviderName(), + ProviderID: s.GetProviderId(), + CredentialKey: s.GetCredentialKey(), + Strategy: RefreshStrategyFromProto(s.GetStrategy()), + Status: s.GetStatus(), + ExpiresAt: TimeFromMillis(s.GetExpiresAtMs()), + NextRefreshAt: refreshNextTimeFromMillis(s.GetNextRefreshAtMs()), + LastRefreshAt: TimeFromMillis(s.GetLastRefreshAtMs()), + LastError: s.GetLastError(), + RecoveryAction: RefreshRecoveryActionFromProto(s.GetRecoveryAction()), + FailureCode: s.GetFailureCode(), + ProviderErrorSubtype: s.GetProviderErrorSubtype(), + LastErrorAt: TimeFromMillis(s.GetLastErrorAtMs()), } } diff --git a/sdk/go/openshell/v1/internal/converter/refresh_test.go b/sdk/go/openshell/v1/internal/converter/refresh_test.go index b426857c9..9ffa6e590 100644 --- a/sdk/go/openshell/v1/internal/converter/refresh_test.go +++ b/sdk/go/openshell/v1/internal/converter/refresh_test.go @@ -4,6 +4,7 @@ package converter import ( + "math" "testing" "time" @@ -35,6 +36,23 @@ func TestRefreshStrategyFromProto(t *testing.T) { } } +func TestRefreshRecoveryActionFromProto(t *testing.T) { + tests := []struct { + proto pb.ProviderCredentialRefreshRecoveryAction + want v1.RefreshRecoveryAction + }{ + {pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_UNSPECIFIED, v1.RefreshRecoveryActionUnspecified}, + {pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_RETRY, v1.RefreshRecoveryActionRetry}, + {pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE, v1.RefreshRecoveryActionReauthorize}, + {pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_FIX_CONFIGURATION, v1.RefreshRecoveryActionFixConfiguration}, + {pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_INVESTIGATE, v1.RefreshRecoveryActionInvestigate}, + {pb.ProviderCredentialRefreshRecoveryAction(999), v1.RefreshRecoveryActionUnspecified}, + } + for _, tt := range tests { + assert.Equal(t, tt.want, RefreshRecoveryActionFromProto(tt.proto)) + } +} + func TestRefreshStrategyToProto(t *testing.T) { tests := []struct { sdk v1.RefreshStrategy @@ -60,15 +78,19 @@ func TestRefreshStrategyToProto(t *testing.T) { func TestRefreshStatusFromProto(t *testing.T) { proto := &pb.ProviderCredentialRefreshStatus{ - ProviderName: "anthropic", - ProviderId: "prov-1", - CredentialKey: "API_KEY", - Strategy: pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_REFRESH_TOKEN, - Status: "active", - ExpiresAtMs: 1700000000000, - NextRefreshAtMs: 1699999000000, - LastRefreshAtMs: 1699998000000, - LastError: "none", + ProviderName: "anthropic", + ProviderId: "prov-1", + CredentialKey: "API_KEY", + Strategy: pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_REFRESH_TOKEN, + Status: "active", + ExpiresAtMs: 1700000000000, + NextRefreshAtMs: 1699999000000, + LastRefreshAtMs: 1699998000000, + LastError: "none", + RecoveryAction: pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE, + FailureCode: "oauth_invalid_grant", + ProviderErrorSubtype: "invalid_rapt", + LastErrorAtMs: 1699997000000, } status := RefreshStatusFromProto(proto) @@ -83,6 +105,10 @@ func TestRefreshStatusFromProto(t *testing.T) { assert.Equal(t, TimeFromMillis(1699999000000), status.NextRefreshAt) assert.Equal(t, TimeFromMillis(1699998000000), status.LastRefreshAt) assert.Equal(t, "none", status.LastError) + assert.Equal(t, v1.RefreshRecoveryActionReauthorize, status.RecoveryAction) + assert.Equal(t, "oauth_invalid_grant", status.FailureCode) + assert.Equal(t, "invalid_rapt", status.ProviderErrorSubtype) + assert.Equal(t, TimeFromMillis(1699997000000), status.LastErrorAt) } func TestRefreshStatusFromProto_Nil(t *testing.T) { @@ -104,6 +130,24 @@ func TestRefreshStatusFromProto_ZeroTimestamps(t *testing.T) { assert.True(t, status.ExpiresAt.IsZero()) assert.True(t, status.NextRefreshAt.IsZero()) assert.True(t, status.LastRefreshAt.IsZero()) + assert.True(t, status.LastErrorAt.IsZero()) + assert.Equal(t, v1.RefreshRecoveryActionUnspecified, status.RecoveryAction) +} + +func TestRefreshStatusFromProto_ParkedRefreshHasNoNextTime(t *testing.T) { + proto := &pb.ProviderCredentialRefreshStatus{ + ProviderName: "test", + CredentialKey: "KEY", + NextRefreshAtMs: math.MaxInt64, + RecoveryAction: pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE, + } + + status := RefreshStatusFromProto(proto) + + require.NotNil(t, status) + assert.True(t, status.NextRefreshAt.IsZero()) + assert.Equal(t, v1.RefreshRecoveryActionReauthorize, status.RecoveryAction) + assert.False(t, TimeFromMillis(math.MaxInt64).IsZero()) } // --- RefreshConfig --- diff --git a/sdk/go/openshell/v1/types/refresh.go b/sdk/go/openshell/v1/types/refresh.go index b67ba3584..6ca49310e 100644 --- a/sdk/go/openshell/v1/types/refresh.go +++ b/sdk/go/openshell/v1/types/refresh.go @@ -18,6 +18,40 @@ const ( RefreshStrategyAWSStsAssumeRole RefreshStrategy = "AWSStsAssumeRole" ) +// RefreshRecoveryAction describes the action required after a refresh failure. +type RefreshRecoveryAction int + +const ( + // RefreshRecoveryActionUnspecified means no recovery action is required. + RefreshRecoveryActionUnspecified RefreshRecoveryAction = iota + // RefreshRecoveryActionRetry means OpenShell will retry automatically. + RefreshRecoveryActionRetry + // RefreshRecoveryActionReauthorize means the user must replace the OAuth grant. + RefreshRecoveryActionReauthorize + // RefreshRecoveryActionFixConfiguration means an operator must repair configuration. + RefreshRecoveryActionFixConfiguration + // RefreshRecoveryActionInvestigate means the failure is not recognized. + RefreshRecoveryActionInvestigate +) + +// String returns the provider-neutral recovery action name. +func (a RefreshRecoveryAction) String() string { + switch a { + case RefreshRecoveryActionUnspecified: + return "unspecified" + case RefreshRecoveryActionRetry: + return "retry" + case RefreshRecoveryActionReauthorize: + return "reauthorize" + case RefreshRecoveryActionFixConfiguration: + return "fix_configuration" + case RefreshRecoveryActionInvestigate: + return "investigate" + default: + return "unknown" + } +} + // RefreshStatus reports the current state of credential refresh for a specific // provider credential. type RefreshStatus struct { @@ -27,9 +61,15 @@ type RefreshStatus struct { Strategy RefreshStrategy Status string ExpiresAt time.Time - NextRefreshAt time.Time - LastRefreshAt time.Time - LastError string + // NextRefreshAt is zero when no automatic refresh is scheduled. Use + // RecoveryAction to distinguish a parked refresh from an unset timestamp. + NextRefreshAt time.Time + LastRefreshAt time.Time + LastError string + RecoveryAction RefreshRecoveryAction + FailureCode string + ProviderErrorSubtype string + LastErrorAt time.Time } // RefreshConfig holds configuration parameters for gateway-owned credential diff --git a/sdk/go/proto/openshellv1/openshell.pb.go b/sdk/go/proto/openshellv1/openshell.pb.go index 778bf4f98..6b3b740a0 100644 --- a/sdk/go/proto/openshellv1/openshell.pb.go +++ b/sdk/go/proto/openshellv1/openshell.pb.go @@ -439,6 +439,65 @@ func (WorkspaceRole) EnumDescriptor() ([]byte, []int) { return file_openshell_proto_rawDescGZIP(), []int{6} } +// Stable recovery action for the most recent provider credential refresh +// failure. Kept after the pre-existing enums so adding it does not renumber +// their generated descriptors. Clients should use this field instead of +// parsing last_error. +type ProviderCredentialRefreshRecoveryAction int32 + +const ( + ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_UNSPECIFIED ProviderCredentialRefreshRecoveryAction = 0 + ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_RETRY ProviderCredentialRefreshRecoveryAction = 1 + ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE ProviderCredentialRefreshRecoveryAction = 2 + ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_FIX_CONFIGURATION ProviderCredentialRefreshRecoveryAction = 3 + ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_INVESTIGATE ProviderCredentialRefreshRecoveryAction = 4 +) + +// Enum value maps for ProviderCredentialRefreshRecoveryAction. +var ( + ProviderCredentialRefreshRecoveryAction_name = map[int32]string{ + 0: "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_UNSPECIFIED", + 1: "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_RETRY", + 2: "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE", + 3: "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_FIX_CONFIGURATION", + 4: "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_INVESTIGATE", + } + ProviderCredentialRefreshRecoveryAction_value = map[string]int32{ + "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_UNSPECIFIED": 0, + "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_RETRY": 1, + "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE": 2, + "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_FIX_CONFIGURATION": 3, + "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_INVESTIGATE": 4, + } +) + +func (x ProviderCredentialRefreshRecoveryAction) Enum() *ProviderCredentialRefreshRecoveryAction { + p := new(ProviderCredentialRefreshRecoveryAction) + *p = x + return p +} + +func (x ProviderCredentialRefreshRecoveryAction) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ProviderCredentialRefreshRecoveryAction) Descriptor() protoreflect.EnumDescriptor { + return file_openshell_proto_enumTypes[7].Descriptor() +} + +func (ProviderCredentialRefreshRecoveryAction) Type() protoreflect.EnumType { + return &file_openshell_proto_enumTypes[7] +} + +func (x ProviderCredentialRefreshRecoveryAction) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ProviderCredentialRefreshRecoveryAction.Descriptor instead. +func (ProviderCredentialRefreshRecoveryAction) EnumDescriptor() ([]byte, []int) { + return file_openshell_proto_rawDescGZIP(), []int{7} +} + // IssueSandboxToken request. Empty body; identity is established by the // authentication credentials carried in the request headers (a projected // Kubernetes ServiceAccount JWT in the K8s driver path). @@ -5861,18 +5920,32 @@ func (x *ProviderCredentialRefresh) GetAdditionalOutputs() []*ProviderCredential } type ProviderCredentialRefreshStatus struct { - state protoimpl.MessageState `protogen:"open.v1"` - ProviderName string `protobuf:"bytes,1,opt,name=provider_name,json=providerName,proto3" json:"provider_name,omitempty"` - ProviderId string `protobuf:"bytes,2,opt,name=provider_id,json=providerId,proto3" json:"provider_id,omitempty"` - CredentialKey string `protobuf:"bytes,3,opt,name=credential_key,json=credentialKey,proto3" json:"credential_key,omitempty"` - Strategy ProviderCredentialRefreshStrategy `protobuf:"varint,4,opt,name=strategy,proto3,enum=openshell.v1.ProviderCredentialRefreshStrategy" json:"strategy,omitempty"` - Status string `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"` - ExpiresAtMs int64 `protobuf:"varint,6,opt,name=expires_at_ms,json=expiresAtMs,proto3" json:"expires_at_ms,omitempty"` - NextRefreshAtMs int64 `protobuf:"varint,7,opt,name=next_refresh_at_ms,json=nextRefreshAtMs,proto3" json:"next_refresh_at_ms,omitempty"` - LastRefreshAtMs int64 `protobuf:"varint,8,opt,name=last_refresh_at_ms,json=lastRefreshAtMs,proto3" json:"last_refresh_at_ms,omitempty"` - LastError string `protobuf:"bytes,9,opt,name=last_error,json=lastError,proto3" json:"last_error,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + ProviderName string `protobuf:"bytes,1,opt,name=provider_name,json=providerName,proto3" json:"provider_name,omitempty"` + ProviderId string `protobuf:"bytes,2,opt,name=provider_id,json=providerId,proto3" json:"provider_id,omitempty"` + CredentialKey string `protobuf:"bytes,3,opt,name=credential_key,json=credentialKey,proto3" json:"credential_key,omitempty"` + Strategy ProviderCredentialRefreshStrategy `protobuf:"varint,4,opt,name=strategy,proto3,enum=openshell.v1.ProviderCredentialRefreshStrategy" json:"strategy,omitempty"` + Status string `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"` + ExpiresAtMs int64 `protobuf:"varint,6,opt,name=expires_at_ms,json=expiresAtMs,proto3" json:"expires_at_ms,omitempty"` + // Next automatic refresh time in Unix epoch milliseconds. A value of + // 9223372036854775807 (int64 max) means no automatic retry is scheduled; + // consumers should render it as unset and use recovery_action to determine + // the required recovery workflow. + NextRefreshAtMs int64 `protobuf:"varint,7,opt,name=next_refresh_at_ms,json=nextRefreshAtMs,proto3" json:"next_refresh_at_ms,omitempty"` + LastRefreshAtMs int64 `protobuf:"varint,8,opt,name=last_refresh_at_ms,json=lastRefreshAtMs,proto3" json:"last_refresh_at_ms,omitempty"` + LastError string `protobuf:"bytes,9,opt,name=last_error,json=lastError,proto3" json:"last_error,omitempty"` + RecoveryAction ProviderCredentialRefreshRecoveryAction `protobuf:"varint,10,opt,name=recovery_action,json=recoveryAction,proto3,enum=openshell.v1.ProviderCredentialRefreshRecoveryAction" json:"recovery_action,omitempty"` + // Stable gateway-owned failure identifier, for example + // "oauth_invalid_grant". This is not provider-controlled prose and + // incorporates any recognized top-level OAuth error classification. + FailureCode string `protobuf:"bytes,11,opt,name=failure_code,json=failureCode,proto3" json:"failure_code,omitempty"` + // A bounded, recognized provider subtype that refines failure_code; clients + // do not need a separate provider_error field. Unknown provider-controlled + // values are not persisted or returned. + ProviderErrorSubtype string `protobuf:"bytes,12,opt,name=provider_error_subtype,json=providerErrorSubtype,proto3" json:"provider_error_subtype,omitempty"` + LastErrorAtMs int64 `protobuf:"varint,13,opt,name=last_error_at_ms,json=lastErrorAtMs,proto3" json:"last_error_at_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ProviderCredentialRefreshStatus) Reset() { @@ -5968,6 +6041,34 @@ func (x *ProviderCredentialRefreshStatus) GetLastError() string { return "" } +func (x *ProviderCredentialRefreshStatus) GetRecoveryAction() ProviderCredentialRefreshRecoveryAction { + if x != nil { + return x.RecoveryAction + } + return ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_UNSPECIFIED +} + +func (x *ProviderCredentialRefreshStatus) GetFailureCode() string { + if x != nil { + return x.FailureCode + } + return "" +} + +func (x *ProviderCredentialRefreshStatus) GetProviderErrorSubtype() string { + if x != nil { + return x.ProviderErrorSubtype + } + return "" +} + +func (x *ProviderCredentialRefreshStatus) GetLastErrorAtMs() int64 { + if x != nil { + return x.LastErrorAtMs + } + return 0 +} + // Provider profile local discovery declaration. type ProviderProfileDiscovery struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -6025,8 +6126,9 @@ type StoredProviderCredentialRefreshState struct { // Material names classified as secret. Newly configured values live in the // active credential driver and are absent from material. Legacy inline values // are not automatically migrated before OpenShell 0.1.0. - SecretMaterialKeys []string `protobuf:"bytes,7,rep,name=secret_material_keys,json=secretMaterialKeys,proto3" json:"secret_material_keys,omitempty"` - ExpiresAtMs int64 `protobuf:"varint,8,opt,name=expires_at_ms,json=expiresAtMs,proto3" json:"expires_at_ms,omitempty"` + SecretMaterialKeys []string `protobuf:"bytes,7,rep,name=secret_material_keys,json=secretMaterialKeys,proto3" json:"secret_material_keys,omitempty"` + ExpiresAtMs int64 `protobuf:"varint,8,opt,name=expires_at_ms,json=expiresAtMs,proto3" json:"expires_at_ms,omitempty"` + // int64 max parks the refresh until an explicit rotation or reconfiguration. NextRefreshAtMs int64 `protobuf:"varint,9,opt,name=next_refresh_at_ms,json=nextRefreshAtMs,proto3" json:"next_refresh_at_ms,omitempty"` LastRefreshAtMs int64 `protobuf:"varint,10,opt,name=last_refresh_at_ms,json=lastRefreshAtMs,proto3" json:"last_refresh_at_ms,omitempty"` Status string `protobuf:"bytes,11,opt,name=status,proto3" json:"status,omitempty"` @@ -6055,8 +6157,14 @@ type StoredProviderCredentialRefreshState struct { // Cleanup is retried by the refresh worker so a gateway crash or temporary // credential-backend outage does not lose the deletion reference. PendingSecretDeletions []*StoredRefreshMaterialDeletion `protobuf:"bytes,20,rep,name=pending_secret_deletions,json=pendingSecretDeletions,proto3" json:"pending_secret_deletions,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Structured recovery details for the most recent refresh failure. These + // fields contain only gateway-owned codes and recognized bounded values. + RecoveryAction ProviderCredentialRefreshRecoveryAction `protobuf:"varint,21,opt,name=recovery_action,json=recoveryAction,proto3,enum=openshell.v1.ProviderCredentialRefreshRecoveryAction" json:"recovery_action,omitempty"` + FailureCode string `protobuf:"bytes,22,opt,name=failure_code,json=failureCode,proto3" json:"failure_code,omitempty"` + ProviderErrorSubtype string `protobuf:"bytes,23,opt,name=provider_error_subtype,json=providerErrorSubtype,proto3" json:"provider_error_subtype,omitempty"` + LastErrorAtMs int64 `protobuf:"varint,24,opt,name=last_error_at_ms,json=lastErrorAtMs,proto3" json:"last_error_at_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *StoredProviderCredentialRefreshState) Reset() { @@ -6229,6 +6337,34 @@ func (x *StoredProviderCredentialRefreshState) GetPendingSecretDeletions() []*St return nil } +func (x *StoredProviderCredentialRefreshState) GetRecoveryAction() ProviderCredentialRefreshRecoveryAction { + if x != nil { + return x.RecoveryAction + } + return ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_UNSPECIFIED +} + +func (x *StoredProviderCredentialRefreshState) GetFailureCode() string { + if x != nil { + return x.FailureCode + } + return "" +} + +func (x *StoredProviderCredentialRefreshState) GetProviderErrorSubtype() string { + if x != nil { + return x.ProviderErrorSubtype + } + return "" +} + +func (x *StoredProviderCredentialRefreshState) GetLastErrorAtMs() int64 { + if x != nil { + return x.LastErrorAtMs + } + return 0 +} + type StoredRefreshMaterialDeletion struct { state protoimpl.MessageState `protogen:"open.v1"` // Original material name used to derive the credential driver's storage key. @@ -14233,7 +14369,7 @@ const file_openshell_proto_rawDesc = "" + "\x16refresh_before_seconds\x18\x04 \x01(\x03R\x14refreshBeforeSeconds\x120\n" + "\x14max_lifetime_seconds\x18\x05 \x01(\x03R\x12maxLifetimeSeconds\x12K\n" + "\bmaterial\x18\x06 \x03(\v2/.openshell.v1.ProviderCredentialRefreshMaterialR\bmaterial\x12\\\n" + - "\x12additional_outputs\x18\a \x03(\v2-.openshell.v1.ProviderCredentialRefreshOutputR\x11additionalOutputs\"\x90\x03\n" + + "\x12additional_outputs\x18\a \x03(\v2-.openshell.v1.ProviderCredentialRefreshOutputR\x11additionalOutputs\"\xf2\x04\n" + "\x1fProviderCredentialRefreshStatus\x12#\n" + "\rprovider_name\x18\x01 \x01(\tR\fproviderName\x12\x1f\n" + "\vprovider_id\x18\x02 \x01(\tR\n" + @@ -14245,9 +14381,14 @@ const file_openshell_proto_rawDesc = "" + "\x12next_refresh_at_ms\x18\a \x01(\x03R\x0fnextRefreshAtMs\x12+\n" + "\x12last_refresh_at_ms\x18\b \x01(\x03R\x0flastRefreshAtMs\x12\x1d\n" + "\n" + - "last_error\x18\t \x01(\tR\tlastError\"<\n" + + "last_error\x18\t \x01(\tR\tlastError\x12^\n" + + "\x0frecovery_action\x18\n" + + " \x01(\x0e25.openshell.v1.ProviderCredentialRefreshRecoveryActionR\x0erecoveryAction\x12!\n" + + "\ffailure_code\x18\v \x01(\tR\vfailureCode\x124\n" + + "\x16provider_error_subtype\x18\f \x01(\tR\x14providerErrorSubtype\x12'\n" + + "\x10last_error_at_ms\x18\r \x01(\x03R\rlastErrorAtMs\"<\n" + "\x18ProviderProfileDiscovery\x12 \n" + - "\vcredentials\x18\x01 \x03(\tR\vcredentials\"\xa7\v\n" + + "\vcredentials\x18\x01 \x03(\tR\vcredentials\"\x89\r\n" + "$StoredProviderCredentialRefreshState\x12>\n" + "\bmetadata\x18\x01 \x01(\v2\".openshell.datamodel.v1.ObjectMetaR\bmetadata\x12\x1f\n" + "\vprovider_id\x18\x02 \x01(\tR\n" + @@ -14271,7 +14412,11 @@ const file_openshell_proto_rawDesc = "" + "\x16additional_output_keys\x18\x11 \x03(\v2L.openshell.v1.StoredProviderCredentialRefreshState.AdditionalOutputKeysEntryR\x14additionalOutputKeys\x12/\n" + "\x13authorization_epoch\x18\x12 \x01(\tR\x12authorizationEpoch\x12\x85\x01\n" + "\x17secret_material_handles\x18\x13 \x03(\v2M.openshell.v1.StoredProviderCredentialRefreshState.SecretMaterialHandlesEntryR\x15secretMaterialHandles\x12e\n" + - "\x18pending_secret_deletions\x18\x14 \x03(\v2+.openshell.v1.StoredRefreshMaterialDeletionR\x16pendingSecretDeletions\x1a;\n" + + "\x18pending_secret_deletions\x18\x14 \x03(\v2+.openshell.v1.StoredRefreshMaterialDeletionR\x16pendingSecretDeletions\x12^\n" + + "\x0frecovery_action\x18\x15 \x01(\x0e25.openshell.v1.ProviderCredentialRefreshRecoveryActionR\x0erecoveryAction\x12!\n" + + "\ffailure_code\x18\x16 \x01(\tR\vfailureCode\x124\n" + + "\x16provider_error_subtype\x18\x17 \x01(\tR\x14providerErrorSubtype\x12'\n" + + "\x10last_error_at_ms\x18\x18 \x01(\x03R\rlastErrorAtMs\x1a;\n" + "\rMaterialEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1aG\n" + @@ -14930,7 +15075,13 @@ const file_openshell_proto_rawDesc = "" + "\rWorkspaceRole\x12\x1e\n" + "\x1aWORKSPACE_ROLE_UNSPECIFIED\x10\x00\x12\x17\n" + "\x13WORKSPACE_ROLE_USER\x10\x01\x12\x18\n" + - "\x14WORKSPACE_ROLE_ADMIN\x10\x022\xacF\n" + + "\x14WORKSPACE_ROLE_ADMIN\x10\x02*\xda\x02\n" + + "'ProviderCredentialRefreshRecoveryAction\x12;\n" + + "7PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_UNSPECIFIED\x10\x00\x125\n" + + "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" + "\tOpenShell\x12Z\n" + "\x06Health\x12\x1b.openshell.v1.HealthRequest\x1a\x1c.openshell.v1.HealthResponse\"\x15\x82\xb5\x18\x11\n" + "\x0funauthenticated\x12i\n" + @@ -15084,7 +15235,7 @@ func file_openshell_proto_rawDescGZIP() []byte { return file_openshell_proto_rawDescData } -var file_openshell_proto_enumTypes = make([]protoimpl.EnumInfo, 7) +var file_openshell_proto_enumTypes = make([]protoimpl.EnumInfo, 8) var file_openshell_proto_msgTypes = make([]protoimpl.MessageInfo, 217) var file_openshell_proto_goTypes = []any{ (SandboxPhase)(0), // 0: openshell.v1.SandboxPhase @@ -15094,546 +15245,549 @@ var file_openshell_proto_goTypes = []any{ (PolicyStatus)(0), // 4: openshell.v1.PolicyStatus (ServiceStatus)(0), // 5: openshell.v1.ServiceStatus (WorkspaceRole)(0), // 6: openshell.v1.WorkspaceRole - (*IssueSandboxTokenRequest)(nil), // 7: openshell.v1.IssueSandboxTokenRequest - (*IssueSandboxTokenResponse)(nil), // 8: openshell.v1.IssueSandboxTokenResponse - (*RefreshSandboxTokenRequest)(nil), // 9: openshell.v1.RefreshSandboxTokenRequest - (*RefreshSandboxTokenResponse)(nil), // 10: openshell.v1.RefreshSandboxTokenResponse - (*HealthRequest)(nil), // 11: openshell.v1.HealthRequest - (*HealthResponse)(nil), // 12: openshell.v1.HealthResponse - (*GetCurrentUserRequest)(nil), // 13: openshell.v1.GetCurrentUserRequest - (*GetCurrentUserResponse)(nil), // 14: openshell.v1.GetCurrentUserResponse - (*GetGatewayInfoRequest)(nil), // 15: openshell.v1.GetGatewayInfoRequest - (*GetGatewayInfoResponse)(nil), // 16: openshell.v1.GetGatewayInfoResponse - (*ComputeDriverInfo)(nil), // 17: openshell.v1.ComputeDriverInfo - (*ComputeDriverCapabilities)(nil), // 18: openshell.v1.ComputeDriverCapabilities - (*Sandbox)(nil), // 19: openshell.v1.Sandbox - (*SandboxSpec)(nil), // 20: openshell.v1.SandboxSpec - (*ResourceRequirements)(nil), // 21: openshell.v1.ResourceRequirements - (*GpuResourceRequirements)(nil), // 22: openshell.v1.GpuResourceRequirements - (*SandboxTemplate)(nil), // 23: openshell.v1.SandboxTemplate - (*SandboxStatus)(nil), // 24: openshell.v1.SandboxStatus - (*SandboxCondition)(nil), // 25: openshell.v1.SandboxCondition - (*PlatformEvent)(nil), // 26: openshell.v1.PlatformEvent - (*CreateSandboxRequest)(nil), // 27: openshell.v1.CreateSandboxRequest - (*GetSandboxRequest)(nil), // 28: openshell.v1.GetSandboxRequest - (*ListSandboxesRequest)(nil), // 29: openshell.v1.ListSandboxesRequest - (*ListSandboxProvidersRequest)(nil), // 30: openshell.v1.ListSandboxProvidersRequest - (*AttachSandboxProviderRequest)(nil), // 31: openshell.v1.AttachSandboxProviderRequest - (*DetachSandboxProviderRequest)(nil), // 32: openshell.v1.DetachSandboxProviderRequest - (*DeleteSandboxRequest)(nil), // 33: openshell.v1.DeleteSandboxRequest - (*StopSandboxRequest)(nil), // 34: openshell.v1.StopSandboxRequest - (*StartSandboxRequest)(nil), // 35: openshell.v1.StartSandboxRequest - (*SandboxResponse)(nil), // 36: openshell.v1.SandboxResponse - (*ListSandboxesResponse)(nil), // 37: openshell.v1.ListSandboxesResponse - (*ListSandboxProvidersResponse)(nil), // 38: openshell.v1.ListSandboxProvidersResponse - (*AttachSandboxProviderResponse)(nil), // 39: openshell.v1.AttachSandboxProviderResponse - (*DetachSandboxProviderResponse)(nil), // 40: openshell.v1.DetachSandboxProviderResponse - (*DeleteSandboxResponse)(nil), // 41: openshell.v1.DeleteSandboxResponse - (*CreateSshSessionRequest)(nil), // 42: openshell.v1.CreateSshSessionRequest - (*CreateSshSessionResponse)(nil), // 43: openshell.v1.CreateSshSessionResponse - (*ExposeServiceRequest)(nil), // 44: openshell.v1.ExposeServiceRequest - (*GetServiceRequest)(nil), // 45: openshell.v1.GetServiceRequest - (*ListServicesRequest)(nil), // 46: openshell.v1.ListServicesRequest - (*ListServicesResponse)(nil), // 47: openshell.v1.ListServicesResponse - (*DeleteServiceRequest)(nil), // 48: openshell.v1.DeleteServiceRequest - (*DeleteServiceResponse)(nil), // 49: openshell.v1.DeleteServiceResponse - (*ServiceEndpoint)(nil), // 50: openshell.v1.ServiceEndpoint - (*ServiceEndpointResponse)(nil), // 51: openshell.v1.ServiceEndpointResponse - (*RevokeSshSessionRequest)(nil), // 52: openshell.v1.RevokeSshSessionRequest - (*RevokeSshSessionResponse)(nil), // 53: openshell.v1.RevokeSshSessionResponse - (*ExecSandboxRequest)(nil), // 54: openshell.v1.ExecSandboxRequest - (*ExecSandboxStdout)(nil), // 55: openshell.v1.ExecSandboxStdout - (*ExecSandboxStderr)(nil), // 56: openshell.v1.ExecSandboxStderr - (*ExecSandboxExit)(nil), // 57: openshell.v1.ExecSandboxExit - (*ExecSandboxEvent)(nil), // 58: openshell.v1.ExecSandboxEvent - (*TcpForwardInit)(nil), // 59: openshell.v1.TcpForwardInit - (*TcpForwardFrame)(nil), // 60: openshell.v1.TcpForwardFrame - (*ExecSandboxInput)(nil), // 61: openshell.v1.ExecSandboxInput - (*ExecSandboxWindowResize)(nil), // 62: openshell.v1.ExecSandboxWindowResize - (*SshSession)(nil), // 63: openshell.v1.SshSession - (*WatchSandboxRequest)(nil), // 64: openshell.v1.WatchSandboxRequest - (*SandboxStreamEvent)(nil), // 65: openshell.v1.SandboxStreamEvent - (*SandboxLogLine)(nil), // 66: openshell.v1.SandboxLogLine - (*SandboxStreamWarning)(nil), // 67: openshell.v1.SandboxStreamWarning - (*CreateProviderRequest)(nil), // 68: openshell.v1.CreateProviderRequest - (*GetProviderRequest)(nil), // 69: openshell.v1.GetProviderRequest - (*ListProvidersRequest)(nil), // 70: openshell.v1.ListProvidersRequest - (*UpdateProviderRequest)(nil), // 71: openshell.v1.UpdateProviderRequest - (*DeleteProviderRequest)(nil), // 72: openshell.v1.DeleteProviderRequest - (*ProviderResponse)(nil), // 73: openshell.v1.ProviderResponse - (*ListProvidersResponse)(nil), // 74: openshell.v1.ListProvidersResponse - (*ListProviderProfilesRequest)(nil), // 75: openshell.v1.ListProviderProfilesRequest - (*GetProviderProfileRequest)(nil), // 76: openshell.v1.GetProviderProfileRequest - (*ProviderProfileImportItem)(nil), // 77: openshell.v1.ProviderProfileImportItem - (*ProviderProfileDiagnostic)(nil), // 78: openshell.v1.ProviderProfileDiagnostic - (*ProviderCredentialTokenGrantAudienceOverride)(nil), // 79: openshell.v1.ProviderCredentialTokenGrantAudienceOverride - (*ProviderCredentialTokenGrantSubjectToken)(nil), // 80: openshell.v1.ProviderCredentialTokenGrantSubjectToken - (*ProviderCredentialTokenGrant)(nil), // 81: openshell.v1.ProviderCredentialTokenGrant - (*ProviderProfileCredential)(nil), // 82: openshell.v1.ProviderProfileCredential - (*ProviderCredentialRefreshMaterial)(nil), // 83: openshell.v1.ProviderCredentialRefreshMaterial - (*ProviderCredentialRefreshOutput)(nil), // 84: openshell.v1.ProviderCredentialRefreshOutput - (*ProviderCredentialRefresh)(nil), // 85: openshell.v1.ProviderCredentialRefresh - (*ProviderCredentialRefreshStatus)(nil), // 86: openshell.v1.ProviderCredentialRefreshStatus - (*ProviderProfileDiscovery)(nil), // 87: openshell.v1.ProviderProfileDiscovery - (*StoredProviderCredentialRefreshState)(nil), // 88: openshell.v1.StoredProviderCredentialRefreshState - (*StoredRefreshMaterialDeletion)(nil), // 89: openshell.v1.StoredRefreshMaterialDeletion - (*GetProviderRefreshStatusRequest)(nil), // 90: openshell.v1.GetProviderRefreshStatusRequest - (*GetProviderRefreshStatusResponse)(nil), // 91: openshell.v1.GetProviderRefreshStatusResponse - (*ConfigureProviderRefreshRequest)(nil), // 92: openshell.v1.ConfigureProviderRefreshRequest - (*ConfigureProviderRefreshResponse)(nil), // 93: openshell.v1.ConfigureProviderRefreshResponse - (*RotateProviderCredentialRequest)(nil), // 94: openshell.v1.RotateProviderCredentialRequest - (*RotateProviderCredentialResponse)(nil), // 95: openshell.v1.RotateProviderCredentialResponse - (*DeleteProviderRefreshRequest)(nil), // 96: openshell.v1.DeleteProviderRefreshRequest - (*DeleteProviderRefreshResponse)(nil), // 97: openshell.v1.DeleteProviderRefreshResponse - (*ProviderProfile)(nil), // 98: openshell.v1.ProviderProfile - (*StoredProviderProfile)(nil), // 99: openshell.v1.StoredProviderProfile - (*ProviderProfileResponse)(nil), // 100: openshell.v1.ProviderProfileResponse - (*ListProviderProfilesResponse)(nil), // 101: openshell.v1.ListProviderProfilesResponse - (*ImportProviderProfilesRequest)(nil), // 102: openshell.v1.ImportProviderProfilesRequest - (*ImportProviderProfilesResponse)(nil), // 103: openshell.v1.ImportProviderProfilesResponse - (*UpdateProviderProfilesRequest)(nil), // 104: openshell.v1.UpdateProviderProfilesRequest - (*UpdateProviderProfilesResponse)(nil), // 105: openshell.v1.UpdateProviderProfilesResponse - (*LintProviderProfilesRequest)(nil), // 106: openshell.v1.LintProviderProfilesRequest - (*LintProviderProfilesResponse)(nil), // 107: openshell.v1.LintProviderProfilesResponse - (*DeleteProviderResponse)(nil), // 108: openshell.v1.DeleteProviderResponse - (*DeleteProviderProfileRequest)(nil), // 109: openshell.v1.DeleteProviderProfileRequest - (*DeleteProviderProfileResponse)(nil), // 110: openshell.v1.DeleteProviderProfileResponse - (*GetSandboxProviderEnvironmentRequest)(nil), // 111: openshell.v1.GetSandboxProviderEnvironmentRequest - (*StaticCredentialEndpointBinding)(nil), // 112: openshell.v1.StaticCredentialEndpointBinding - (*StaticCredentialBinding)(nil), // 113: openshell.v1.StaticCredentialBinding - (*GetSandboxProviderEnvironmentResponse)(nil), // 114: openshell.v1.GetSandboxProviderEnvironmentResponse - (*ExchangeProviderSubjectTokenRequest)(nil), // 115: openshell.v1.ExchangeProviderSubjectTokenRequest - (*ExchangeProviderSubjectTokenResponse)(nil), // 116: openshell.v1.ExchangeProviderSubjectTokenResponse - (*UpdateConfigRequest)(nil), // 117: openshell.v1.UpdateConfigRequest - (*PolicyMergeOperation)(nil), // 118: openshell.v1.PolicyMergeOperation - (*AddNetworkRule)(nil), // 119: openshell.v1.AddNetworkRule - (*RemoveNetworkEndpoint)(nil), // 120: openshell.v1.RemoveNetworkEndpoint - (*RemoveNetworkRule)(nil), // 121: openshell.v1.RemoveNetworkRule - (*AddDenyRules)(nil), // 122: openshell.v1.AddDenyRules - (*AddAllowRules)(nil), // 123: openshell.v1.AddAllowRules - (*RemoveNetworkBinary)(nil), // 124: openshell.v1.RemoveNetworkBinary - (*UpdateConfigResponse)(nil), // 125: openshell.v1.UpdateConfigResponse - (*GetSandboxPolicyStatusRequest)(nil), // 126: openshell.v1.GetSandboxPolicyStatusRequest - (*GetSandboxPolicyStatusResponse)(nil), // 127: openshell.v1.GetSandboxPolicyStatusResponse - (*ListSandboxPoliciesRequest)(nil), // 128: openshell.v1.ListSandboxPoliciesRequest - (*ListSandboxPoliciesResponse)(nil), // 129: openshell.v1.ListSandboxPoliciesResponse - (*ReportPolicyStatusRequest)(nil), // 130: openshell.v1.ReportPolicyStatusRequest - (*ReportPolicyStatusResponse)(nil), // 131: openshell.v1.ReportPolicyStatusResponse - (*SandboxPolicyRevision)(nil), // 132: openshell.v1.SandboxPolicyRevision - (*GetSandboxLogsRequest)(nil), // 133: openshell.v1.GetSandboxLogsRequest - (*PushSandboxLogsRequest)(nil), // 134: openshell.v1.PushSandboxLogsRequest - (*PushSandboxLogsResponse)(nil), // 135: openshell.v1.PushSandboxLogsResponse - (*GetSandboxLogsResponse)(nil), // 136: openshell.v1.GetSandboxLogsResponse - (*SupervisorMessage)(nil), // 137: openshell.v1.SupervisorMessage - (*GatewayMessage)(nil), // 138: openshell.v1.GatewayMessage - (*SupervisorHello)(nil), // 139: openshell.v1.SupervisorHello - (*SessionAccepted)(nil), // 140: openshell.v1.SessionAccepted - (*SessionRejected)(nil), // 141: openshell.v1.SessionRejected - (*SupervisorHeartbeat)(nil), // 142: openshell.v1.SupervisorHeartbeat - (*GatewayHeartbeat)(nil), // 143: openshell.v1.GatewayHeartbeat - (*ReportMainProcessExitRequest)(nil), // 144: openshell.v1.ReportMainProcessExitRequest - (*ReportMainProcessExitResponse)(nil), // 145: openshell.v1.ReportMainProcessExitResponse - (*RelayOpen)(nil), // 146: openshell.v1.RelayOpen - (*SshRelayTarget)(nil), // 147: openshell.v1.SshRelayTarget - (*TcpRelayTarget)(nil), // 148: openshell.v1.TcpRelayTarget - (*RelayInit)(nil), // 149: openshell.v1.RelayInit - (*RelayFrame)(nil), // 150: openshell.v1.RelayFrame - (*RelayOpenResult)(nil), // 151: openshell.v1.RelayOpenResult - (*RelayClose)(nil), // 152: openshell.v1.RelayClose - (*L7RequestSample)(nil), // 153: openshell.v1.L7RequestSample - (*DenialSummary)(nil), // 154: openshell.v1.DenialSummary - (*DenialGroupCount)(nil), // 155: openshell.v1.DenialGroupCount - (*NetworkActivitySummary)(nil), // 156: openshell.v1.NetworkActivitySummary - (*PolicyChunk)(nil), // 157: openshell.v1.PolicyChunk - (*DraftPolicyUpdate)(nil), // 158: openshell.v1.DraftPolicyUpdate - (*SubmitPolicyAnalysisRequest)(nil), // 159: openshell.v1.SubmitPolicyAnalysisRequest - (*SubmitPolicyAnalysisResponse)(nil), // 160: openshell.v1.SubmitPolicyAnalysisResponse - (*GetDraftPolicyRequest)(nil), // 161: openshell.v1.GetDraftPolicyRequest - (*GetDraftPolicyResponse)(nil), // 162: openshell.v1.GetDraftPolicyResponse - (*ApproveDraftChunkRequest)(nil), // 163: openshell.v1.ApproveDraftChunkRequest - (*ApproveDraftChunkResponse)(nil), // 164: openshell.v1.ApproveDraftChunkResponse - (*RejectDraftChunkRequest)(nil), // 165: openshell.v1.RejectDraftChunkRequest - (*RejectDraftChunkResponse)(nil), // 166: openshell.v1.RejectDraftChunkResponse - (*DraftChunkApproval)(nil), // 167: openshell.v1.DraftChunkApproval - (*ApproveAllDraftChunksRequest)(nil), // 168: openshell.v1.ApproveAllDraftChunksRequest - (*ApproveAllDraftChunksResponse)(nil), // 169: openshell.v1.ApproveAllDraftChunksResponse - (*EditDraftChunkRequest)(nil), // 170: openshell.v1.EditDraftChunkRequest - (*EditDraftChunkResponse)(nil), // 171: openshell.v1.EditDraftChunkResponse - (*UndoDraftChunkRequest)(nil), // 172: openshell.v1.UndoDraftChunkRequest - (*UndoDraftChunkResponse)(nil), // 173: openshell.v1.UndoDraftChunkResponse - (*ClearDraftChunksRequest)(nil), // 174: openshell.v1.ClearDraftChunksRequest - (*ClearDraftChunksResponse)(nil), // 175: openshell.v1.ClearDraftChunksResponse - (*GetDraftHistoryRequest)(nil), // 176: openshell.v1.GetDraftHistoryRequest - (*DraftHistoryEntry)(nil), // 177: openshell.v1.DraftHistoryEntry - (*GetDraftHistoryResponse)(nil), // 178: openshell.v1.GetDraftHistoryResponse - (*PolicyRevisionPayload)(nil), // 179: openshell.v1.PolicyRevisionPayload - (*DraftChunkPayload)(nil), // 180: openshell.v1.DraftChunkPayload - (*StoredPolicyRevision)(nil), // 181: openshell.v1.StoredPolicyRevision - (*StoredDraftChunk)(nil), // 182: openshell.v1.StoredDraftChunk - (*CreateWorkspaceRequest)(nil), // 183: openshell.v1.CreateWorkspaceRequest - (*CreateWorkspaceResponse)(nil), // 184: openshell.v1.CreateWorkspaceResponse - (*GetWorkspaceRequest)(nil), // 185: openshell.v1.GetWorkspaceRequest - (*GetWorkspaceResponse)(nil), // 186: openshell.v1.GetWorkspaceResponse - (*ListWorkspacesRequest)(nil), // 187: openshell.v1.ListWorkspacesRequest - (*ListWorkspacesResponse)(nil), // 188: openshell.v1.ListWorkspacesResponse - (*DeleteWorkspaceRequest)(nil), // 189: openshell.v1.DeleteWorkspaceRequest - (*DeleteWorkspaceResponse)(nil), // 190: openshell.v1.DeleteWorkspaceResponse - (*WorkspaceMember)(nil), // 191: openshell.v1.WorkspaceMember - (*AddWorkspaceMemberRequest)(nil), // 192: openshell.v1.AddWorkspaceMemberRequest - (*AddWorkspaceMemberResponse)(nil), // 193: openshell.v1.AddWorkspaceMemberResponse - (*RemoveWorkspaceMemberRequest)(nil), // 194: openshell.v1.RemoveWorkspaceMemberRequest - (*RemoveWorkspaceMemberResponse)(nil), // 195: openshell.v1.RemoveWorkspaceMemberResponse - (*ListWorkspaceMembersRequest)(nil), // 196: openshell.v1.ListWorkspaceMembersRequest - (*ListWorkspaceMembersResponse)(nil), // 197: openshell.v1.ListWorkspaceMembersResponse - (*ExtensionServiceCredential)(nil), // 198: openshell.v1.ExtensionServiceCredential - nil, // 199: openshell.v1.SandboxSpec.EnvironmentEntry - nil, // 200: openshell.v1.SandboxTemplate.LabelsEntry - nil, // 201: openshell.v1.SandboxTemplate.AnnotationsEntry - nil, // 202: openshell.v1.SandboxTemplate.EnvironmentEntry - nil, // 203: openshell.v1.PlatformEvent.MetadataEntry - nil, // 204: openshell.v1.CreateSandboxRequest.LabelsEntry - nil, // 205: openshell.v1.CreateSandboxRequest.AnnotationsEntry - nil, // 206: openshell.v1.ExecSandboxRequest.EnvironmentEntry - nil, // 207: openshell.v1.SandboxLogLine.FieldsEntry - nil, // 208: openshell.v1.UpdateProviderRequest.CredentialExpiresAtMsEntry - nil, // 209: openshell.v1.StoredProviderCredentialRefreshState.MaterialEntry - nil, // 210: openshell.v1.StoredProviderCredentialRefreshState.AdditionalOutputKeysEntry - nil, // 211: openshell.v1.StoredProviderCredentialRefreshState.SecretMaterialHandlesEntry - nil, // 212: openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry - nil, // 213: openshell.v1.ProviderProfile.AnnotationsEntry - nil, // 214: openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry - nil, // 215: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpiresAtMsEntry - nil, // 216: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry - nil, // 217: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry - nil, // 218: openshell.v1.UpdateConfigRequest.AnnotationsEntry - nil, // 219: openshell.v1.UpdateConfigResponse.AnnotationsEntry - nil, // 220: openshell.v1.SandboxPolicyRevision.ProvenanceEntry - nil, // 221: openshell.v1.PolicyRevisionPayload.ProvenanceEntry - nil, // 222: openshell.v1.StoredPolicyRevision.ProvenanceEntry - nil, // 223: openshell.v1.CreateWorkspaceRequest.LabelsEntry - (*datamodelv1.ObjectMeta)(nil), // 224: openshell.datamodel.v1.ObjectMeta - (*sandboxv1.SandboxPolicy)(nil), // 225: openshell.sandbox.v1.SandboxPolicy - (*structpb.Struct)(nil), // 226: google.protobuf.Struct - (*datamodelv1.Provider)(nil), // 227: openshell.datamodel.v1.Provider - (*datamodelv1.CredentialHandle)(nil), // 228: openshell.datamodel.v1.CredentialHandle - (*sandboxv1.NetworkEndpoint)(nil), // 229: openshell.sandbox.v1.NetworkEndpoint - (*sandboxv1.NetworkBinary)(nil), // 230: openshell.sandbox.v1.NetworkBinary - (*sandboxv1.SettingValue)(nil), // 231: openshell.sandbox.v1.SettingValue - (*sandboxv1.NetworkPolicyRule)(nil), // 232: openshell.sandbox.v1.NetworkPolicyRule - (*sandboxv1.L7DenyRule)(nil), // 233: openshell.sandbox.v1.L7DenyRule - (*sandboxv1.L7Rule)(nil), // 234: openshell.sandbox.v1.L7Rule - (*datamodelv1.Workspace)(nil), // 235: openshell.datamodel.v1.Workspace - (*sandboxv1.GetSandboxConfigRequest)(nil), // 236: openshell.sandbox.v1.GetSandboxConfigRequest - (*sandboxv1.GetGatewayConfigRequest)(nil), // 237: openshell.sandbox.v1.GetGatewayConfigRequest - (*sandboxv1.GetSandboxConfigResponse)(nil), // 238: openshell.sandbox.v1.GetSandboxConfigResponse - (*sandboxv1.GetGatewayConfigResponse)(nil), // 239: openshell.sandbox.v1.GetGatewayConfigResponse + (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 } var file_openshell_proto_depIdxs = []int32{ - 198, // 0: openshell.v1.RefreshSandboxTokenResponse.extension_credentials:type_name -> openshell.v1.ExtensionServiceCredential + 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 - 17, // 3: openshell.v1.GetGatewayInfoResponse.compute_drivers:type_name -> openshell.v1.ComputeDriverInfo - 18, // 4: openshell.v1.ComputeDriverInfo.capabilities:type_name -> openshell.v1.ComputeDriverCapabilities - 224, // 5: openshell.v1.Sandbox.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 20, // 6: openshell.v1.Sandbox.spec:type_name -> openshell.v1.SandboxSpec - 24, // 7: openshell.v1.Sandbox.status:type_name -> openshell.v1.SandboxStatus - 199, // 8: openshell.v1.SandboxSpec.environment:type_name -> openshell.v1.SandboxSpec.EnvironmentEntry - 23, // 9: openshell.v1.SandboxSpec.template:type_name -> openshell.v1.SandboxTemplate - 225, // 10: openshell.v1.SandboxSpec.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 21, // 11: openshell.v1.SandboxSpec.resource_requirements:type_name -> openshell.v1.ResourceRequirements - 22, // 12: openshell.v1.ResourceRequirements.gpu:type_name -> openshell.v1.GpuResourceRequirements - 200, // 13: openshell.v1.SandboxTemplate.labels:type_name -> openshell.v1.SandboxTemplate.LabelsEntry - 201, // 14: openshell.v1.SandboxTemplate.annotations:type_name -> openshell.v1.SandboxTemplate.AnnotationsEntry - 202, // 15: openshell.v1.SandboxTemplate.environment:type_name -> openshell.v1.SandboxTemplate.EnvironmentEntry - 226, // 16: openshell.v1.SandboxTemplate.resources:type_name -> google.protobuf.Struct - 226, // 17: openshell.v1.SandboxTemplate.driver_config:type_name -> google.protobuf.Struct - 25, // 18: openshell.v1.SandboxStatus.conditions:type_name -> openshell.v1.SandboxCondition + 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 0, // 19: openshell.v1.SandboxStatus.phase:type_name -> openshell.v1.SandboxPhase - 203, // 20: openshell.v1.PlatformEvent.metadata:type_name -> openshell.v1.PlatformEvent.MetadataEntry - 20, // 21: openshell.v1.CreateSandboxRequest.spec:type_name -> openshell.v1.SandboxSpec - 204, // 22: openshell.v1.CreateSandboxRequest.labels:type_name -> openshell.v1.CreateSandboxRequest.LabelsEntry - 205, // 23: openshell.v1.CreateSandboxRequest.annotations:type_name -> openshell.v1.CreateSandboxRequest.AnnotationsEntry - 19, // 24: openshell.v1.SandboxResponse.sandbox:type_name -> openshell.v1.Sandbox - 19, // 25: openshell.v1.ListSandboxesResponse.sandboxes:type_name -> openshell.v1.Sandbox - 227, // 26: openshell.v1.ListSandboxProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider - 19, // 27: openshell.v1.AttachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox - 19, // 28: openshell.v1.DetachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox - 51, // 29: openshell.v1.ListServicesResponse.services:type_name -> openshell.v1.ServiceEndpointResponse - 224, // 30: openshell.v1.ServiceEndpoint.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 50, // 31: openshell.v1.ServiceEndpointResponse.endpoint:type_name -> openshell.v1.ServiceEndpoint - 206, // 32: openshell.v1.ExecSandboxRequest.environment:type_name -> openshell.v1.ExecSandboxRequest.EnvironmentEntry - 55, // 33: openshell.v1.ExecSandboxEvent.stdout:type_name -> openshell.v1.ExecSandboxStdout - 56, // 34: openshell.v1.ExecSandboxEvent.stderr:type_name -> openshell.v1.ExecSandboxStderr - 57, // 35: openshell.v1.ExecSandboxEvent.exit:type_name -> openshell.v1.ExecSandboxExit - 147, // 36: openshell.v1.TcpForwardInit.ssh:type_name -> openshell.v1.SshRelayTarget - 148, // 37: openshell.v1.TcpForwardInit.tcp:type_name -> openshell.v1.TcpRelayTarget - 59, // 38: openshell.v1.TcpForwardFrame.init:type_name -> openshell.v1.TcpForwardInit - 54, // 39: openshell.v1.ExecSandboxInput.start:type_name -> openshell.v1.ExecSandboxRequest - 62, // 40: openshell.v1.ExecSandboxInput.resize:type_name -> openshell.v1.ExecSandboxWindowResize - 224, // 41: openshell.v1.SshSession.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 19, // 42: openshell.v1.SandboxStreamEvent.sandbox:type_name -> openshell.v1.Sandbox - 66, // 43: openshell.v1.SandboxStreamEvent.log:type_name -> openshell.v1.SandboxLogLine - 26, // 44: openshell.v1.SandboxStreamEvent.event:type_name -> openshell.v1.PlatformEvent - 67, // 45: openshell.v1.SandboxStreamEvent.warning:type_name -> openshell.v1.SandboxStreamWarning - 158, // 46: openshell.v1.SandboxStreamEvent.draft_policy_update:type_name -> openshell.v1.DraftPolicyUpdate - 207, // 47: openshell.v1.SandboxLogLine.fields:type_name -> openshell.v1.SandboxLogLine.FieldsEntry - 227, // 48: openshell.v1.CreateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider - 227, // 49: openshell.v1.UpdateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider - 208, // 50: openshell.v1.UpdateProviderRequest.credential_expires_at_ms:type_name -> openshell.v1.UpdateProviderRequest.CredentialExpiresAtMsEntry - 227, // 51: openshell.v1.ProviderResponse.provider:type_name -> openshell.datamodel.v1.Provider - 227, // 52: openshell.v1.ListProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider - 98, // 53: openshell.v1.ProviderProfileImportItem.profile:type_name -> openshell.v1.ProviderProfile - 79, // 54: openshell.v1.ProviderCredentialTokenGrant.audience_overrides:type_name -> openshell.v1.ProviderCredentialTokenGrantAudienceOverride + 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 1, // 55: openshell.v1.ProviderCredentialTokenGrant.grant_type:type_name -> openshell.v1.ProviderCredentialTokenGrantType - 80, // 56: openshell.v1.ProviderCredentialTokenGrant.subject_token:type_name -> openshell.v1.ProviderCredentialTokenGrantSubjectToken - 85, // 57: openshell.v1.ProviderProfileCredential.refresh:type_name -> openshell.v1.ProviderCredentialRefresh - 81, // 58: openshell.v1.ProviderProfileCredential.token_grant:type_name -> openshell.v1.ProviderCredentialTokenGrant + 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 2, // 59: openshell.v1.ProviderCredentialRefresh.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 83, // 60: openshell.v1.ProviderCredentialRefresh.material:type_name -> openshell.v1.ProviderCredentialRefreshMaterial - 84, // 61: openshell.v1.ProviderCredentialRefresh.additional_outputs:type_name -> openshell.v1.ProviderCredentialRefreshOutput + 84, // 60: openshell.v1.ProviderCredentialRefresh.material:type_name -> openshell.v1.ProviderCredentialRefreshMaterial + 85, // 61: openshell.v1.ProviderCredentialRefresh.additional_outputs:type_name -> openshell.v1.ProviderCredentialRefreshOutput 2, // 62: openshell.v1.ProviderCredentialRefreshStatus.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 224, // 63: openshell.v1.StoredProviderCredentialRefreshState.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 2, // 64: openshell.v1.StoredProviderCredentialRefreshState.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 209, // 65: openshell.v1.StoredProviderCredentialRefreshState.material:type_name -> openshell.v1.StoredProviderCredentialRefreshState.MaterialEntry - 210, // 66: openshell.v1.StoredProviderCredentialRefreshState.additional_output_keys:type_name -> openshell.v1.StoredProviderCredentialRefreshState.AdditionalOutputKeysEntry - 211, // 67: openshell.v1.StoredProviderCredentialRefreshState.secret_material_handles:type_name -> openshell.v1.StoredProviderCredentialRefreshState.SecretMaterialHandlesEntry - 89, // 68: openshell.v1.StoredProviderCredentialRefreshState.pending_secret_deletions:type_name -> openshell.v1.StoredRefreshMaterialDeletion - 228, // 69: openshell.v1.StoredRefreshMaterialDeletion.handle:type_name -> openshell.datamodel.v1.CredentialHandle - 86, // 70: openshell.v1.GetProviderRefreshStatusResponse.credentials:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 2, // 71: openshell.v1.ConfigureProviderRefreshRequest.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 212, // 72: openshell.v1.ConfigureProviderRefreshRequest.material:type_name -> openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry - 86, // 73: openshell.v1.ConfigureProviderRefreshResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 86, // 74: openshell.v1.RotateProviderCredentialResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 3, // 75: openshell.v1.ProviderProfile.category:type_name -> openshell.v1.ProviderProfileCategory - 82, // 76: openshell.v1.ProviderProfile.credentials:type_name -> openshell.v1.ProviderProfileCredential - 229, // 77: openshell.v1.ProviderProfile.endpoints:type_name -> openshell.sandbox.v1.NetworkEndpoint - 230, // 78: openshell.v1.ProviderProfile.binaries:type_name -> openshell.sandbox.v1.NetworkBinary - 87, // 79: openshell.v1.ProviderProfile.discovery:type_name -> openshell.v1.ProviderProfileDiscovery - 213, // 80: openshell.v1.ProviderProfile.annotations:type_name -> openshell.v1.ProviderProfile.AnnotationsEntry - 224, // 81: openshell.v1.StoredProviderProfile.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 98, // 82: openshell.v1.StoredProviderProfile.profile:type_name -> openshell.v1.ProviderProfile - 98, // 83: openshell.v1.ProviderProfileResponse.profile:type_name -> openshell.v1.ProviderProfile - 98, // 84: openshell.v1.ListProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile - 77, // 85: openshell.v1.ImportProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem - 78, // 86: openshell.v1.ImportProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 98, // 87: openshell.v1.ImportProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile - 77, // 88: openshell.v1.UpdateProviderProfilesRequest.profile:type_name -> openshell.v1.ProviderProfileImportItem - 78, // 89: openshell.v1.UpdateProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 98, // 90: openshell.v1.UpdateProviderProfilesResponse.profile:type_name -> openshell.v1.ProviderProfile - 77, // 91: openshell.v1.LintProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem - 78, // 92: openshell.v1.LintProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 112, // 93: openshell.v1.StaticCredentialBinding.endpoints:type_name -> openshell.v1.StaticCredentialEndpointBinding - 214, // 94: openshell.v1.GetSandboxProviderEnvironmentResponse.environment:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry - 215, // 95: openshell.v1.GetSandboxProviderEnvironmentResponse.credential_expires_at_ms:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpiresAtMsEntry - 216, // 96: openshell.v1.GetSandboxProviderEnvironmentResponse.dynamic_credentials:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry - 217, // 97: openshell.v1.GetSandboxProviderEnvironmentResponse.static_credential_bindings:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry - 225, // 98: openshell.v1.UpdateConfigRequest.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 231, // 99: openshell.v1.UpdateConfigRequest.setting_value:type_name -> openshell.sandbox.v1.SettingValue - 118, // 100: openshell.v1.UpdateConfigRequest.merge_operations:type_name -> openshell.v1.PolicyMergeOperation - 218, // 101: openshell.v1.UpdateConfigRequest.annotations:type_name -> openshell.v1.UpdateConfigRequest.AnnotationsEntry - 119, // 102: openshell.v1.PolicyMergeOperation.add_rule:type_name -> openshell.v1.AddNetworkRule - 120, // 103: openshell.v1.PolicyMergeOperation.remove_endpoint:type_name -> openshell.v1.RemoveNetworkEndpoint - 121, // 104: openshell.v1.PolicyMergeOperation.remove_rule:type_name -> openshell.v1.RemoveNetworkRule - 122, // 105: openshell.v1.PolicyMergeOperation.add_deny_rules:type_name -> openshell.v1.AddDenyRules - 123, // 106: openshell.v1.PolicyMergeOperation.add_allow_rules:type_name -> openshell.v1.AddAllowRules - 124, // 107: openshell.v1.PolicyMergeOperation.remove_binary:type_name -> openshell.v1.RemoveNetworkBinary - 232, // 108: openshell.v1.AddNetworkRule.rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 233, // 109: openshell.v1.AddDenyRules.deny_rules:type_name -> openshell.sandbox.v1.L7DenyRule - 234, // 110: openshell.v1.AddAllowRules.rules:type_name -> openshell.sandbox.v1.L7Rule - 219, // 111: openshell.v1.UpdateConfigResponse.annotations:type_name -> openshell.v1.UpdateConfigResponse.AnnotationsEntry - 132, // 112: openshell.v1.GetSandboxPolicyStatusResponse.revision:type_name -> openshell.v1.SandboxPolicyRevision - 132, // 113: openshell.v1.ListSandboxPoliciesResponse.revisions:type_name -> openshell.v1.SandboxPolicyRevision - 4, // 114: openshell.v1.ReportPolicyStatusRequest.status:type_name -> openshell.v1.PolicyStatus - 4, // 115: openshell.v1.SandboxPolicyRevision.status:type_name -> openshell.v1.PolicyStatus - 225, // 116: openshell.v1.SandboxPolicyRevision.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 220, // 117: openshell.v1.SandboxPolicyRevision.provenance:type_name -> openshell.v1.SandboxPolicyRevision.ProvenanceEntry - 66, // 118: openshell.v1.PushSandboxLogsRequest.logs:type_name -> openshell.v1.SandboxLogLine - 66, // 119: openshell.v1.GetSandboxLogsResponse.logs:type_name -> openshell.v1.SandboxLogLine - 139, // 120: openshell.v1.SupervisorMessage.hello:type_name -> openshell.v1.SupervisorHello - 142, // 121: openshell.v1.SupervisorMessage.heartbeat:type_name -> openshell.v1.SupervisorHeartbeat - 151, // 122: openshell.v1.SupervisorMessage.relay_open_result:type_name -> openshell.v1.RelayOpenResult - 152, // 123: openshell.v1.SupervisorMessage.relay_close:type_name -> openshell.v1.RelayClose - 140, // 124: openshell.v1.GatewayMessage.session_accepted:type_name -> openshell.v1.SessionAccepted - 141, // 125: openshell.v1.GatewayMessage.session_rejected:type_name -> openshell.v1.SessionRejected - 143, // 126: openshell.v1.GatewayMessage.heartbeat:type_name -> openshell.v1.GatewayHeartbeat - 146, // 127: openshell.v1.GatewayMessage.relay_open:type_name -> openshell.v1.RelayOpen - 152, // 128: openshell.v1.GatewayMessage.relay_close:type_name -> openshell.v1.RelayClose - 147, // 129: openshell.v1.RelayOpen.ssh:type_name -> openshell.v1.SshRelayTarget - 148, // 130: openshell.v1.RelayOpen.tcp:type_name -> openshell.v1.TcpRelayTarget - 149, // 131: openshell.v1.RelayFrame.init:type_name -> openshell.v1.RelayInit - 153, // 132: openshell.v1.DenialSummary.l7_request_samples:type_name -> openshell.v1.L7RequestSample - 155, // 133: openshell.v1.NetworkActivitySummary.denials_by_group:type_name -> openshell.v1.DenialGroupCount - 232, // 134: openshell.v1.PolicyChunk.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 225, // 135: openshell.v1.PolicyChunk.current_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 225, // 136: openshell.v1.PolicyChunk.candidate_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 154, // 137: openshell.v1.SubmitPolicyAnalysisRequest.summaries:type_name -> openshell.v1.DenialSummary - 157, // 138: openshell.v1.SubmitPolicyAnalysisRequest.proposed_chunks:type_name -> openshell.v1.PolicyChunk - 156, // 139: openshell.v1.SubmitPolicyAnalysisRequest.network_activity_summaries:type_name -> openshell.v1.NetworkActivitySummary - 157, // 140: openshell.v1.GetDraftPolicyResponse.chunks:type_name -> openshell.v1.PolicyChunk - 167, // 141: openshell.v1.ApproveAllDraftChunksRequest.approvals:type_name -> openshell.v1.DraftChunkApproval - 232, // 142: openshell.v1.EditDraftChunkRequest.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 177, // 143: openshell.v1.GetDraftHistoryResponse.entries:type_name -> openshell.v1.DraftHistoryEntry - 225, // 144: openshell.v1.PolicyRevisionPayload.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 221, // 145: openshell.v1.PolicyRevisionPayload.provenance:type_name -> openshell.v1.PolicyRevisionPayload.ProvenanceEntry - 232, // 146: openshell.v1.DraftChunkPayload.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 225, // 147: openshell.v1.DraftChunkPayload.current_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 225, // 148: openshell.v1.DraftChunkPayload.candidate_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 222, // 149: openshell.v1.StoredPolicyRevision.provenance:type_name -> openshell.v1.StoredPolicyRevision.ProvenanceEntry - 225, // 150: openshell.v1.StoredDraftChunk.current_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 225, // 151: openshell.v1.StoredDraftChunk.candidate_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 223, // 152: openshell.v1.CreateWorkspaceRequest.labels:type_name -> openshell.v1.CreateWorkspaceRequest.LabelsEntry - 235, // 153: openshell.v1.CreateWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace - 235, // 154: openshell.v1.GetWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace - 235, // 155: openshell.v1.ListWorkspacesResponse.workspaces:type_name -> openshell.datamodel.v1.Workspace - 224, // 156: openshell.v1.WorkspaceMember.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 6, // 157: openshell.v1.WorkspaceMember.role:type_name -> openshell.v1.WorkspaceRole - 6, // 158: openshell.v1.AddWorkspaceMemberRequest.role:type_name -> openshell.v1.WorkspaceRole - 191, // 159: openshell.v1.AddWorkspaceMemberResponse.member:type_name -> openshell.v1.WorkspaceMember - 191, // 160: openshell.v1.ListWorkspaceMembersResponse.members:type_name -> openshell.v1.WorkspaceMember - 228, // 161: openshell.v1.StoredProviderCredentialRefreshState.SecretMaterialHandlesEntry.value:type_name -> openshell.datamodel.v1.CredentialHandle - 82, // 162: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential - 113, // 163: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry.value:type_name -> openshell.v1.StaticCredentialBinding - 11, // 164: openshell.v1.OpenShell.Health:input_type -> openshell.v1.HealthRequest - 13, // 165: openshell.v1.OpenShell.GetCurrentUser:input_type -> openshell.v1.GetCurrentUserRequest - 15, // 166: openshell.v1.OpenShell.GetGatewayInfo:input_type -> openshell.v1.GetGatewayInfoRequest - 27, // 167: openshell.v1.OpenShell.CreateSandbox:input_type -> openshell.v1.CreateSandboxRequest - 28, // 168: openshell.v1.OpenShell.GetSandbox:input_type -> openshell.v1.GetSandboxRequest - 29, // 169: openshell.v1.OpenShell.ListSandboxes:input_type -> openshell.v1.ListSandboxesRequest - 30, // 170: openshell.v1.OpenShell.ListSandboxProviders:input_type -> openshell.v1.ListSandboxProvidersRequest - 31, // 171: openshell.v1.OpenShell.AttachSandboxProvider:input_type -> openshell.v1.AttachSandboxProviderRequest - 32, // 172: openshell.v1.OpenShell.DetachSandboxProvider:input_type -> openshell.v1.DetachSandboxProviderRequest - 33, // 173: openshell.v1.OpenShell.DeleteSandbox:input_type -> openshell.v1.DeleteSandboxRequest - 34, // 174: openshell.v1.OpenShell.StopSandbox:input_type -> openshell.v1.StopSandboxRequest - 35, // 175: openshell.v1.OpenShell.StartSandbox:input_type -> openshell.v1.StartSandboxRequest - 42, // 176: openshell.v1.OpenShell.CreateSshSession:input_type -> openshell.v1.CreateSshSessionRequest - 44, // 177: openshell.v1.OpenShell.ExposeService:input_type -> openshell.v1.ExposeServiceRequest - 45, // 178: openshell.v1.OpenShell.GetService:input_type -> openshell.v1.GetServiceRequest - 46, // 179: openshell.v1.OpenShell.ListServices:input_type -> openshell.v1.ListServicesRequest - 48, // 180: openshell.v1.OpenShell.DeleteService:input_type -> openshell.v1.DeleteServiceRequest - 52, // 181: openshell.v1.OpenShell.RevokeSshSession:input_type -> openshell.v1.RevokeSshSessionRequest - 54, // 182: openshell.v1.OpenShell.ExecSandbox:input_type -> openshell.v1.ExecSandboxRequest - 60, // 183: openshell.v1.OpenShell.ForwardTcp:input_type -> openshell.v1.TcpForwardFrame - 61, // 184: openshell.v1.OpenShell.ExecSandboxInteractive:input_type -> openshell.v1.ExecSandboxInput - 68, // 185: openshell.v1.OpenShell.CreateProvider:input_type -> openshell.v1.CreateProviderRequest - 69, // 186: openshell.v1.OpenShell.GetProvider:input_type -> openshell.v1.GetProviderRequest - 70, // 187: openshell.v1.OpenShell.ListProviders:input_type -> openshell.v1.ListProvidersRequest - 75, // 188: openshell.v1.OpenShell.ListProviderProfiles:input_type -> openshell.v1.ListProviderProfilesRequest - 76, // 189: openshell.v1.OpenShell.GetProviderProfile:input_type -> openshell.v1.GetProviderProfileRequest - 102, // 190: openshell.v1.OpenShell.ImportProviderProfiles:input_type -> openshell.v1.ImportProviderProfilesRequest - 104, // 191: openshell.v1.OpenShell.UpdateProviderProfiles:input_type -> openshell.v1.UpdateProviderProfilesRequest - 106, // 192: openshell.v1.OpenShell.LintProviderProfiles:input_type -> openshell.v1.LintProviderProfilesRequest - 71, // 193: openshell.v1.OpenShell.UpdateProvider:input_type -> openshell.v1.UpdateProviderRequest - 90, // 194: openshell.v1.OpenShell.GetProviderRefreshStatus:input_type -> openshell.v1.GetProviderRefreshStatusRequest - 92, // 195: openshell.v1.OpenShell.ConfigureProviderRefresh:input_type -> openshell.v1.ConfigureProviderRefreshRequest - 94, // 196: openshell.v1.OpenShell.RotateProviderCredential:input_type -> openshell.v1.RotateProviderCredentialRequest - 96, // 197: openshell.v1.OpenShell.DeleteProviderRefresh:input_type -> openshell.v1.DeleteProviderRefreshRequest - 72, // 198: openshell.v1.OpenShell.DeleteProvider:input_type -> openshell.v1.DeleteProviderRequest - 109, // 199: openshell.v1.OpenShell.DeleteProviderProfile:input_type -> openshell.v1.DeleteProviderProfileRequest - 236, // 200: openshell.v1.OpenShell.GetSandboxConfig:input_type -> openshell.sandbox.v1.GetSandboxConfigRequest - 237, // 201: openshell.v1.OpenShell.GetGatewayConfig:input_type -> openshell.sandbox.v1.GetGatewayConfigRequest - 117, // 202: openshell.v1.OpenShell.UpdateConfig:input_type -> openshell.v1.UpdateConfigRequest - 126, // 203: openshell.v1.OpenShell.GetSandboxPolicyStatus:input_type -> openshell.v1.GetSandboxPolicyStatusRequest - 128, // 204: openshell.v1.OpenShell.ListSandboxPolicies:input_type -> openshell.v1.ListSandboxPoliciesRequest - 130, // 205: openshell.v1.OpenShell.ReportPolicyStatus:input_type -> openshell.v1.ReportPolicyStatusRequest - 111, // 206: openshell.v1.OpenShell.GetSandboxProviderEnvironment:input_type -> openshell.v1.GetSandboxProviderEnvironmentRequest - 115, // 207: openshell.v1.OpenShell.ExchangeProviderSubjectToken:input_type -> openshell.v1.ExchangeProviderSubjectTokenRequest - 133, // 208: openshell.v1.OpenShell.GetSandboxLogs:input_type -> openshell.v1.GetSandboxLogsRequest - 134, // 209: openshell.v1.OpenShell.PushSandboxLogs:input_type -> openshell.v1.PushSandboxLogsRequest - 137, // 210: openshell.v1.OpenShell.ConnectSupervisor:input_type -> openshell.v1.SupervisorMessage - 144, // 211: openshell.v1.OpenShell.ReportMainProcessExit:input_type -> openshell.v1.ReportMainProcessExitRequest - 150, // 212: openshell.v1.OpenShell.RelayStream:input_type -> openshell.v1.RelayFrame - 64, // 213: openshell.v1.OpenShell.WatchSandbox:input_type -> openshell.v1.WatchSandboxRequest - 159, // 214: openshell.v1.OpenShell.SubmitPolicyAnalysis:input_type -> openshell.v1.SubmitPolicyAnalysisRequest - 161, // 215: openshell.v1.OpenShell.GetDraftPolicy:input_type -> openshell.v1.GetDraftPolicyRequest - 163, // 216: openshell.v1.OpenShell.ApproveDraftChunk:input_type -> openshell.v1.ApproveDraftChunkRequest - 165, // 217: openshell.v1.OpenShell.RejectDraftChunk:input_type -> openshell.v1.RejectDraftChunkRequest - 168, // 218: openshell.v1.OpenShell.ApproveAllDraftChunks:input_type -> openshell.v1.ApproveAllDraftChunksRequest - 170, // 219: openshell.v1.OpenShell.EditDraftChunk:input_type -> openshell.v1.EditDraftChunkRequest - 172, // 220: openshell.v1.OpenShell.UndoDraftChunk:input_type -> openshell.v1.UndoDraftChunkRequest - 174, // 221: openshell.v1.OpenShell.ClearDraftChunks:input_type -> openshell.v1.ClearDraftChunksRequest - 176, // 222: openshell.v1.OpenShell.GetDraftHistory:input_type -> openshell.v1.GetDraftHistoryRequest - 7, // 223: openshell.v1.OpenShell.IssueSandboxToken:input_type -> openshell.v1.IssueSandboxTokenRequest - 9, // 224: openshell.v1.OpenShell.RefreshSandboxToken:input_type -> openshell.v1.RefreshSandboxTokenRequest - 183, // 225: openshell.v1.OpenShell.CreateWorkspace:input_type -> openshell.v1.CreateWorkspaceRequest - 185, // 226: openshell.v1.OpenShell.GetWorkspace:input_type -> openshell.v1.GetWorkspaceRequest - 187, // 227: openshell.v1.OpenShell.ListWorkspaces:input_type -> openshell.v1.ListWorkspacesRequest - 189, // 228: openshell.v1.OpenShell.DeleteWorkspace:input_type -> openshell.v1.DeleteWorkspaceRequest - 192, // 229: openshell.v1.OpenShell.AddWorkspaceMember:input_type -> openshell.v1.AddWorkspaceMemberRequest - 194, // 230: openshell.v1.OpenShell.RemoveWorkspaceMember:input_type -> openshell.v1.RemoveWorkspaceMemberRequest - 196, // 231: openshell.v1.OpenShell.ListWorkspaceMembers:input_type -> openshell.v1.ListWorkspaceMembersRequest - 12, // 232: openshell.v1.OpenShell.Health:output_type -> openshell.v1.HealthResponse - 14, // 233: openshell.v1.OpenShell.GetCurrentUser:output_type -> openshell.v1.GetCurrentUserResponse - 16, // 234: openshell.v1.OpenShell.GetGatewayInfo:output_type -> openshell.v1.GetGatewayInfoResponse - 36, // 235: openshell.v1.OpenShell.CreateSandbox:output_type -> openshell.v1.SandboxResponse - 36, // 236: openshell.v1.OpenShell.GetSandbox:output_type -> openshell.v1.SandboxResponse - 37, // 237: openshell.v1.OpenShell.ListSandboxes:output_type -> openshell.v1.ListSandboxesResponse - 38, // 238: openshell.v1.OpenShell.ListSandboxProviders:output_type -> openshell.v1.ListSandboxProvidersResponse - 39, // 239: openshell.v1.OpenShell.AttachSandboxProvider:output_type -> openshell.v1.AttachSandboxProviderResponse - 40, // 240: openshell.v1.OpenShell.DetachSandboxProvider:output_type -> openshell.v1.DetachSandboxProviderResponse - 41, // 241: openshell.v1.OpenShell.DeleteSandbox:output_type -> openshell.v1.DeleteSandboxResponse - 36, // 242: openshell.v1.OpenShell.StopSandbox:output_type -> openshell.v1.SandboxResponse - 36, // 243: openshell.v1.OpenShell.StartSandbox:output_type -> openshell.v1.SandboxResponse - 43, // 244: openshell.v1.OpenShell.CreateSshSession:output_type -> openshell.v1.CreateSshSessionResponse - 51, // 245: openshell.v1.OpenShell.ExposeService:output_type -> openshell.v1.ServiceEndpointResponse - 51, // 246: openshell.v1.OpenShell.GetService:output_type -> openshell.v1.ServiceEndpointResponse - 47, // 247: openshell.v1.OpenShell.ListServices:output_type -> openshell.v1.ListServicesResponse - 49, // 248: openshell.v1.OpenShell.DeleteService:output_type -> openshell.v1.DeleteServiceResponse - 53, // 249: openshell.v1.OpenShell.RevokeSshSession:output_type -> openshell.v1.RevokeSshSessionResponse - 58, // 250: openshell.v1.OpenShell.ExecSandbox:output_type -> openshell.v1.ExecSandboxEvent - 60, // 251: openshell.v1.OpenShell.ForwardTcp:output_type -> openshell.v1.TcpForwardFrame - 58, // 252: openshell.v1.OpenShell.ExecSandboxInteractive:output_type -> openshell.v1.ExecSandboxEvent - 73, // 253: openshell.v1.OpenShell.CreateProvider:output_type -> openshell.v1.ProviderResponse - 73, // 254: openshell.v1.OpenShell.GetProvider:output_type -> openshell.v1.ProviderResponse - 74, // 255: openshell.v1.OpenShell.ListProviders:output_type -> openshell.v1.ListProvidersResponse - 101, // 256: openshell.v1.OpenShell.ListProviderProfiles:output_type -> openshell.v1.ListProviderProfilesResponse - 100, // 257: openshell.v1.OpenShell.GetProviderProfile:output_type -> openshell.v1.ProviderProfileResponse - 103, // 258: openshell.v1.OpenShell.ImportProviderProfiles:output_type -> openshell.v1.ImportProviderProfilesResponse - 105, // 259: openshell.v1.OpenShell.UpdateProviderProfiles:output_type -> openshell.v1.UpdateProviderProfilesResponse - 107, // 260: openshell.v1.OpenShell.LintProviderProfiles:output_type -> openshell.v1.LintProviderProfilesResponse - 73, // 261: openshell.v1.OpenShell.UpdateProvider:output_type -> openshell.v1.ProviderResponse - 91, // 262: openshell.v1.OpenShell.GetProviderRefreshStatus:output_type -> openshell.v1.GetProviderRefreshStatusResponse - 93, // 263: openshell.v1.OpenShell.ConfigureProviderRefresh:output_type -> openshell.v1.ConfigureProviderRefreshResponse - 95, // 264: openshell.v1.OpenShell.RotateProviderCredential:output_type -> openshell.v1.RotateProviderCredentialResponse - 97, // 265: openshell.v1.OpenShell.DeleteProviderRefresh:output_type -> openshell.v1.DeleteProviderRefreshResponse - 108, // 266: openshell.v1.OpenShell.DeleteProvider:output_type -> openshell.v1.DeleteProviderResponse - 110, // 267: openshell.v1.OpenShell.DeleteProviderProfile:output_type -> openshell.v1.DeleteProviderProfileResponse - 238, // 268: openshell.v1.OpenShell.GetSandboxConfig:output_type -> openshell.sandbox.v1.GetSandboxConfigResponse - 239, // 269: openshell.v1.OpenShell.GetGatewayConfig:output_type -> openshell.sandbox.v1.GetGatewayConfigResponse - 125, // 270: openshell.v1.OpenShell.UpdateConfig:output_type -> openshell.v1.UpdateConfigResponse - 127, // 271: openshell.v1.OpenShell.GetSandboxPolicyStatus:output_type -> openshell.v1.GetSandboxPolicyStatusResponse - 129, // 272: openshell.v1.OpenShell.ListSandboxPolicies:output_type -> openshell.v1.ListSandboxPoliciesResponse - 131, // 273: openshell.v1.OpenShell.ReportPolicyStatus:output_type -> openshell.v1.ReportPolicyStatusResponse - 114, // 274: openshell.v1.OpenShell.GetSandboxProviderEnvironment:output_type -> openshell.v1.GetSandboxProviderEnvironmentResponse - 116, // 275: openshell.v1.OpenShell.ExchangeProviderSubjectToken:output_type -> openshell.v1.ExchangeProviderSubjectTokenResponse - 136, // 276: openshell.v1.OpenShell.GetSandboxLogs:output_type -> openshell.v1.GetSandboxLogsResponse - 135, // 277: openshell.v1.OpenShell.PushSandboxLogs:output_type -> openshell.v1.PushSandboxLogsResponse - 138, // 278: openshell.v1.OpenShell.ConnectSupervisor:output_type -> openshell.v1.GatewayMessage - 145, // 279: openshell.v1.OpenShell.ReportMainProcessExit:output_type -> openshell.v1.ReportMainProcessExitResponse - 150, // 280: openshell.v1.OpenShell.RelayStream:output_type -> openshell.v1.RelayFrame - 65, // 281: openshell.v1.OpenShell.WatchSandbox:output_type -> openshell.v1.SandboxStreamEvent - 160, // 282: openshell.v1.OpenShell.SubmitPolicyAnalysis:output_type -> openshell.v1.SubmitPolicyAnalysisResponse - 162, // 283: openshell.v1.OpenShell.GetDraftPolicy:output_type -> openshell.v1.GetDraftPolicyResponse - 164, // 284: openshell.v1.OpenShell.ApproveDraftChunk:output_type -> openshell.v1.ApproveDraftChunkResponse - 166, // 285: openshell.v1.OpenShell.RejectDraftChunk:output_type -> openshell.v1.RejectDraftChunkResponse - 169, // 286: openshell.v1.OpenShell.ApproveAllDraftChunks:output_type -> openshell.v1.ApproveAllDraftChunksResponse - 171, // 287: openshell.v1.OpenShell.EditDraftChunk:output_type -> openshell.v1.EditDraftChunkResponse - 173, // 288: openshell.v1.OpenShell.UndoDraftChunk:output_type -> openshell.v1.UndoDraftChunkResponse - 175, // 289: openshell.v1.OpenShell.ClearDraftChunks:output_type -> openshell.v1.ClearDraftChunksResponse - 178, // 290: openshell.v1.OpenShell.GetDraftHistory:output_type -> openshell.v1.GetDraftHistoryResponse - 8, // 291: openshell.v1.OpenShell.IssueSandboxToken:output_type -> openshell.v1.IssueSandboxTokenResponse - 10, // 292: openshell.v1.OpenShell.RefreshSandboxToken:output_type -> openshell.v1.RefreshSandboxTokenResponse - 184, // 293: openshell.v1.OpenShell.CreateWorkspace:output_type -> openshell.v1.CreateWorkspaceResponse - 186, // 294: openshell.v1.OpenShell.GetWorkspace:output_type -> openshell.v1.GetWorkspaceResponse - 188, // 295: openshell.v1.OpenShell.ListWorkspaces:output_type -> openshell.v1.ListWorkspacesResponse - 190, // 296: openshell.v1.OpenShell.DeleteWorkspace:output_type -> openshell.v1.DeleteWorkspaceResponse - 193, // 297: openshell.v1.OpenShell.AddWorkspaceMember:output_type -> openshell.v1.AddWorkspaceMemberResponse - 195, // 298: openshell.v1.OpenShell.RemoveWorkspaceMember:output_type -> openshell.v1.RemoveWorkspaceMemberResponse - 197, // 299: openshell.v1.OpenShell.ListWorkspaceMembers:output_type -> openshell.v1.ListWorkspaceMembersResponse - 232, // [232:300] is the sub-list for method output_type - 164, // [164:232] is the sub-list for method input_type - 164, // [164:164] is the sub-list for extension type_name - 164, // [164:164] is the sub-list for extension extendee - 0, // [0:164] is the sub-list for field type_name + 7, // 63: openshell.v1.ProviderCredentialRefreshStatus.recovery_action:type_name -> openshell.v1.ProviderCredentialRefreshRecoveryAction + 225, // 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 + 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 + 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 + 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 } func init() { file_openshell_proto_init() } @@ -15706,7 +15860,7 @@ func file_openshell_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_openshell_proto_rawDesc), len(file_openshell_proto_rawDesc)), - NumEnums: 7, + NumEnums: 8, NumMessages: 217, NumExtensions: 0, NumServices: 1, diff --git a/tasks/test.toml b/tasks/test.toml index e3328d12b..dfb632dda 100644 --- a/tasks/test.toml +++ b/tasks/test.toml @@ -116,6 +116,12 @@ run = [ "CONTAINER_RUNTIME=podman e2e/with-keycloak.sh env OPENSHELL_E2E_OIDC_GATEWAY=1 e2e/with-podman-gateway.sh cargo test --manifest-path e2e/rust/Cargo.toml --features e2e-oidc-pkce --test oidc_pkce", ] +["e2e:provider-refresh-keycloak"] +description = "Run provider refresh recovery e2e against Keycloak and a Podman gateway" +run = [ + "CONTAINER_RUNTIME=podman e2e/with-keycloak.sh e2e/with-podman-gateway.sh cargo test --manifest-path e2e/rust/Cargo.toml --features e2e-provider-refresh-keycloak --test provider_refresh_keycloak -- --nocapture", +] + ["e2e:oidc-pkce:docker"] description = "Run Linux browser PKCE and RBAC e2e tests against Keycloak and a Docker gateway" run = [