mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-04 08:28:19 +08:00
* fix(mxc): reject cpu/memory limits instead of silently discarding them CreateSandbox accepted --cpu/--memory and reached Ready with no Job Object enforcement and no diagnostic, leaving the SDD's T11 host-exhaustion mitigation silently unmet. MXC's schema does not expose CPU rate control or memory limiting outside the WSLC backend, so reject requests carrying cpu/memory limits synchronously at CreateSandbox, matching the existing fail-closed GPU rejection. NVBug 6782894 Signed-off-by: Prashant Khodade <pkhodade@nvidia.com> * fix(server): validate sandbox resource quantities Signed-off-by: Shailendra Singh <shailendras@nvidia.com> * docs(skill): clarify MXC resource limit behavior Signed-off-by: Shailendra Singh <shailendras@nvidia.com> * chore(go): regenerate protobuf bindings Signed-off-by: Shailendra Singh <shailendras@nvidia.com> * fix(go): align generated protobuf comments Signed-off-by: Shailendra Singh <shailendras@nvidia.com> --------- Signed-off-by: Prashant Khodade <pkhodade@nvidia.com> Signed-off-by: Shailendra Singh <shailendras@nvidia.com> Co-authored-by: Shailendra Singh <shailendras@nvidia.com>
3206 lines
110 KiB
Protocol Buffer
3206 lines
110 KiB
Protocol Buffer
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
syntax = "proto3";
|
|
|
|
package openshell.v1;
|
|
|
|
import "datamodel.proto";
|
|
import "google/protobuf/duration.proto";
|
|
import "google/protobuf/struct.proto";
|
|
import "options.proto";
|
|
import "sandbox.proto";
|
|
|
|
// OpenShell service provides sandbox, provider, and runtime management capabilities.
|
|
//
|
|
// Conventions:
|
|
// - This file owns the public API resource model exposed to OpenShell clients.
|
|
// - `Sandbox`, `SandboxSpec`, `SandboxStatus`, and `SandboxPhase` are gateway-owned
|
|
// public types. Internal compute drivers must not import or return them directly.
|
|
// - The gateway translates internal compute-driver observations into these public
|
|
// resource messages before persisting or returning them to clients.
|
|
service OpenShell {
|
|
// Check the health of the service.
|
|
rpc Health(HealthRequest) returns (HealthResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "unauthenticated"
|
|
};
|
|
}
|
|
|
|
// Return the authenticated caller identity established by the gateway.
|
|
rpc GetCurrentUser(GetCurrentUserRequest) returns (GetCurrentUserResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
};
|
|
}
|
|
|
|
// Fetch elevated live gateway runtime metadata.
|
|
rpc GetGatewayInfo(GetGatewayInfoRequest) returns (GetGatewayInfoResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "config:read"
|
|
global_role: "platform_admin"
|
|
};
|
|
}
|
|
|
|
// Create a new sandbox.
|
|
rpc CreateSandbox(CreateSandboxRequest) returns (SandboxResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:write"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Allocate a gateway-owned staging slot for a local rootfs tar archive.
|
|
//
|
|
// The gateway creates a request-scoped directory inside the compute driver's
|
|
// staging root and returns an opaque single-use token plus the absolute path
|
|
// the client must write the archive to. The token is then passed as
|
|
// `template.driver_config.<driver>.rootfs_tar_staging_token` on
|
|
// CreateSandbox; callers never name a filesystem path themselves. Only a
|
|
// client sharing the gateway's filesystem can complete the upload.
|
|
rpc BeginRootfsTarStaging(BeginRootfsTarStagingRequest)
|
|
returns (BeginRootfsTarStagingResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:write"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Fetch a sandbox by name.
|
|
rpc GetSandbox(GetSandboxRequest) returns (SandboxResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// List sandboxes.
|
|
rpc ListSandboxes(ListSandboxesRequest) returns (ListSandboxesResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Create a reusable sandbox workload template.
|
|
rpc CreateSandboxTemplate(CreateSandboxTemplateRequest)
|
|
returns (SandboxTemplateResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Fetch a reusable sandbox workload template by name.
|
|
rpc GetSandboxTemplate(GetSandboxTemplateRequest)
|
|
returns (SandboxTemplateResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// List reusable sandbox workload templates.
|
|
rpc ListSandboxTemplates(ListSandboxTemplatesRequest)
|
|
returns (ListSandboxTemplatesResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Delete a reusable sandbox workload template by name.
|
|
rpc DeleteSandboxTemplate(DeleteSandboxTemplateRequest)
|
|
returns (DeleteSandboxTemplateResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// List provider records attached to a sandbox.
|
|
rpc ListSandboxProviders(ListSandboxProvidersRequest)
|
|
returns (ListSandboxProvidersResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Attach a provider record to an existing sandbox.
|
|
rpc AttachSandboxProvider(AttachSandboxProviderRequest)
|
|
returns (AttachSandboxProviderResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:write"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Detach a provider record from an existing sandbox.
|
|
rpc DetachSandboxProvider(DetachSandboxProviderRequest)
|
|
returns (DetachSandboxProviderResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:write"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Delete a sandbox by name.
|
|
rpc DeleteSandbox(DeleteSandboxRequest) returns (DeleteSandboxResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:write"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Stop a sandbox while retaining its persistent state.
|
|
rpc StopSandbox(StopSandboxRequest) returns (SandboxResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:write"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Start a previously stopped sandbox.
|
|
rpc StartSandbox(StartSandboxRequest) returns (SandboxResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:write"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Create a short-lived SSH session for a sandbox.
|
|
rpc CreateSshSession(CreateSshSessionRequest) returns (CreateSshSessionResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:write"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Create or update a sandbox HTTP service endpoint for local routing.
|
|
rpc ExposeService(ExposeServiceRequest) returns (ServiceEndpointResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:write"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Fetch one sandbox HTTP service endpoint.
|
|
rpc GetService(GetServiceRequest) returns (ServiceEndpointResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// List sandbox HTTP service endpoints.
|
|
rpc ListServices(ListServicesRequest) returns (ListServicesResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Delete one sandbox HTTP service endpoint.
|
|
rpc DeleteService(DeleteServiceRequest) returns (DeleteServiceResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:write"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Revoke a previously issued SSH session.
|
|
rpc RevokeSshSession(RevokeSshSessionRequest) returns (RevokeSshSessionResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:write"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Execute a command in a ready sandbox and stream output.
|
|
rpc ExecSandbox(ExecSandboxRequest) returns (stream ExecSandboxEvent) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:write"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Forward one CLI-side TCP connection to a loopback TCP target in a sandbox.
|
|
rpc ForwardTcp(stream TcpForwardFrame) returns (stream TcpForwardFrame) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:write"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Execute an interactive command with bidirectional stdin/stdout streaming.
|
|
// The first client message MUST carry an ExecSandboxInput with the start
|
|
// variant. Subsequent messages carry stdin bytes or window resize events.
|
|
rpc ExecSandboxInteractive(stream ExecSandboxInput) returns (stream ExecSandboxEvent) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:write"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Create a provider.
|
|
rpc CreateProvider(CreateProviderRequest) returns (ProviderResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "provider:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Fetch a provider by name.
|
|
rpc GetProvider(GetProviderRequest) returns (ProviderResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "provider:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// List providers.
|
|
rpc ListProviders(ListProvidersRequest) returns (ListProvidersResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "provider:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// List available provider type profiles.
|
|
rpc ListProviderProfiles(ListProviderProfilesRequest)
|
|
returns (ListProviderProfilesResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "provider:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Fetch one provider type profile by id.
|
|
rpc GetProviderProfile(GetProviderProfileRequest)
|
|
returns (ProviderProfileResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "provider:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Import custom provider type profiles.
|
|
rpc ImportProviderProfiles(ImportProviderProfilesRequest)
|
|
returns (ImportProviderProfilesResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "provider:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Update an existing custom provider type profile.
|
|
rpc UpdateProviderProfiles(UpdateProviderProfilesRequest)
|
|
returns (UpdateProviderProfilesResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "provider:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Validate provider type profiles without registering them.
|
|
rpc LintProviderProfiles(LintProviderProfilesRequest)
|
|
returns (LintProviderProfilesResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "provider:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Update an existing provider by name.
|
|
rpc UpdateProvider(UpdateProviderRequest) returns (ProviderResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "provider:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Fetch refresh status for one provider or provider credential.
|
|
rpc GetProviderRefreshStatus(GetProviderRefreshStatusRequest)
|
|
returns (GetProviderRefreshStatusResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "provider:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Configure gateway-owned refresh material for one provider credential.
|
|
rpc ConfigureProviderRefresh(ConfigureProviderRefreshRequest)
|
|
returns (ConfigureProviderRefreshResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "provider:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Record a gateway-owned refresh request for one provider credential.
|
|
rpc RotateProviderCredential(RotateProviderCredentialRequest)
|
|
returns (RotateProviderCredentialResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "provider:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Delete gateway-owned refresh configuration for one provider credential.
|
|
rpc DeleteProviderRefresh(DeleteProviderRefreshRequest)
|
|
returns (DeleteProviderRefreshResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "provider:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Delete a provider by name.
|
|
rpc DeleteProvider(DeleteProviderRequest) returns (DeleteProviderResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "provider:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Delete a custom provider type profile by id.
|
|
rpc DeleteProviderProfile(DeleteProviderProfileRequest)
|
|
returns (DeleteProviderProfileResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "provider:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Get sandbox settings by id (called by sandbox entrypoint and poll loop).
|
|
rpc GetSandboxConfig(openshell.sandbox.v1.GetSandboxConfigRequest)
|
|
returns (openshell.sandbox.v1.GetSandboxConfigResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "dual"
|
|
scope: "config:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Get gateway-global settings (read-only runtime configuration; any
|
|
// authenticated user may read these without requiring Platform Admin).
|
|
//
|
|
// Scope-only (no role): scopes are granted by the IdP at token issuance,
|
|
// orthogonal to workspace membership. Deployments that enable scope
|
|
// enforcement configure the IdP to grant config:read (or openshell:all)
|
|
// to all sandbox users, so this does not block least-privilege flows.
|
|
rpc GetGatewayConfig(openshell.sandbox.v1.GetGatewayConfigRequest)
|
|
returns (openshell.sandbox.v1.GetGatewayConfigResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "config:read"
|
|
};
|
|
}
|
|
|
|
// Update settings or policy at sandbox or global scope.
|
|
rpc UpdateConfig(UpdateConfigRequest)
|
|
returns (UpdateConfigResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "dual"
|
|
scope: "config:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Get the load status of a specific policy version.
|
|
rpc GetSandboxPolicyStatus(GetSandboxPolicyStatusRequest)
|
|
returns (GetSandboxPolicyStatusResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// List policy history for a sandbox.
|
|
rpc ListSandboxPolicies(ListSandboxPoliciesRequest)
|
|
returns (ListSandboxPoliciesResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Report policy load result (called by sandbox after reload attempt).
|
|
rpc ReportPolicyStatus(ReportPolicyStatusRequest)
|
|
returns (ReportPolicyStatusResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "sandbox"
|
|
};
|
|
}
|
|
|
|
// Replace the gateway's observed tool server endpoint status for one sandbox.
|
|
rpc ReportEndpointStatus(ReportEndpointStatusRequest)
|
|
returns (ReportEndpointStatusResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "sandbox"
|
|
};
|
|
}
|
|
|
|
// Get provider environment for a sandbox (called by sandbox supervisor at startup).
|
|
rpc GetSandboxProviderEnvironment(GetSandboxProviderEnvironmentRequest)
|
|
returns (GetSandboxProviderEnvironmentResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "sandbox"
|
|
};
|
|
}
|
|
|
|
// Exchange a stored provider subject token for an intermediate token scoped
|
|
// to the calling supervisor's SPIFFE identity.
|
|
rpc ExchangeProviderSubjectToken(ExchangeProviderSubjectTokenRequest)
|
|
returns (ExchangeProviderSubjectTokenResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "sandbox"
|
|
};
|
|
}
|
|
|
|
// Fetch recent sandbox logs (one-shot).
|
|
rpc GetSandboxLogs(GetSandboxLogsRequest) returns (GetSandboxLogsResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Push sandbox supervisor logs to the server (client-streaming).
|
|
rpc PushSandboxLogs(stream PushSandboxLogsRequest) returns (PushSandboxLogsResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "sandbox"
|
|
};
|
|
}
|
|
|
|
// Persistent supervisor-to-gateway session (bidirectional streaming).
|
|
//
|
|
// The supervisor opens this stream at startup and keeps it alive for the
|
|
// sandbox lifetime. The gateway uses it to coordinate relay channels for
|
|
// SSH connect, ExecSandbox, and targetable sandbox services. Raw service
|
|
// bytes flow over RelayStream calls (separate HTTP/2 streams on the same
|
|
// connection), not over this stream.
|
|
rpc ConnectSupervisor(stream SupervisorMessage) returns (stream GatewayMessage) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "sandbox"
|
|
};
|
|
}
|
|
|
|
// Persist the canonical main process result before the supervisor exits.
|
|
rpc ReportMainProcessExit(ReportMainProcessExitRequest) returns (ReportMainProcessExitResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "sandbox"
|
|
};
|
|
}
|
|
|
|
// Confirm that foreground terminal delivery completed naturally.
|
|
rpc FinalizeMainProcessExit(FinalizeMainProcessExitRequest)
|
|
returns (FinalizeMainProcessExitResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "sandbox"
|
|
};
|
|
}
|
|
|
|
// Raw byte relay between supervisor and gateway.
|
|
//
|
|
// The supervisor initiates this call after receiving a RelayOpen message
|
|
// on its ConnectSupervisor stream. The first RelayFrame carries a
|
|
// RelayInit with the channel_id to associate the new HTTP/2 stream with
|
|
// the pending relay slot on the gateway. Subsequent frames carry raw bytes in either
|
|
// direction between the gateway-side waiter (ForwardTcp / exec handler)
|
|
// and the supervisor-side target bridge.
|
|
//
|
|
// This rides the same TCP+TLS+HTTP/2 connection as ConnectSupervisor —
|
|
// no new TLS handshake, no reverse HTTP CONNECT.
|
|
rpc RelayStream(stream RelayFrame) returns (stream RelayFrame) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "sandbox"
|
|
};
|
|
}
|
|
|
|
// Watch a sandbox and stream updates.
|
|
//
|
|
// This stream can include:
|
|
// - Sandbox status snapshots (phase/status)
|
|
// - OpenShell server process logs correlated by sandbox_id
|
|
// - Platform events correlated to the sandbox
|
|
rpc WatchSandbox(WatchSandboxRequest) returns (stream SandboxStreamEvent) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Draft policy recommendation RPCs
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Submit denial analysis results from sandbox (summaries + proposed chunks).
|
|
rpc SubmitPolicyAnalysis(SubmitPolicyAnalysisRequest)
|
|
returns (SubmitPolicyAnalysisResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "sandbox"
|
|
};
|
|
}
|
|
|
|
// Get draft policy recommendations for a sandbox.
|
|
rpc GetDraftPolicy(GetDraftPolicyRequest) returns (GetDraftPolicyResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "dual"
|
|
scope: "config:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Approve a single draft policy chunk (merges into active policy).
|
|
rpc ApproveDraftChunk(ApproveDraftChunkRequest)
|
|
returns (ApproveDraftChunkResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "config:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Reject a single draft policy chunk.
|
|
rpc RejectDraftChunk(RejectDraftChunkRequest)
|
|
returns (RejectDraftChunkResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "config:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Approve all pending draft chunks (skips security-flagged unless forced).
|
|
rpc ApproveAllDraftChunks(ApproveAllDraftChunksRequest)
|
|
returns (ApproveAllDraftChunksResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "config:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Edit a pending draft chunk in-place (e.g. narrow allowed_ips).
|
|
rpc EditDraftChunk(EditDraftChunkRequest) returns (EditDraftChunkResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "config:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Reverse an approval (remove merged rule from active policy).
|
|
rpc UndoDraftChunk(UndoDraftChunkRequest) returns (UndoDraftChunkResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "config:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Clear all pending draft chunks for a sandbox.
|
|
rpc ClearDraftChunks(ClearDraftChunksRequest)
|
|
returns (ClearDraftChunksResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "config:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Get decision history for a sandbox's draft policy.
|
|
rpc GetDraftHistory(GetDraftHistoryRequest) returns (GetDraftHistoryResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "config:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Exchange a sandbox-bootstrap credential (e.g. a Kubernetes projected
|
|
// ServiceAccount token) for a gateway-minted JWT bound to the calling
|
|
// sandbox's UUID. Used by the Kubernetes driver path; singleplayer
|
|
// drivers receive the gateway JWT directly from the create-sandbox flow
|
|
// and never call this RPC.
|
|
rpc IssueSandboxToken(IssueSandboxTokenRequest) returns (IssueSandboxTokenResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "sandbox"
|
|
};
|
|
}
|
|
|
|
// Renew the calling sandbox's gateway JWT. Older tokens remain valid
|
|
// until their own expiry; deployments should keep token TTLs short to
|
|
// bound replay exposure. The supervisor calls this from a background
|
|
// task at ~80% of the token's lifetime; the new token is cached in
|
|
// memory only — the on-disk bootstrap file is intentionally not
|
|
// rewritten.
|
|
rpc RefreshSandboxToken(RefreshSandboxTokenRequest)
|
|
returns (RefreshSandboxTokenResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "sandbox"
|
|
};
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Workspace management RPCs
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Create a workspace.
|
|
rpc CreateWorkspace(CreateWorkspaceRequest) returns (CreateWorkspaceResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "workspace:write"
|
|
global_role: "platform_admin"
|
|
};
|
|
}
|
|
|
|
// Fetch a workspace by name.
|
|
rpc GetWorkspace(GetWorkspaceRequest) returns (GetWorkspaceResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "workspace:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// List workspaces.
|
|
rpc ListWorkspaces(ListWorkspacesRequest) returns (ListWorkspacesResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "workspace:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
|
|
// Delete a workspace by name.
|
|
rpc DeleteWorkspace(DeleteWorkspaceRequest) returns (DeleteWorkspaceResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "workspace:write"
|
|
global_role: "platform_admin"
|
|
};
|
|
}
|
|
|
|
// Add a member to a workspace.
|
|
rpc AddWorkspaceMember(AddWorkspaceMemberRequest) returns (AddWorkspaceMemberResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "workspace:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// Remove a member from a workspace.
|
|
rpc RemoveWorkspaceMember(RemoveWorkspaceMemberRequest) returns (RemoveWorkspaceMemberResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "workspace:write"
|
|
workspace_role: "admin"
|
|
};
|
|
}
|
|
|
|
// List members of a workspace.
|
|
rpc ListWorkspaceMembers(ListWorkspaceMembersRequest) returns (ListWorkspaceMembersResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "workspace:read"
|
|
workspace_role: "user"
|
|
};
|
|
}
|
|
}
|
|
|
|
// 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).
|
|
message IssueSandboxTokenRequest {}
|
|
|
|
// IssueSandboxToken response. The supervisor caches the returned token in
|
|
// memory and presents it as `Authorization: Bearer` on every subsequent
|
|
// gateway RPC.
|
|
message IssueSandboxTokenResponse {
|
|
// Gateway-minted JWT bound to the calling sandbox's UUID.
|
|
string token = 1 [(openshell.options.v1.secret) = true];
|
|
// Absolute expiry of the issued token, milliseconds since the epoch. 0 means
|
|
// the token is non-expiring.
|
|
int64 expires_at_ms = 2;
|
|
}
|
|
|
|
// RefreshSandboxToken request. The calling principal must already be a
|
|
// sandbox principal (i.e. the request carries a still-valid gateway-minted
|
|
// JWT in its Authorization header). Extension service names are resolved
|
|
// against server-owned registrations and the sandbox's effective policy;
|
|
// callers never choose token audiences directly.
|
|
message RefreshSandboxTokenRequest {
|
|
// Operator registration names for extension services selected by the
|
|
// sandbox's effective policy.
|
|
repeated string extension_service_names = 1;
|
|
}
|
|
|
|
// RefreshSandboxToken response. The new token replaces the supervisor's
|
|
// in-memory bearer credential.
|
|
message RefreshSandboxTokenResponse {
|
|
// Fresh gateway-minted JWT bound to the same sandbox UUID.
|
|
string token = 1 [(openshell.options.v1.secret) = true];
|
|
// Absolute expiry of the new token, milliseconds since the epoch. 0 means
|
|
// the token is non-expiring.
|
|
int64 expires_at_ms = 2;
|
|
// Fresh credentials for the requested, policy-authorized extension
|
|
// services. These remain in supervisor memory and are never persisted.
|
|
repeated ExtensionServiceCredential extension_credentials = 3;
|
|
}
|
|
|
|
|
|
// Health check request.
|
|
message HealthRequest {}
|
|
|
|
// Health check response.
|
|
message HealthResponse {
|
|
// Service status.
|
|
ServiceStatus status = 1;
|
|
|
|
// Service version.
|
|
string version = 2;
|
|
}
|
|
|
|
// Current-user request. The identity comes from the authenticated request.
|
|
message GetCurrentUserRequest {}
|
|
|
|
// Authenticated user identity as validated by the gateway.
|
|
message GetCurrentUserResponse {
|
|
// Stable identity subject (for example, the OIDC `sub` claim).
|
|
string subject = 1;
|
|
|
|
// Human-readable identity name when supplied by the authentication provider.
|
|
string display_name = 2;
|
|
|
|
// Roles granted to the authenticated identity.
|
|
repeated string roles = 3;
|
|
|
|
// OAuth2 scopes granted to the authenticated identity.
|
|
repeated string scopes = 4;
|
|
|
|
// Authentication provider that established the identity.
|
|
string identity_provider = 5;
|
|
}
|
|
|
|
// Gateway info request.
|
|
message GetGatewayInfoRequest {}
|
|
|
|
// Gateway info response.
|
|
message GetGatewayInfoResponse {
|
|
// Service status.
|
|
ServiceStatus status = 1;
|
|
|
|
// OpenShell gateway binary version.
|
|
string gateway_version = 2;
|
|
|
|
// Compute driver runtimes initialized by this gateway. Current gateways
|
|
// return exactly one entry.
|
|
repeated ComputeDriverInfo compute_drivers = 3;
|
|
}
|
|
|
|
// Info for one initialized compute driver runtime.
|
|
message ComputeDriverInfo {
|
|
// Gateway-selected driver name used for routing and driver_config keys.
|
|
string name = 1;
|
|
|
|
// Capabilities reported by the driver during gateway runtime initialization.
|
|
ComputeDriverCapabilities capabilities = 2;
|
|
}
|
|
|
|
// Public compute driver capability snapshot.
|
|
message ComputeDriverCapabilities {
|
|
// Driver-reported human-readable name from the startup capability snapshot.
|
|
string driver_name = 1;
|
|
|
|
// Driver-reported implementation version from the startup capability snapshot.
|
|
string driver_version = 2;
|
|
|
|
// Static portable resource request forms reported by the driver.
|
|
ResourceCapabilities resource_capabilities = 3;
|
|
|
|
// Whether the configured driver instance completely enforces the portable
|
|
// SandboxPolicy.ui contract.
|
|
bool supports_ui_policy = 4;
|
|
}
|
|
|
|
// Static portable resource request forms reported by a compute driver.
|
|
// An omitted domain means the driver does not report that domain.
|
|
message ResourceCapabilities {
|
|
CpuResourceCapabilities cpu = 1;
|
|
MemoryResourceCapabilities memory = 2;
|
|
GpuResourceCapabilities gpu = 3;
|
|
}
|
|
|
|
message CpuResourceCapabilities {
|
|
// The driver accepts and enforces a portable CPU limit.
|
|
bool limit_supported = 1;
|
|
}
|
|
|
|
message MemoryResourceCapabilities {
|
|
// The driver accepts and enforces a portable memory limit.
|
|
bool limit_supported = 1;
|
|
}
|
|
|
|
message GpuResourceCapabilities {
|
|
// The driver accepts a GPU request with no explicit count.
|
|
bool default_selection_supported = 1;
|
|
// The driver accepts an explicit `gpu.count` request.
|
|
bool count_selection_supported = 2;
|
|
}
|
|
|
|
// Public sandbox resource exposed by the OpenShell API.
|
|
//
|
|
// This is the canonical gateway-owned view of a sandbox. It merges user intent
|
|
// (`spec`) with gateway-managed metadata and status derived from internal
|
|
// compute-driver observations.
|
|
//
|
|
// Note: The `namespace` field has been removed from the public API. It remains
|
|
// in the internal `DriverSandbox` message as a compute-driver implementation detail.
|
|
message Sandbox {
|
|
// Kubernetes-style metadata (id, name, labels, timestamps, resource version).
|
|
openshell.datamodel.v1.ObjectMeta metadata = 1;
|
|
// Desired sandbox configuration submitted through the API.
|
|
SandboxSpec spec = 2;
|
|
// Latest user-facing observed status derived by the gateway.
|
|
SandboxStatus status = 3;
|
|
// Read-only provenance for sandboxes created from a reusable workload template.
|
|
SandboxWorkloadTemplateProvenance created_from_workload_template = 20;
|
|
|
|
reserved 4, 5;
|
|
reserved "phase", "current_policy_version";
|
|
}
|
|
|
|
// Desired sandbox configuration provided through the public API.
|
|
message SandboxSpec {
|
|
// Log level exposed to processes running inside the sandbox.
|
|
string log_level = 1;
|
|
// Environment variables injected into the sandbox runtime.
|
|
map<string, string> environment = 5;
|
|
// Container or VM template used to provision the sandbox.
|
|
SandboxTemplate template = 6;
|
|
// Required sandbox policy configuration.
|
|
openshell.sandbox.v1.SandboxPolicy policy = 7;
|
|
// Provider names to attach to this sandbox.
|
|
repeated string providers = 8;
|
|
// Portable resource requirements used by the gateway for driver selection
|
|
// and by drivers for provisioning.
|
|
ResourceRequirements resource_requirements = 9;
|
|
reserved 10;
|
|
reserved "gpu_device";
|
|
// Field 11 was `proposal_approval_mode`. The approval mode is now a
|
|
// runtime setting (gateway or sandbox scope) read via UpdateConfig /
|
|
// GetSandboxConfig, so it can be flipped on a running sandbox and
|
|
// managed fleet-wide.
|
|
reserved 11;
|
|
reserved "proposal_approval_mode";
|
|
// Canonical command launched once by the sandbox supervisor. No shell
|
|
// parsing is performed. The gateway normalizes an omitted command to the
|
|
// portable scratch login shell before persistence.
|
|
repeated string command = 12;
|
|
// Allocate a retained pseudo-terminal for the main process.
|
|
bool tty = 13;
|
|
}
|
|
|
|
message ResourceRequirements {
|
|
// GPU requirements for the sandbox. Presence indicates a GPU request.
|
|
GpuResourceRequirements gpu = 1;
|
|
}
|
|
|
|
// Public GPU resource requirements.
|
|
message GpuResourceRequirements {
|
|
// Optional number of GPUs requested. When omitted, the request is for one
|
|
// GPU using the selected driver's default assignment behavior.
|
|
optional uint32 count = 1;
|
|
}
|
|
|
|
// Historical inline compute template mapped onto compute-driver template inputs.
|
|
//
|
|
// Despite its name, this is not a reusable named sandbox template resource. It
|
|
// is an inline part of `SandboxSpec` kept for v1 compatibility. A future
|
|
// breaking API cleanup may rename this message to free `SandboxTemplate` for
|
|
// the reusable template resource now represented by `SandboxWorkloadTemplate`.
|
|
message SandboxTemplate {
|
|
// Fully-qualified OCI image reference used to boot the sandbox.
|
|
string image = 1;
|
|
// Optional runtime class name requested from the compute platform.
|
|
string runtime_class_name = 2;
|
|
// Optional agent socket path exposed to the workload.
|
|
string agent_socket = 3;
|
|
// Labels applied to compute-platform resources for this sandbox.
|
|
map<string, string> labels = 4;
|
|
// Annotations applied to compute-platform resources for this sandbox.
|
|
map<string, string> annotations = 5;
|
|
// Additional environment variables injected by the template.
|
|
map<string, string> environment = 6;
|
|
// Platform-specific compute resource requirements and limits. Well-known
|
|
// cpu and memory entries under limits or requests must be non-empty strings.
|
|
google.protobuf.Struct resources = 7;
|
|
reserved 9;
|
|
reserved "volume_claim_templates";
|
|
// Enable Kubernetes user namespace isolation (hostUsers: false).
|
|
// When true, container UID 0 maps to a non-root host UID and capabilities
|
|
// become namespaced. Requires Kubernetes 1.33+ with user namespace support
|
|
// available (beta through 1.35, GA in 1.36+) and a supporting runtime.
|
|
// When unset, the cluster-wide default is used.
|
|
optional bool user_namespaces = 10;
|
|
// Driver-keyed opaque config envelope supplied by the caller.
|
|
// The gateway selects the block matching the active compute driver and
|
|
// forwards only that inner Struct to DriverSandboxTemplate.driver_config.
|
|
// The selected driver owns nested schema validation.
|
|
google.protobuf.Struct driver_config = 11;
|
|
}
|
|
|
|
// Reusable named sandbox workload template resource.
|
|
//
|
|
// This is the actual workspace-scoped template resource used to create
|
|
// sandboxes by reference. It uses the longer name in v1 to avoid colliding with
|
|
// the historical inline `SandboxTemplate` message. A future breaking API
|
|
// cleanup may rename this resource to `SandboxTemplate`.
|
|
message SandboxWorkloadTemplate {
|
|
// Kubernetes-style metadata (id, name, labels, timestamps, resource version).
|
|
openshell.datamodel.v1.ObjectMeta metadata = 1;
|
|
// Desired reusable workload shape and template-owned driver config.
|
|
SandboxWorkloadTemplateSpec spec = 2;
|
|
}
|
|
|
|
message SandboxWorkloadTemplateSpec {
|
|
// Portable workload shape.
|
|
SandboxWorkloadConfig workload = 1;
|
|
// Driver-keyed opaque config envelope supplied by the template owner.
|
|
google.protobuf.Struct driver_config = 2;
|
|
// Desired service level associated with this template.
|
|
SandboxServiceLevel desired_service_level = 3;
|
|
}
|
|
|
|
message SandboxWorkloadConfig {
|
|
// Fully-qualified OCI image reference used to boot the sandbox.
|
|
string image = 1;
|
|
// Environment variables injected into the sandbox runtime.
|
|
map<string, string> environment = 2;
|
|
// Portable resource requirements for sandboxes created from this workload.
|
|
SandboxResources resources = 3;
|
|
}
|
|
|
|
message SandboxResources {
|
|
// Portable CPU quantity, for example "500m" or "2".
|
|
string cpu = 1;
|
|
// Portable memory quantity, for example "512Mi" or "2Gi".
|
|
string memory = 2;
|
|
// GPU requirements for the sandbox workload. Presence indicates a GPU
|
|
// request. When count is omitted, the request uses the selected driver's
|
|
// default GPU assignment behavior.
|
|
GpuResourceRequirements gpu = 3;
|
|
}
|
|
|
|
message SandboxServiceLevel {
|
|
SandboxStartup startup = 1;
|
|
}
|
|
|
|
message SandboxStartup {
|
|
google.protobuf.Duration ready_within = 1;
|
|
uint32 max_burst = 2;
|
|
}
|
|
|
|
message SandboxWorkloadTemplateProvenance {
|
|
string name = 1;
|
|
string resource_version = 2;
|
|
}
|
|
|
|
// User-facing sandbox status derived by the gateway from compute-driver observations.
|
|
//
|
|
// Public status does not embed driver-only flags such as `deleting`.
|
|
message SandboxStatus {
|
|
// Compute-platform sandbox object name.
|
|
string sandbox_name = 1;
|
|
// Name of the agent pod or equivalent runtime instance.
|
|
string agent_pod = 2;
|
|
// File descriptor or endpoint for reaching the agent service, when available.
|
|
string agent_fd = 3;
|
|
// File descriptor or endpoint for reaching the sandbox service, when available.
|
|
string sandbox_fd = 4;
|
|
// Latest user-facing readiness and lifecycle conditions.
|
|
repeated SandboxCondition conditions = 5;
|
|
// Gateway-derived lifecycle summary.
|
|
SandboxPhase phase = 6;
|
|
// Currently active policy version (updated when sandbox reports loaded).
|
|
uint32 current_policy_version = 7;
|
|
// Supervisor instance currently associated with the canonical main process.
|
|
// The gateway uses this to reject stale exit reports after a restart.
|
|
string main_process_instance_id = 8;
|
|
// Normalized main process result. Signal exits use 128 + signal number.
|
|
// Presence indicates that the canonical main process exited. Exit code 0
|
|
// produces Completed; nonzero and signal-normalized exits produce Error.
|
|
optional int32 exit_code = 9;
|
|
// Last accepted network result for each configured tool server endpoint.
|
|
// Currently populated for MCP-over-HTTP endpoints. These passive results
|
|
// remain separate from sandbox lifecycle conditions and readiness.
|
|
repeated EndpointStatus endpoint_statuses = 10;
|
|
}
|
|
|
|
// User-facing sandbox condition derived from platform or gateway observations.
|
|
message SandboxCondition {
|
|
// Condition class, typically mirroring the underlying platform condition type.
|
|
string type = 1;
|
|
// Condition status value such as `True`, `False`, or `Unknown`.
|
|
string status = 2;
|
|
// Short machine-readable reason associated with the condition.
|
|
string reason = 3;
|
|
// Human-readable condition message.
|
|
string message = 4;
|
|
// RFC 3339 UTC timestamp supplied by the condition owner for the last transition.
|
|
string last_transition_time = 5;
|
|
}
|
|
|
|
// High-level sandbox lifecycle phase derived by the gateway.
|
|
//
|
|
// Clients should rely on this normalized lifecycle summary for readiness and
|
|
// deletion decisions instead of interpreting raw conditions.
|
|
enum SandboxPhase {
|
|
SANDBOX_PHASE_UNSPECIFIED = 0;
|
|
SANDBOX_PHASE_PROVISIONING = 1;
|
|
SANDBOX_PHASE_READY = 2;
|
|
SANDBOX_PHASE_ERROR = 3;
|
|
SANDBOX_PHASE_DELETING = 4;
|
|
SANDBOX_PHASE_UNKNOWN = 5;
|
|
SANDBOX_PHASE_STOPPING = 6;
|
|
SANDBOX_PHASE_STOPPED = 7;
|
|
SANDBOX_PHASE_STARTING = 8;
|
|
// The canonical main process exited successfully and its result is final.
|
|
SANDBOX_PHASE_COMPLETED = 9;
|
|
}
|
|
|
|
// Public platform event exposed on the sandbox watch stream.
|
|
message PlatformEvent {
|
|
// Event timestamp in milliseconds since epoch.
|
|
int64 timestamp_ms = 1;
|
|
// Event source (e.g. "kubernetes", "docker", "process").
|
|
string source = 2;
|
|
// Event type/severity (e.g. "Normal", "Warning").
|
|
string type = 3;
|
|
// Short reason code (e.g. "Started", "Pulled", "Failed").
|
|
string reason = 4;
|
|
// Human-readable event message.
|
|
string message = 5;
|
|
// Optional metadata as key-value pairs.
|
|
map<string, string> metadata = 6;
|
|
}
|
|
|
|
// Create sandbox request.
|
|
message CreateSandboxRequest {
|
|
reserved 5;
|
|
reserved "workspace";
|
|
SandboxSpec spec = 1;
|
|
// Optional user-supplied sandbox name. When empty the server generates one.
|
|
string name = 2;
|
|
// Optional labels for the sandbox (key-value metadata).
|
|
map<string, string> labels = 3;
|
|
// Optional annotations for the sandbox (non-selector metadata).
|
|
map<string, string> annotations = 4;
|
|
// One-shot launch hint indicating that the creating client will attach to
|
|
// the canonical main process. The supervisor keeps the terminal transport
|
|
// alive until that attachment connects and closes naturally.
|
|
bool await_main_process_attachment = 6;
|
|
// Workspace-scoped SandboxWorkloadTemplate name to resolve at creation time.
|
|
string workload_template_name = 7;
|
|
// Explicit workspace for the sandbox. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 8;
|
|
}
|
|
|
|
message CreateSandboxTemplateRequest {
|
|
reserved 2;
|
|
reserved "workspace";
|
|
SandboxWorkloadTemplate template = 1;
|
|
// Explicit workspace for the template. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
}
|
|
|
|
message GetSandboxTemplateRequest {
|
|
reserved 2;
|
|
reserved "workspace";
|
|
string name = 1;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
}
|
|
|
|
message ListSandboxTemplatesRequest {
|
|
reserved 3, 4;
|
|
reserved "workspace", "all_workspaces";
|
|
// The maximum number of templates to return. Zero uses 100. Values above
|
|
// 1000 are coerced to 1000; negative values are invalid.
|
|
int32 page_size = 1;
|
|
// Token from a previous ListSandboxTemplates response. All other request
|
|
// parameters except page_size must match the request that produced it.
|
|
string page_token = 2;
|
|
// Optional label selector in key=value comma-separated form.
|
|
string label_selector = 5;
|
|
// Explicit named or all-workspaces scope.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 6;
|
|
}
|
|
|
|
message DeleteSandboxTemplateRequest {
|
|
reserved 2;
|
|
reserved "workspace";
|
|
string name = 1;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
}
|
|
|
|
message SandboxTemplateResponse {
|
|
SandboxWorkloadTemplate template = 1;
|
|
}
|
|
|
|
message ListSandboxTemplatesResponse {
|
|
repeated SandboxWorkloadTemplate templates = 1;
|
|
// Token for the next page. Empty when there are no subsequent pages.
|
|
string next_page_token = 2;
|
|
}
|
|
|
|
message DeleteSandboxTemplateResponse {
|
|
bool deleted = 1;
|
|
}
|
|
|
|
// Request a gateway-owned staging slot for a local rootfs tar archive.
|
|
message BeginRootfsTarStagingRequest {
|
|
reserved 1;
|
|
reserved "workspace";
|
|
// Base file name of the local archive. The gateway uses it only to name the
|
|
// staged file; path separators and traversal components are rejected.
|
|
string file_name = 2;
|
|
// Size of the local archive in bytes, checked against the driver limit
|
|
// before the gateway allocates a slot.
|
|
uint64 size_bytes = 3;
|
|
// Explicit workspace that will own the sandbox created from this archive.
|
|
// The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
}
|
|
|
|
// Gateway-issued staging slot.
|
|
message BeginRootfsTarStagingResponse {
|
|
// Opaque single-use token. Pass it as
|
|
// `template.driver_config.<driver>.rootfs_tar_staging_token` on
|
|
// CreateSandbox. The first CreateSandbox presenting it consumes it.
|
|
string staging_token = 1;
|
|
// Absolute path on the gateway host the client must write the archive to.
|
|
string upload_path = 2;
|
|
// Maximum accepted archive size in bytes, enforced again by the driver.
|
|
uint64 max_bytes = 3;
|
|
// Wall-clock deadline after which the gateway reclaims the slot.
|
|
int64 expires_at_ms = 4;
|
|
}
|
|
|
|
// Get sandbox request.
|
|
message GetSandboxRequest {
|
|
reserved 2;
|
|
reserved "workspace";
|
|
// Sandbox name (canonical lookup key).
|
|
string name = 1;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
}
|
|
|
|
// List sandboxes request.
|
|
message ListSandboxesRequest {
|
|
reserved 4, 5;
|
|
reserved "workspace", "all_workspaces";
|
|
// The maximum number of sandboxes to return. Zero uses 100. Values above
|
|
// 1000 are coerced to 1000; negative values are invalid.
|
|
int32 page_size = 1;
|
|
// Token from a previous ListSandboxes response. All other request parameters
|
|
// except page_size must match the request that produced it.
|
|
string page_token = 2;
|
|
// Optional label selector for filtering (format: "key1=value1,key2=value2").
|
|
string label_selector = 3;
|
|
// Explicit named or all-workspaces scope.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 6;
|
|
}
|
|
|
|
// List providers attached to a sandbox request.
|
|
message ListSandboxProvidersRequest {
|
|
reserved 2;
|
|
reserved "workspace";
|
|
// Sandbox name (canonical lookup key).
|
|
string sandbox_name = 1;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
}
|
|
|
|
// Attach provider to sandbox request.
|
|
message AttachSandboxProviderRequest {
|
|
reserved 4;
|
|
reserved "workspace";
|
|
// Sandbox name (canonical lookup key).
|
|
string sandbox_name = 1;
|
|
// Provider name to attach.
|
|
string provider_name = 2;
|
|
// Expected resource version for optimistic concurrency control.
|
|
// If 0, the server uses the current version (backward compatibility).
|
|
// If non-zero, the server validates that the sandbox's current resource_version
|
|
// matches this value before applying the mutation, returning ABORTED on mismatch.
|
|
uint64 expected_resource_version = 3;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 5;
|
|
}
|
|
|
|
// Detach provider from sandbox request.
|
|
message DetachSandboxProviderRequest {
|
|
reserved 4;
|
|
reserved "workspace";
|
|
// Sandbox name (canonical lookup key).
|
|
string sandbox_name = 1;
|
|
// Provider name to detach.
|
|
string provider_name = 2;
|
|
// Expected resource version for optimistic concurrency control.
|
|
// If 0, the server uses the current version (backward compatibility).
|
|
// If non-zero, the server validates that the sandbox's current resource_version
|
|
// matches this value before applying the mutation, returning ABORTED on mismatch.
|
|
uint64 expected_resource_version = 3;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 5;
|
|
}
|
|
|
|
// Delete sandbox request.
|
|
message DeleteSandboxRequest {
|
|
reserved 2;
|
|
reserved "workspace";
|
|
// Sandbox name (canonical lookup key).
|
|
string name = 1;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
// Optional immutable sandbox identity precondition. When non-empty, the
|
|
// gateway rejects the request with ABORTED unless the currently resolved
|
|
// sandbox has this exact metadata ID. The check is repeated under the
|
|
// lifecycle lock immediately before any delete mutation.
|
|
string expected_sandbox_id = 4;
|
|
// Optional optimistic-concurrency precondition. Requires
|
|
// expected_sandbox_id. When non-zero, the gateway rejects the request with
|
|
// ABORTED unless the sandbox's current resource version matches this value
|
|
// immediately before any delete mutation.
|
|
uint64 expected_resource_version = 5;
|
|
}
|
|
|
|
// Stop sandbox request.
|
|
message StopSandboxRequest {
|
|
reserved 2;
|
|
reserved "workspace";
|
|
// Sandbox name (canonical lookup key).
|
|
string name = 1;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
}
|
|
|
|
// Start sandbox request.
|
|
message StartSandboxRequest {
|
|
reserved 2;
|
|
reserved "workspace";
|
|
// Sandbox name (canonical lookup key).
|
|
string name = 1;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
}
|
|
|
|
// Sandbox response.
|
|
message SandboxResponse {
|
|
Sandbox sandbox = 1;
|
|
}
|
|
|
|
// List sandboxes response.
|
|
message ListSandboxesResponse {
|
|
repeated Sandbox sandboxes = 1;
|
|
// Token for the next page. Empty when there are no subsequent pages.
|
|
string next_page_token = 2;
|
|
}
|
|
|
|
// List providers attached to a sandbox response.
|
|
message ListSandboxProvidersResponse {
|
|
repeated openshell.datamodel.v1.Provider providers = 1;
|
|
}
|
|
|
|
// Attach provider to sandbox response.
|
|
message AttachSandboxProviderResponse {
|
|
Sandbox sandbox = 1;
|
|
// True when the provider was newly attached. False means it was already attached.
|
|
bool attached = 2;
|
|
}
|
|
|
|
// Detach provider from sandbox response.
|
|
message DetachSandboxProviderResponse {
|
|
Sandbox sandbox = 1;
|
|
// True when the provider was removed. False means it was not attached.
|
|
bool detached = 2;
|
|
}
|
|
|
|
// Delete sandbox response.
|
|
message DeleteSandboxResponse {
|
|
bool deleted = 1;
|
|
}
|
|
|
|
// Create SSH session request.
|
|
message CreateSshSessionRequest {
|
|
// Sandbox id.
|
|
string sandbox_id = 1;
|
|
}
|
|
|
|
// Create SSH session response.
|
|
//
|
|
// Fields are interpolated into an SSH `ProxyCommand` string that OpenSSH
|
|
// executes through `/bin/sh -c` on the caller's workstation. Servers MUST
|
|
// uphold the charset contract below; clients MUST reject responses that
|
|
// violate it. The client's own escaping provides defense-in-depth, but
|
|
// narrow charsets close injection vectors at the trust boundary.
|
|
message CreateSshSessionResponse {
|
|
// Sandbox id. [A-Za-z0-9._-]{1,128}.
|
|
string sandbox_id = 1;
|
|
|
|
// Session token for the gateway tunnel. URL-safe ASCII
|
|
// ([A-Za-z0-9._~+/=-]) up to 4096 bytes. No shell metacharacters or
|
|
// whitespace.
|
|
string token = 2 [(openshell.options.v1.secret) = true];
|
|
|
|
// Gateway host for SSH proxy connection. IPv4 address, bracketed IPv6
|
|
// address, or DNS hostname (Punycode-encoded for IDN). Alphanumeric plus
|
|
// `.-:[]` only, up to 253 bytes.
|
|
string gateway_host = 3;
|
|
|
|
// Gateway port for SSH proxy connection. Must be in range 1..=65535.
|
|
uint32 gateway_port = 4;
|
|
|
|
// Gateway scheme. Must be exactly "http" or "https".
|
|
string gateway_scheme = 5;
|
|
|
|
// Optional host key fingerprint. If non-empty, [A-Za-z0-9:+/=-] only.
|
|
string host_key_fingerprint = 7;
|
|
|
|
// Expiry timestamp in milliseconds since epoch. 0 means no expiry.
|
|
int64 expires_at_ms = 8;
|
|
}
|
|
|
|
// Request to expose an HTTP service running inside a sandbox.
|
|
message ExposeServiceRequest {
|
|
reserved 5;
|
|
reserved "workspace";
|
|
// Sandbox name.
|
|
string sandbox = 1;
|
|
// Service name within the sandbox.
|
|
string service = 2;
|
|
// Loopback TCP port inside the sandbox.
|
|
uint32 target_port = 3;
|
|
// Whether to print/use the browser-facing service URL.
|
|
bool domain = 4;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 6;
|
|
}
|
|
|
|
// Request to fetch an exposed sandbox service endpoint.
|
|
message GetServiceRequest {
|
|
reserved 3;
|
|
reserved "workspace";
|
|
// Sandbox name.
|
|
string sandbox = 1;
|
|
// Service name within the sandbox. Empty selects the unnamed endpoint.
|
|
string service = 2;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
}
|
|
|
|
// Request to list exposed sandbox service endpoints.
|
|
message ListServicesRequest {
|
|
reserved 4, 5;
|
|
reserved "workspace", "all_workspaces";
|
|
// Optional sandbox name. Empty lists endpoints for all sandboxes.
|
|
string sandbox = 1;
|
|
// The maximum number of services to return. Zero uses 100. Values above
|
|
// 1000 are coerced to 1000; negative values are invalid.
|
|
int32 page_size = 2;
|
|
// Token from a previous ListServices response. All other request parameters
|
|
// except page_size must match the request that produced it.
|
|
string page_token = 3;
|
|
// Explicit named or all-workspaces scope.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 6;
|
|
}
|
|
|
|
// Response containing exposed sandbox service endpoints.
|
|
message ListServicesResponse {
|
|
repeated ServiceEndpointResponse services = 1;
|
|
// Token for the next page. Empty when there are no subsequent pages.
|
|
string next_page_token = 2;
|
|
}
|
|
|
|
// Request to delete an exposed sandbox service endpoint.
|
|
message DeleteServiceRequest {
|
|
reserved 3;
|
|
reserved "workspace";
|
|
// Sandbox name.
|
|
string sandbox = 1;
|
|
// Service name within the sandbox. Empty selects the unnamed endpoint.
|
|
string service = 2;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
}
|
|
|
|
// Response for deleting an exposed sandbox service endpoint.
|
|
message DeleteServiceResponse {
|
|
// True when an endpoint existed and was deleted.
|
|
bool deleted = 1;
|
|
}
|
|
|
|
// Persisted sandbox service endpoint.
|
|
message ServiceEndpoint {
|
|
// Kubernetes-style metadata.
|
|
openshell.datamodel.v1.ObjectMeta metadata = 1;
|
|
// Sandbox object ID.
|
|
string sandbox_id = 2;
|
|
// Sandbox name.
|
|
string sandbox_name = 3;
|
|
// Service name within the sandbox.
|
|
string service_name = 4;
|
|
// Loopback TCP port inside the sandbox.
|
|
uint32 target_port = 5;
|
|
// Whether browser-facing service routing is enabled for this endpoint.
|
|
bool domain = 6;
|
|
}
|
|
|
|
// Response containing a service endpoint and, when available, its local URL.
|
|
message ServiceEndpointResponse {
|
|
ServiceEndpoint endpoint = 1;
|
|
string url = 2;
|
|
}
|
|
|
|
// Revoke SSH session request.
|
|
message RevokeSshSessionRequest {
|
|
// Session token to revoke.
|
|
string token = 1 [(openshell.options.v1.secret) = true];
|
|
}
|
|
|
|
// Revoke SSH session response.
|
|
message RevokeSshSessionResponse {
|
|
// True when a session was revoked.
|
|
bool revoked = 1;
|
|
}
|
|
|
|
// Execute command request.
|
|
message ExecSandboxRequest {
|
|
// Sandbox id.
|
|
string sandbox_id = 1;
|
|
|
|
// Command and arguments.
|
|
repeated string command = 2;
|
|
|
|
// Optional working directory.
|
|
string workdir = 3;
|
|
|
|
// Optional environment overrides.
|
|
map<string, string> environment = 4;
|
|
|
|
// Optional timeout in seconds. 0 means no timeout.
|
|
uint32 timeout_seconds = 5;
|
|
|
|
// Optional stdin payload passed to the command.
|
|
bytes stdin = 6;
|
|
|
|
// Request a pseudo-terminal for the remote command.
|
|
bool tty = 7;
|
|
|
|
// Initial terminal columns (used when tty=true, 0 = use default).
|
|
uint32 cols = 8;
|
|
|
|
// Initial terminal rows (used when tty=true, 0 = use default).
|
|
uint32 rows = 9;
|
|
|
|
// Skip sourcing shell login/profile startup files before running the command.
|
|
// When false (the default), the command runs through a login shell
|
|
// (`bash -lc`) so user startup files (.bash_profile/.profile, and .bashrc if
|
|
// sourced by them) are applied. When true, the command runs without those
|
|
// files (`bash -c`), for automation that needs predictable startup behavior.
|
|
bool no_login_shell = 10;
|
|
}
|
|
|
|
// One stdout chunk from a sandbox exec.
|
|
message ExecSandboxStdout {
|
|
bytes data = 1;
|
|
}
|
|
|
|
// One stderr chunk from a sandbox exec.
|
|
message ExecSandboxStderr {
|
|
bytes data = 1;
|
|
}
|
|
|
|
// Final exit status for a sandbox exec.
|
|
message ExecSandboxExit {
|
|
int32 exit_code = 1;
|
|
}
|
|
|
|
// One event in a sandbox exec stream.
|
|
message ExecSandboxEvent {
|
|
oneof payload {
|
|
ExecSandboxStdout stdout = 1;
|
|
ExecSandboxStderr stderr = 2;
|
|
ExecSandboxExit exit = 3;
|
|
}
|
|
}
|
|
|
|
// Initial frame for one TCP forward stream.
|
|
message TcpForwardInit {
|
|
// Sandbox id.
|
|
string sandbox_id = 1;
|
|
// Optional service identifier for audit/correlation.
|
|
string service_id = 4;
|
|
// Target the gateway should request from the supervisor.
|
|
oneof target {
|
|
SshRelayTarget ssh = 5;
|
|
TcpRelayTarget tcp = 6;
|
|
}
|
|
// Optional target-specific authorization token. SSH targets use this as the
|
|
// short-lived SSH session token issued by CreateSshSession.
|
|
string authorization_token = 7 [(openshell.options.v1.secret) = true];
|
|
}
|
|
|
|
// A single frame on the CLI-to-gateway TCP forward stream.
|
|
message TcpForwardFrame {
|
|
oneof payload {
|
|
TcpForwardInit init = 1;
|
|
bytes data = 2;
|
|
}
|
|
}
|
|
|
|
// Client-to-server message for interactive exec.
|
|
message ExecSandboxInput {
|
|
oneof payload {
|
|
// First message: exec request metadata.
|
|
ExecSandboxRequest start = 1;
|
|
// Subsequent messages: raw stdin bytes.
|
|
bytes stdin = 2;
|
|
// Terminal window size change.
|
|
ExecSandboxWindowResize resize = 3;
|
|
}
|
|
}
|
|
|
|
// Terminal window resize event for interactive exec.
|
|
message ExecSandboxWindowResize {
|
|
uint32 cols = 1;
|
|
uint32 rows = 2;
|
|
}
|
|
|
|
|
|
// SSH session record stored in persistence.
|
|
message SshSession {
|
|
// Kubernetes-style metadata (id, name, labels, timestamps, resource version).
|
|
openshell.datamodel.v1.ObjectMeta metadata = 1;
|
|
|
|
// Sandbox id.
|
|
string sandbox_id = 2;
|
|
|
|
// Session token.
|
|
string token = 3 [(openshell.options.v1.secret) = true];
|
|
|
|
// Expiry timestamp in milliseconds since epoch. 0 means no expiry
|
|
// (backward-compatible default for sessions created before this field existed).
|
|
int64 expires_at_ms = 4;
|
|
|
|
// Revoked flag.
|
|
bool revoked = 5;
|
|
}
|
|
|
|
// Watch sandbox request.
|
|
message WatchSandboxRequest {
|
|
// Sandbox id.
|
|
string id = 1;
|
|
|
|
// Stream sandbox status snapshots.
|
|
bool follow_status = 2;
|
|
|
|
// Stream openshell-server process logs correlated to this sandbox.
|
|
bool follow_logs = 3;
|
|
|
|
// Stream platform events correlated to this sandbox.
|
|
bool follow_events = 4;
|
|
|
|
// Replay the last N log lines (best-effort) before following.
|
|
uint32 log_tail_lines = 5;
|
|
|
|
// Replay the last N platform events (best-effort) before following.
|
|
uint32 event_tail = 6;
|
|
|
|
// Stop streaming once the sandbox reaches READY or a terminal result phase
|
|
// (COMPLETED, STOPPED, or ERROR).
|
|
bool stop_on_terminal = 7;
|
|
|
|
// Only include log lines with timestamp >= this value (milliseconds since epoch).
|
|
// 0 means no time filter. Applies to both tail replay and live streaming.
|
|
int64 log_since_ms = 8;
|
|
|
|
// Filter by log source (e.g. "gateway", "sandbox"). Empty means all sources.
|
|
repeated string log_sources = 9;
|
|
|
|
// Minimum log level to include (e.g. "INFO", "WARN", "ERROR"). Empty means all levels.
|
|
string log_min_level = 10;
|
|
}
|
|
|
|
// One event in a sandbox watch stream.
|
|
message SandboxStreamEvent {
|
|
oneof payload {
|
|
// Latest sandbox snapshot.
|
|
Sandbox sandbox = 1;
|
|
// One server log line/event.
|
|
SandboxLogLine log = 2;
|
|
// One platform event.
|
|
PlatformEvent event = 3;
|
|
// Warning from the server (e.g. missed messages due to lag).
|
|
SandboxStreamWarning warning = 4;
|
|
// Draft policy update notification.
|
|
DraftPolicyUpdate draft_policy_update = 5;
|
|
}
|
|
}
|
|
|
|
// Log line correlated to a sandbox.
|
|
message SandboxLogLine {
|
|
string sandbox_id = 1;
|
|
int64 timestamp_ms = 2;
|
|
string level = 3;
|
|
string target = 4;
|
|
string message = 5;
|
|
// Log source: "gateway" (server-side) or "sandbox" (supervisor).
|
|
// Empty is treated as "gateway" for backward compatibility.
|
|
string source = 6;
|
|
// Structured key-value fields from the tracing event (e.g. dst_host, action).
|
|
map<string, string> fields = 7;
|
|
}
|
|
|
|
message SandboxStreamWarning {
|
|
string message = 1;
|
|
}
|
|
|
|
// Create provider request.
|
|
message CreateProviderRequest {
|
|
reserved 2;
|
|
reserved "workspace";
|
|
openshell.datamodel.v1.Provider provider = 1;
|
|
// Explicit workspace for the provider. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
}
|
|
|
|
// Get provider request.
|
|
message GetProviderRequest {
|
|
reserved 2;
|
|
reserved "workspace";
|
|
string name = 1;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
}
|
|
|
|
// List providers request.
|
|
message ListProvidersRequest {
|
|
reserved 3, 4;
|
|
reserved "workspace", "all_workspaces";
|
|
// The maximum number of providers to return. Zero uses 100. Values above
|
|
// 1000 are coerced to 1000; negative values are invalid.
|
|
int32 page_size = 1;
|
|
// Token from a previous ListProviders response. All other request parameters
|
|
// except page_size must match the request that produced it.
|
|
string page_token = 2;
|
|
// Explicit named or all-workspaces scope.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 5;
|
|
}
|
|
|
|
// Update provider request.
|
|
message UpdateProviderRequest {
|
|
reserved 3;
|
|
reserved "workspace";
|
|
openshell.datamodel.v1.Provider provider = 1;
|
|
// Optional per-credential expiry timestamps to merge into the provider.
|
|
// A zero value removes the expiry for that credential.
|
|
map<string, int64> credential_expires_at_ms = 2;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
}
|
|
|
|
// Delete provider request.
|
|
message DeleteProviderRequest {
|
|
reserved 2;
|
|
reserved "workspace";
|
|
string name = 1;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
}
|
|
|
|
// Provider response.
|
|
message ProviderResponse {
|
|
openshell.datamodel.v1.Provider provider = 1;
|
|
}
|
|
|
|
// List providers response.
|
|
message ListProvidersResponse {
|
|
repeated openshell.datamodel.v1.Provider providers = 1;
|
|
// Token for the next page. Empty when there are no subsequent pages.
|
|
string next_page_token = 2;
|
|
}
|
|
|
|
// List provider type profiles request.
|
|
message ListProviderProfilesRequest {
|
|
// The maximum number of profiles to return. Zero uses 100. Values above
|
|
// 1000 are coerced to 1000; negative values are invalid.
|
|
int32 page_size = 1;
|
|
// Token from a previous ListProviderProfiles response. All other request
|
|
// parameters except page_size must match the request that produced it.
|
|
string page_token = 2;
|
|
// Workspace scope. When set, returns workspace-scoped + built-in profiles.
|
|
// When empty, returns platform-scoped + built-in only.
|
|
string workspace = 3;
|
|
}
|
|
|
|
// Fetch provider type profile request.
|
|
message GetProviderProfileRequest {
|
|
string id = 1;
|
|
// Workspace scope for two-tier profile resolution. When set, checks
|
|
// workspace-scoped profiles first, then platform-scoped, then built-in.
|
|
// When empty, checks platform-scoped then built-in only.
|
|
string workspace = 2;
|
|
}
|
|
|
|
// Provider profile payload with optional source metadata for diagnostics.
|
|
message ProviderProfileImportItem {
|
|
ProviderProfile profile = 1;
|
|
string source = 2;
|
|
}
|
|
|
|
// Provider profile validation diagnostic.
|
|
message ProviderProfileDiagnostic {
|
|
string source = 1;
|
|
string profile_id = 2;
|
|
string field = 3;
|
|
string message = 4;
|
|
string severity = 5;
|
|
}
|
|
|
|
// Endpoint selector for token grant audience overrides.
|
|
message ProviderCredentialTokenGrantAudienceOverride {
|
|
// Optional: endpoint host selector. If omitted, inherits the profile endpoint host.
|
|
string host = 1;
|
|
|
|
// Optional: endpoint port selector. If omitted, matches the expanded profile endpoint port.
|
|
uint32 port = 2;
|
|
|
|
// Optional: endpoint path selector. If omitted, inherits the profile endpoint path.
|
|
string path = 3;
|
|
|
|
// Resource audience to request for matching endpoints.
|
|
string audience = 4;
|
|
|
|
// Optional: OAuth2 scopes to request. If omitted, inherits the token grant scopes.
|
|
repeated string scopes = 5;
|
|
}
|
|
|
|
// Provider credential token grant configuration.
|
|
// When present, the credential is obtained dynamically via OAuth2 grant when needed.
|
|
enum ProviderCredentialTokenGrantType {
|
|
PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_UNSPECIFIED = 0;
|
|
PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_CLIENT_CREDENTIALS = 1;
|
|
PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_TOKEN_EXCHANGE = 2;
|
|
}
|
|
|
|
message ProviderCredentialTokenGrantSubjectToken {
|
|
// Source for the token exchange subject token. Phase one supports
|
|
// "provider_credential".
|
|
string source = 1;
|
|
|
|
// Provider credential key that stores the subject token.
|
|
string credential = 2;
|
|
|
|
// OAuth2 subject_token_type. If omitted, OpenShell uses
|
|
// urn:ietf:params:oauth:token-type:access_token.
|
|
string subject_token_type = 3;
|
|
}
|
|
|
|
message ProviderCredentialTokenGrant {
|
|
// OAuth2 token endpoint URL (e.g., https://keycloak.example.com/realms/my-realm/protocol/openid-connect/token)
|
|
string token_endpoint = 1;
|
|
|
|
// Optional: default resource audience to request from the token service
|
|
string audience = 2;
|
|
|
|
// Optional: audience to request when fetching the JWT-SVID from SPIRE.
|
|
// If omitted, the sandbox derives this from token_endpoint.
|
|
string jwt_svid_audience = 6;
|
|
|
|
// Optional: OAuth2 scopes to request
|
|
repeated string scopes = 3;
|
|
|
|
// Optional: override token cache TTL (seconds)
|
|
// If 0 or omitted, use expires_in from token response
|
|
int64 cache_ttl_seconds = 4;
|
|
|
|
// Optional: endpoint-specific resource audience overrides.
|
|
repeated ProviderCredentialTokenGrantAudienceOverride audience_overrides = 5;
|
|
|
|
// Optional: OAuth2 client_assertion_type value. If omitted, OpenShell uses
|
|
// urn:ietf:params:oauth:client-assertion-type:jwt-bearer.
|
|
string client_assertion_type = 7;
|
|
|
|
// Grant type. If omitted/unspecified, OpenShell treats this as client_credentials
|
|
// for backwards compatibility.
|
|
ProviderCredentialTokenGrantType grant_type = 8;
|
|
|
|
// Subject token metadata for token_exchange grants.
|
|
ProviderCredentialTokenGrantSubjectToken subject_token = 9;
|
|
|
|
// OAuth2 requested_token_type. If omitted for token_exchange, OpenShell uses
|
|
// urn:ietf:params:oauth:token-type:access_token.
|
|
string requested_token_type = 10;
|
|
}
|
|
|
|
// Provider credential declaration.
|
|
message ProviderProfileCredential {
|
|
string name = 1;
|
|
string description = 2;
|
|
repeated string env_vars = 3;
|
|
bool required = 4;
|
|
string auth_style = 5;
|
|
string header_name = 6;
|
|
string query_param = 7;
|
|
ProviderCredentialRefresh refresh = 8;
|
|
string path_template = 9;
|
|
ProviderCredentialTokenGrant token_grant = 10;
|
|
}
|
|
|
|
enum ProviderCredentialRefreshStrategy {
|
|
PROVIDER_CREDENTIAL_REFRESH_STRATEGY_UNSPECIFIED = 0;
|
|
PROVIDER_CREDENTIAL_REFRESH_STRATEGY_STATIC = 1;
|
|
PROVIDER_CREDENTIAL_REFRESH_STRATEGY_EXTERNAL = 2;
|
|
PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_REFRESH_TOKEN = 3;
|
|
PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_CLIENT_CREDENTIALS = 4;
|
|
PROVIDER_CREDENTIAL_REFRESH_STRATEGY_GOOGLE_SERVICE_ACCOUNT_JWT = 5;
|
|
PROVIDER_CREDENTIAL_REFRESH_STRATEGY_AWS_STS_ASSUME_ROLE = 6;
|
|
}
|
|
|
|
message ProviderCredentialRefreshMaterial {
|
|
string name = 1;
|
|
string description = 2;
|
|
bool required = 3;
|
|
bool secret = 4;
|
|
}
|
|
|
|
// Declares that a single refresh operation mints more than one credential.
|
|
// The refresh is attached to a primary credential; each additional output
|
|
// maps a strategy-defined semantic output id to a sibling credential whose
|
|
// env_vars receive the minted value.
|
|
message ProviderCredentialRefreshOutput {
|
|
string output = 1; // strategy-defined semantic output id (e.g. "session_token")
|
|
string credential = 2; // sibling credential name whose env_vars receive this output
|
|
}
|
|
|
|
message ProviderCredentialRefresh {
|
|
ProviderCredentialRefreshStrategy strategy = 1;
|
|
string token_url = 2;
|
|
repeated string scopes = 3;
|
|
int64 refresh_before_seconds = 4;
|
|
int64 max_lifetime_seconds = 5;
|
|
repeated ProviderCredentialRefreshMaterial material = 6;
|
|
repeated ProviderCredentialRefreshOutput additional_outputs = 7;
|
|
}
|
|
|
|
message ProviderCredentialRefreshStatus {
|
|
string provider_name = 1;
|
|
string provider_id = 2;
|
|
string credential_key = 3;
|
|
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.
|
|
message ProviderProfileDiscovery {
|
|
// Credential names from ProviderProfile.credentials eligible for local discovery.
|
|
repeated string credentials = 1;
|
|
}
|
|
|
|
message GetProviderRefreshStatusRequest {
|
|
reserved 3;
|
|
reserved "workspace";
|
|
string provider = 1;
|
|
string credential_key = 2;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
}
|
|
|
|
message GetProviderRefreshStatusResponse {
|
|
repeated ProviderCredentialRefreshStatus credentials = 1;
|
|
}
|
|
|
|
message ConfigureProviderRefreshRequest {
|
|
reserved 7;
|
|
reserved "workspace";
|
|
string provider = 1;
|
|
string credential_key = 2;
|
|
ProviderCredentialRefreshStrategy strategy = 3;
|
|
map<string, string> material = 4 [(openshell.options.v1.secret) = true];
|
|
// Additional material names the caller requests be stored as secrets. Every
|
|
// name must be present in material. The server also classifies secrets from
|
|
// the authoritative provider profile and refresh strategy.
|
|
repeated string secret_material_keys = 5;
|
|
optional int64 expires_at_ms = 6;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 8;
|
|
}
|
|
|
|
message ConfigureProviderRefreshResponse {
|
|
ProviderCredentialRefreshStatus status = 1;
|
|
}
|
|
|
|
message RotateProviderCredentialRequest {
|
|
reserved 3;
|
|
reserved "workspace";
|
|
string provider = 1;
|
|
string credential_key = 2;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
}
|
|
|
|
message RotateProviderCredentialResponse {
|
|
ProviderCredentialRefreshStatus status = 1;
|
|
}
|
|
|
|
message DeleteProviderRefreshRequest {
|
|
reserved 3;
|
|
reserved "workspace";
|
|
string provider = 1;
|
|
string credential_key = 2;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
}
|
|
|
|
message DeleteProviderRefreshResponse {
|
|
bool deleted = 1;
|
|
}
|
|
|
|
// Stable provider profile categories used by clients for grouping and filtering.
|
|
enum ProviderProfileCategory {
|
|
PROVIDER_PROFILE_CATEGORY_UNSPECIFIED = 0;
|
|
PROVIDER_PROFILE_CATEGORY_OTHER = 1;
|
|
PROVIDER_PROFILE_CATEGORY_INFERENCE = 2;
|
|
PROVIDER_PROFILE_CATEGORY_AGENT = 3;
|
|
PROVIDER_PROFILE_CATEGORY_SOURCE_CONTROL = 4;
|
|
PROVIDER_PROFILE_CATEGORY_MESSAGING = 5;
|
|
PROVIDER_PROFILE_CATEGORY_DATA = 6;
|
|
PROVIDER_PROFILE_CATEGORY_KNOWLEDGE = 7;
|
|
}
|
|
|
|
// Provider type profile metadata exposed to clients.
|
|
message ProviderProfile {
|
|
string id = 1;
|
|
string display_name = 2;
|
|
string description = 3;
|
|
ProviderProfileCategory category = 4;
|
|
repeated ProviderProfileCredential credentials = 5;
|
|
repeated openshell.sandbox.v1.NetworkEndpoint endpoints = 6;
|
|
repeated openshell.sandbox.v1.NetworkBinary binaries = 7;
|
|
bool inference_capable = 8;
|
|
ProviderProfileDiscovery discovery = 9;
|
|
// Storage resource version for custom profiles. Built-in profiles and new
|
|
// profile files use 0. Gateway responses set this for stored custom profiles.
|
|
// Update calls use this for optimistic concurrency.
|
|
uint64 resource_version = 10;
|
|
// Optional non-secret annotations attached by profile sources or importers.
|
|
map<string, string> annotations = 11;
|
|
// Server-set provenance: "builtin", "user", or "interceptor/{name}".
|
|
// Ignored on import/update payloads.
|
|
string source = 12;
|
|
// Server-set visibility: "platform", "workspace", or empty for
|
|
// non-scoped sources. Ignored on import/update payloads.
|
|
string scope = 13;
|
|
}
|
|
|
|
// Provider profile response.
|
|
message ProviderProfileResponse {
|
|
ProviderProfile profile = 1;
|
|
}
|
|
|
|
// List provider profiles response.
|
|
message ListProviderProfilesResponse {
|
|
repeated ProviderProfile profiles = 1;
|
|
// Token for the next page. Empty when there are no subsequent pages.
|
|
string next_page_token = 2;
|
|
}
|
|
|
|
// Import custom provider profiles request.
|
|
message ImportProviderProfilesRequest {
|
|
repeated ProviderProfileImportItem profiles = 1;
|
|
// Workspace scope. When set, profiles are workspace-scoped (Workspace Admin).
|
|
// When empty, profiles are platform-scoped (Platform Admin).
|
|
string workspace = 2;
|
|
}
|
|
|
|
// Import custom provider profiles response.
|
|
message ImportProviderProfilesResponse {
|
|
repeated ProviderProfileDiagnostic diagnostics = 1;
|
|
repeated ProviderProfile profiles = 2;
|
|
bool imported = 3;
|
|
}
|
|
|
|
// Update one custom provider profile request.
|
|
message UpdateProviderProfilesRequest {
|
|
ProviderProfileImportItem profile = 1;
|
|
// Expected storage resource version for optimistic concurrency control.
|
|
// If 0, the server uses the resource_version embedded in profile.profile.
|
|
// Updates without a non-zero version are rejected to prevent stale files from
|
|
// silently overwriting newer profile definitions.
|
|
uint64 expected_resource_version = 2;
|
|
// Existing custom provider profile ID to update. The payload ID must match.
|
|
string id = 3;
|
|
// Workspace scope. When set, targets workspace-scoped profile. When empty,
|
|
// targets platform-scoped profile.
|
|
string workspace = 4;
|
|
}
|
|
|
|
// Update one custom provider profile response.
|
|
message UpdateProviderProfilesResponse {
|
|
repeated ProviderProfileDiagnostic diagnostics = 1;
|
|
ProviderProfile profile = 2;
|
|
bool updated = 3;
|
|
}
|
|
|
|
// Lint provider profiles request.
|
|
message LintProviderProfilesRequest {
|
|
repeated ProviderProfileImportItem profiles = 1;
|
|
// Workspace scope. Used to check for conflicts against existing profiles
|
|
// in the target workspace.
|
|
string workspace = 2;
|
|
}
|
|
|
|
// Lint provider profiles response.
|
|
message LintProviderProfilesResponse {
|
|
repeated ProviderProfileDiagnostic diagnostics = 1;
|
|
bool valid = 2;
|
|
}
|
|
|
|
// Delete provider response.
|
|
message DeleteProviderResponse {
|
|
bool deleted = 1;
|
|
}
|
|
|
|
// Delete custom provider profile request.
|
|
message DeleteProviderProfileRequest {
|
|
string id = 1;
|
|
// Workspace scope. When set, targets workspace-scoped profile. When empty,
|
|
// targets platform-scoped profile.
|
|
string workspace = 2;
|
|
}
|
|
|
|
// Delete custom provider profile response.
|
|
message DeleteProviderProfileResponse {
|
|
bool deleted = 1;
|
|
}
|
|
|
|
// Get sandbox provider environment request.
|
|
message GetSandboxProviderEnvironmentRequest {
|
|
// The sandbox ID.
|
|
string sandbox_id = 1;
|
|
// Whether the requesting supervisor enforces endpoint bindings for static
|
|
// provider credentials. Gateways withhold static credential material when
|
|
// this capability is absent.
|
|
bool supports_static_credential_bindings = 2;
|
|
}
|
|
|
|
// One network endpoint at which a static provider credential may be resolved.
|
|
message StaticCredentialEndpointBinding {
|
|
string host = 1;
|
|
uint32 port = 2;
|
|
string path = 3;
|
|
}
|
|
|
|
// Endpoint allowlist for one static provider credential environment variable.
|
|
message StaticCredentialBinding {
|
|
repeated StaticCredentialEndpointBinding endpoints = 1;
|
|
// Stable identity of the provider credential that produced this binding.
|
|
// Supervisors use it to retain old revision placeholders only across
|
|
// rotations of the same provider credential.
|
|
string credential_identity = 2;
|
|
// Opaque gateway-issued handle for a refresh-managed credential identity
|
|
// epoch. When non-empty, supervisors keep the workload placeholder stable
|
|
// across access-token rotations and replace only the resolver value. The
|
|
// handle changes when the sandbox, provider, credential key, refresh
|
|
// authorization epoch, or endpoint authorization boundary changes.
|
|
string workload_credential_handle = 3;
|
|
}
|
|
|
|
// Get sandbox provider environment response.
|
|
message GetSandboxProviderEnvironmentResponse {
|
|
// Provider credential environment variables.
|
|
map<string, string> environment = 1 [(openshell.options.v1.secret) = true];
|
|
// Fingerprint for the provider credential inputs that produced environment.
|
|
uint64 provider_env_revision = 2;
|
|
// Expiration timestamps for returned environment variables.
|
|
map<string, int64> credential_expires_at_ms = 3;
|
|
// Dynamic credentials that require token grants or other runtime injection.
|
|
// Maps endpoint-bound provider metadata to credential metadata.
|
|
// Supervisor uses this to inject Authorization headers for token grant credentials.
|
|
map<string, ProviderProfileCredential> dynamic_credentials = 4;
|
|
// Endpoint allowlists for static credential environment variables. Metadata
|
|
// can be incomplete when a provider profile is invalid; capable supervisors
|
|
// reject all static material in that snapshot while preserving independently
|
|
// endpoint-bound dynamic credentials.
|
|
map<string, StaticCredentialBinding> static_credential_bindings = 5;
|
|
// Environment variables that contain provider configuration rather than
|
|
// credentials and therefore do not require endpoint-scoped resolution.
|
|
repeated string non_secret_environment_keys = 6;
|
|
}
|
|
|
|
message ExchangeProviderSubjectTokenRequest {
|
|
// The sandbox ID. Must match the authenticated sandbox principal.
|
|
string sandbox_id = 1;
|
|
|
|
// Attached provider record holding the configured subject token credential.
|
|
string provider = 2;
|
|
|
|
// Provider profile credential that declares the token_exchange grant.
|
|
string credential_key = 3;
|
|
|
|
// Supervisor JWT-SVID. The gateway verifies this and uses its `sub` claim
|
|
// as the requested audience for the intermediate token.
|
|
string supervisor_jwt_svid = 4 [(openshell.options.v1.secret) = true];
|
|
}
|
|
|
|
message ExchangeProviderSubjectTokenResponse {
|
|
string access_token = 1 [(openshell.options.v1.secret) = true];
|
|
int64 expires_in = 2;
|
|
string token_type = 3;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Policy update messages
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Update sandbox policy request.
|
|
message UpdateConfigRequest {
|
|
reserved 10;
|
|
reserved "workspace";
|
|
// Sandbox name (canonical lookup key). Required for sandbox-scoped updates.
|
|
// Not required when `global=true`.
|
|
string name = 1;
|
|
// The new policy to apply.
|
|
//
|
|
// Sandbox scope (`global=false`):
|
|
// - only network_policies may differ from create-time
|
|
// policy; static fields must match version 1.
|
|
//
|
|
// Global scope (`global=true`):
|
|
// - applies to all sandboxes in full (no merge).
|
|
openshell.sandbox.v1.SandboxPolicy policy = 2;
|
|
// Optional single setting key to mutate.
|
|
string setting_key = 3;
|
|
// Setting value for upsert operations.
|
|
openshell.sandbox.v1.SettingValue setting_value = 4;
|
|
// Delete the setting key from scope.
|
|
// Sandbox-scoped deletes are rejected; only global delete is supported.
|
|
bool delete_setting = 5;
|
|
// Apply mutation at gateway-global scope.
|
|
bool global = 6;
|
|
// Batched incremental policy merge operations. Sandbox-scoped only.
|
|
repeated PolicyMergeOperation merge_operations = 7;
|
|
// Expected resource version for optimistic concurrency control (sandbox-scoped only).
|
|
// If 0, the server uses the current version (backward compatibility).
|
|
// If non-zero, the server validates that the sandbox's current resource_version
|
|
// matches this value before applying the mutation, returning ABORTED on mismatch.
|
|
// Ignored for global-scoped updates.
|
|
uint64 expected_resource_version = 8;
|
|
// Caller-provided annotations associated with a sandbox-scoped update. Values
|
|
// must not contain secrets; the gateway treats them as opaque metadata and does
|
|
// not interpret or verify their semantics. For policy updates, the gateway
|
|
// stores the annotations immutably with the revision and merges them into
|
|
// sandbox metadata as a convenience projection. For setting-only updates, it
|
|
// only merges them into sandbox metadata.
|
|
map<string, string> annotations = 9;
|
|
// Explicit workspace scope for sandbox-scoped updates. Omit only when
|
|
// `global` is true; the all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 11;
|
|
}
|
|
|
|
message PolicyMergeOperation {
|
|
oneof operation {
|
|
AddNetworkRule add_rule = 1;
|
|
RemoveNetworkEndpoint remove_endpoint = 2;
|
|
RemoveNetworkRule remove_rule = 3;
|
|
AddDenyRules add_deny_rules = 4;
|
|
AddAllowRules add_allow_rules = 5;
|
|
RemoveNetworkBinary remove_binary = 6;
|
|
}
|
|
}
|
|
|
|
message AddNetworkRule {
|
|
string rule_name = 1;
|
|
openshell.sandbox.v1.NetworkPolicyRule rule = 2;
|
|
}
|
|
|
|
message RemoveNetworkEndpoint {
|
|
string rule_name = 1;
|
|
string host = 2;
|
|
uint32 port = 3;
|
|
}
|
|
|
|
message RemoveNetworkRule {
|
|
string rule_name = 1;
|
|
}
|
|
|
|
message AddDenyRules {
|
|
string host = 1;
|
|
uint32 port = 2;
|
|
repeated openshell.sandbox.v1.L7DenyRule deny_rules = 3;
|
|
}
|
|
|
|
message AddAllowRules {
|
|
string host = 1;
|
|
uint32 port = 2;
|
|
repeated openshell.sandbox.v1.L7Rule rules = 3;
|
|
}
|
|
|
|
message RemoveNetworkBinary {
|
|
string rule_name = 1;
|
|
string binary_path = 2;
|
|
}
|
|
|
|
// Update sandbox policy response.
|
|
message UpdateConfigResponse {
|
|
// Assigned policy version (monotonically increasing per sandbox).
|
|
uint32 version = 1;
|
|
// SHA-256 hash of the serialized policy payload.
|
|
string policy_hash = 2;
|
|
// Settings revision for the scope that was modified.
|
|
uint64 settings_revision = 3;
|
|
// True when a setting delete operation removed an existing key.
|
|
bool deleted = 4;
|
|
// Sandbox metadata annotations after the update. Empty for global updates.
|
|
map<string, string> annotations = 5;
|
|
}
|
|
|
|
// Get sandbox policy status request.
|
|
message GetSandboxPolicyStatusRequest {
|
|
reserved 4;
|
|
reserved "workspace";
|
|
// Sandbox name (canonical lookup key). Ignored when global is true.
|
|
string name = 1;
|
|
// The specific policy version to query. 0 means latest.
|
|
uint32 version = 2;
|
|
// Query global policy revisions instead of a sandbox-scoped one.
|
|
bool global = 3;
|
|
// Explicit workspace scope for sandbox-scoped queries. Omit only when
|
|
// `global` is true; the all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 5;
|
|
}
|
|
|
|
// Get sandbox policy status response.
|
|
message GetSandboxPolicyStatusResponse {
|
|
// The queried policy revision.
|
|
SandboxPolicyRevision revision = 1;
|
|
// The currently active (loaded) policy version for this sandbox.
|
|
uint32 active_version = 2;
|
|
}
|
|
|
|
// List sandbox policies request.
|
|
message ListSandboxPoliciesRequest {
|
|
reserved 5;
|
|
reserved "workspace";
|
|
// Sandbox name (canonical lookup key). Ignored when global is true.
|
|
string name = 1;
|
|
// The maximum number of revisions to return. Zero uses 100. Values above
|
|
// 1000 are coerced to 1000; negative values are invalid.
|
|
int32 page_size = 2;
|
|
// Token from a previous ListSandboxPolicies response. All other request
|
|
// parameters except page_size must match the request that produced it.
|
|
string page_token = 3;
|
|
// List global policy revisions instead of sandbox-scoped ones.
|
|
bool global = 4;
|
|
// Explicit workspace scope for sandbox-scoped queries. Omit only when
|
|
// `global` is true; the all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 6;
|
|
}
|
|
|
|
// List sandbox policies response.
|
|
message ListSandboxPoliciesResponse {
|
|
// Invalid historical payloads remain visible as failed projections so one
|
|
// legacy row cannot hide the rest of the policy history.
|
|
repeated SandboxPolicyRevision revisions = 1;
|
|
// Token for the next page. Empty when there are no subsequent pages.
|
|
string next_page_token = 2;
|
|
}
|
|
|
|
// Report policy load status (called by sandbox runtime after reload attempt).
|
|
message ReportPolicyStatusRequest {
|
|
// Sandbox id.
|
|
string sandbox_id = 1;
|
|
// The policy version that was attempted.
|
|
uint32 version = 2;
|
|
// Load result status.
|
|
PolicyStatus status = 3;
|
|
// Error message if status is FAILED.
|
|
string load_error = 4;
|
|
}
|
|
|
|
// Report policy status response.
|
|
message ReportPolicyStatusResponse {}
|
|
|
|
// A versioned policy revision with metadata.
|
|
message SandboxPolicyRevision {
|
|
// Policy version (monotonically increasing per sandbox).
|
|
uint32 version = 1;
|
|
// SHA-256 hash of the canonical serialized policy payload. Empty in a
|
|
// ListSandboxPolicies projection when the stored payload is invalid under
|
|
// the current schema and therefore has no trusted canonical identity.
|
|
string policy_hash = 2;
|
|
// Load status of this revision. ListSandboxPolicies reports FAILED when a
|
|
// stored historical payload is invalid under the current schema, regardless
|
|
// of its persisted sandbox load status.
|
|
PolicyStatus status = 3;
|
|
// Sandbox load error, or the schema-validation diagnostic for an invalid
|
|
// historical row returned by ListSandboxPolicies.
|
|
string load_error = 4;
|
|
// Milliseconds since epoch when this revision was created.
|
|
int64 created_at_ms = 5;
|
|
// Milliseconds since epoch when this revision was loaded by the sandbox.
|
|
int64 loaded_at_ms = 6;
|
|
// The full policy (only populated when explicitly requested).
|
|
openshell.sandbox.v1.SandboxPolicy policy = 7;
|
|
// Immutable provenance supplied with this policy revision.
|
|
map<string, string> provenance = 8;
|
|
}
|
|
|
|
// Policy load status.
|
|
enum PolicyStatus {
|
|
POLICY_STATUS_UNSPECIFIED = 0;
|
|
// Server received the update; sandbox has not yet loaded it.
|
|
POLICY_STATUS_PENDING = 1;
|
|
// Sandbox successfully applied this policy version.
|
|
POLICY_STATUS_LOADED = 2;
|
|
// Sandbox attempted to apply but failed; LKG policy remains active.
|
|
// ListSandboxPolicies also uses FAILED for historical payloads that are
|
|
// invalid under the current schema; load_error contains the diagnostic.
|
|
POLICY_STATUS_FAILED = 3;
|
|
// A newer version was persisted before the sandbox loaded this one.
|
|
POLICY_STATUS_SUPERSEDED = 4;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Sandbox logs messages
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Get sandbox logs request (one-shot fetch).
|
|
message GetSandboxLogsRequest {
|
|
reserved 6;
|
|
reserved "workspace";
|
|
// Sandbox id.
|
|
string sandbox_id = 1;
|
|
// Maximum number of log lines to return. 0 means use default (2000).
|
|
uint32 lines = 2;
|
|
// Only include logs with timestamp >= this value (ms since epoch). 0 means no filter.
|
|
int64 since_ms = 3;
|
|
// Filter by log source (e.g. "gateway", "sandbox"). Empty means all sources.
|
|
repeated string sources = 4;
|
|
// Minimum log level to include (e.g. "INFO", "WARN", "ERROR"). Empty means all levels.
|
|
string min_level = 5;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 7;
|
|
}
|
|
|
|
// Batch of log lines pushed from sandbox to server.
|
|
message PushSandboxLogsRequest {
|
|
// The sandbox ID.
|
|
string sandbox_id = 1;
|
|
// Log lines to ingest.
|
|
repeated SandboxLogLine logs = 2;
|
|
}
|
|
|
|
// Push sandbox logs response.
|
|
message PushSandboxLogsResponse {}
|
|
|
|
// Get sandbox logs response.
|
|
message GetSandboxLogsResponse {
|
|
// Log lines in chronological order.
|
|
repeated SandboxLogLine logs = 1;
|
|
// Total number of lines in the server's buffer for this sandbox.
|
|
uint32 buffer_total = 2;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Supervisor session messages
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Envelope for supervisor-to-gateway messages on the ConnectSupervisor stream.
|
|
message SupervisorMessage {
|
|
oneof payload {
|
|
SupervisorHello hello = 1;
|
|
SupervisorHeartbeat heartbeat = 2;
|
|
RelayOpenResult relay_open_result = 3;
|
|
RelayClose relay_close = 4;
|
|
}
|
|
}
|
|
|
|
// Envelope for gateway-to-supervisor messages on the ConnectSupervisor stream.
|
|
message GatewayMessage {
|
|
oneof payload {
|
|
SessionAccepted session_accepted = 1;
|
|
SessionRejected session_rejected = 2;
|
|
GatewayHeartbeat heartbeat = 3;
|
|
RelayOpen relay_open = 4;
|
|
RelayClose relay_close = 5;
|
|
}
|
|
}
|
|
|
|
// Supervisor identifies itself and the sandbox it manages.
|
|
message SupervisorHello {
|
|
// Sandbox ID this supervisor manages.
|
|
string sandbox_id = 1;
|
|
// Supervisor instance ID (e.g. boot id or process epoch).
|
|
string instance_id = 2;
|
|
}
|
|
|
|
// Gateway accepts the supervisor session.
|
|
message SessionAccepted {
|
|
// Gateway-assigned session ID for this connection.
|
|
string session_id = 1;
|
|
// Recommended heartbeat interval in seconds.
|
|
uint32 heartbeat_interval_secs = 2;
|
|
}
|
|
|
|
// Gateway rejects the supervisor session.
|
|
message SessionRejected {
|
|
// Human-readable rejection reason.
|
|
string reason = 1;
|
|
}
|
|
|
|
// Supervisor heartbeat.
|
|
message SupervisorHeartbeat {}
|
|
|
|
// Gateway heartbeat.
|
|
message GatewayHeartbeat {}
|
|
|
|
// Terminal result reported before the supervisor shuts down. A successful RPC
|
|
// response confirms that the result was durably handled by the gateway.
|
|
message ReportMainProcessExitRequest {
|
|
string sandbox_id = 1;
|
|
string instance_id = 2;
|
|
// Normalized process result. Signal exits use 128 + signal number.
|
|
int32 exit_code = 3;
|
|
}
|
|
|
|
message ReportMainProcessExitResponse {}
|
|
|
|
// Terminal-delivery completion reported after all expected foreground SSH
|
|
// attachments close. A successful response permits ephemeral cleanup.
|
|
message FinalizeMainProcessExitRequest {
|
|
string sandbox_id = 1;
|
|
string instance_id = 2;
|
|
}
|
|
|
|
message FinalizeMainProcessExitResponse {}
|
|
|
|
// Gateway requests the supervisor to open a relay channel.
|
|
//
|
|
// On receiving this, the supervisor should initiate a RelayStream RPC to
|
|
// the gateway, sending a RelayInit in the first RelayFrame to associate
|
|
// the new HTTP/2 stream with the pending relay slot. The supervisor
|
|
// bridges that stream to the requested local target.
|
|
message RelayOpen {
|
|
// Gateway-allocated channel identifier (UUID).
|
|
string channel_id = 1;
|
|
// Target the supervisor should dial inside the sandbox.
|
|
// If absent, supervisors treat the relay as SSH for compatibility.
|
|
oneof target {
|
|
SshRelayTarget ssh = 2;
|
|
TcpRelayTarget tcp = 3;
|
|
}
|
|
// Optional service identifier for audit/correlation.
|
|
string service_id = 5;
|
|
}
|
|
|
|
// Built-in SSH relay target.
|
|
message SshRelayTarget {}
|
|
|
|
// TCP target dialed by the supervisor from inside the sandbox.
|
|
message TcpRelayTarget {
|
|
// Phase 1 accepts loopback only: 127.0.0.1, ::1, or localhost.
|
|
string host = 1;
|
|
// Target port. Must fit in u16 and be non-zero.
|
|
uint32 port = 2;
|
|
}
|
|
|
|
// Initial RelayStream frame sent by the supervisor to claim a pending relay.
|
|
message RelayInit {
|
|
// Gateway-allocated channel identifier (UUID).
|
|
string channel_id = 1;
|
|
}
|
|
|
|
// A single frame on the RelayStream RPC.
|
|
//
|
|
// The supervisor MUST send `init` as the first frame. All subsequent frames
|
|
// in either direction carry raw bytes in `data`.
|
|
message RelayFrame {
|
|
oneof payload {
|
|
RelayInit init = 1;
|
|
bytes data = 2;
|
|
}
|
|
}
|
|
|
|
// Supervisor reports the result of a relay open request.
|
|
message RelayOpenResult {
|
|
// Channel identifier from the RelayOpen request.
|
|
string channel_id = 1;
|
|
// True if the relay was successfully established.
|
|
bool success = 2;
|
|
// Error message if success is false.
|
|
string error = 3;
|
|
}
|
|
|
|
// Either side requests closure of a relay channel.
|
|
message RelayClose {
|
|
// Channel identifier to close.
|
|
string channel_id = 1;
|
|
// Optional reason for closure.
|
|
string reason = 2;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Service status
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Service status enum.
|
|
enum ServiceStatus {
|
|
SERVICE_STATUS_UNSPECIFIED = 0;
|
|
SERVICE_STATUS_HEALTHY = 1;
|
|
SERVICE_STATUS_DEGRADED = 2;
|
|
SERVICE_STATUS_UNHEALTHY = 3;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Draft policy recommendation messages
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Observed HTTP method+path pattern from L7 inspection.
|
|
message L7RequestSample {
|
|
// HTTP method: GET, POST, PUT, DELETE, etc.
|
|
string method = 1;
|
|
// HTTP path: /v1/models, /repos/myorg/issues
|
|
string path = 2;
|
|
// L7 decision: "audit" or "deny" (allowed requests not collected).
|
|
string decision = 3;
|
|
// Number of times this (method, path) was observed.
|
|
uint32 count = 4;
|
|
}
|
|
|
|
// Structured denial summary from sandbox aggregator.
|
|
message DenialSummary {
|
|
// Sandbox ID that produced this summary.
|
|
string sandbox_id = 1;
|
|
// Denied destination host.
|
|
string host = 2;
|
|
// Denied destination port.
|
|
uint32 port = 3;
|
|
// Binary that attempted the connection.
|
|
string binary = 4;
|
|
// Process ancestor chain.
|
|
repeated string ancestors = 5;
|
|
// Denial reason from OPA evaluation.
|
|
string deny_reason = 6;
|
|
// First denial timestamp (ms since epoch).
|
|
int64 first_seen_ms = 7;
|
|
// Most recent denial timestamp (ms since epoch).
|
|
int64 last_seen_ms = 8;
|
|
// Number of denials in the current window.
|
|
uint32 count = 9;
|
|
// Events dropped during aggregator cooldown.
|
|
uint32 suppressed_count = 10;
|
|
// Cumulative lifetime count (never resets).
|
|
uint32 total_count = 11;
|
|
// Distinct cmdline strings observed (sanitized of credentials).
|
|
repeated string sample_cmdlines = 12;
|
|
// SHA-256 of the binary for audit trail.
|
|
string binary_sha256 = 13;
|
|
// True if emitted by stale-flush rather than threshold.
|
|
bool persistent = 14;
|
|
// Denial category: "l4_deny", "l7_deny", "l7_audit", "ssrf".
|
|
string denial_stage = 15;
|
|
// Observed HTTP request patterns (from L7 inspection).
|
|
repeated L7RequestSample l7_request_samples = 16;
|
|
// True if L7 inspection was active during observation window.
|
|
bool l7_inspection_active = 17;
|
|
}
|
|
|
|
// Count of denied actions grouped only by sanitized telemetry category.
|
|
message DenialGroupCount {
|
|
// Sanitized denial category, e.g. "connect_policy", "l7_policy", "ssrf".
|
|
string deny_group = 1;
|
|
// Number of denied actions in this category.
|
|
uint32 denied_count = 2;
|
|
}
|
|
|
|
// Anonymous sandbox network activity counters. This intentionally excludes
|
|
// hosts, paths, binaries, raw deny reasons, sandbox IDs, and user content.
|
|
message NetworkActivitySummary {
|
|
// Total observed network activities in the current window.
|
|
uint32 network_activity_count = 1;
|
|
// Total denied actions in the current window.
|
|
uint32 denied_action_count = 2;
|
|
// Denied action counts grouped by sanitized category.
|
|
repeated DenialGroupCount denials_by_group = 3;
|
|
}
|
|
|
|
// A proposed policy rule with rationale and approval status.
|
|
message PolicyChunk {
|
|
// Unique chunk identifier.
|
|
string id = 1;
|
|
// Approval status: "pending", "approved", "rejected".
|
|
string status = 2;
|
|
// Proposed network_policies map key.
|
|
string rule_name = 3;
|
|
// The proposed network policy rule.
|
|
openshell.sandbox.v1.NetworkPolicyRule proposed_rule = 4;
|
|
// Human-readable explanation of why this rule is proposed.
|
|
string rationale = 5;
|
|
// Security concerns flagged by analysis (empty if none).
|
|
string security_notes = 6;
|
|
// Analysis confidence (0.0-1.0). 0 for mechanistic mode.
|
|
float confidence = 7;
|
|
// IDs of denial summaries that led to this chunk.
|
|
repeated string denial_summary_ids = 8;
|
|
// Creation timestamp (ms since epoch).
|
|
int64 created_at_ms = 9;
|
|
// When the user approved/rejected (ms since epoch). 0 if undecided.
|
|
int64 decided_at_ms = 10;
|
|
// Recommendation stage: "initial" or "refined" (progressive L7 visibility).
|
|
string stage = 11;
|
|
// For stage="refined": the initial chunk this replaces.
|
|
string supersedes_chunk_id = 12;
|
|
// How many times this endpoint has been seen across denial flush cycles.
|
|
int32 hit_count = 13;
|
|
// First time this endpoint was proposed (ms since epoch).
|
|
int64 first_seen_ms = 14;
|
|
// Most recent time this endpoint was re-proposed (ms since epoch).
|
|
int64 last_seen_ms = 15;
|
|
// Binary path that triggered the denial (denormalized for display convenience).
|
|
string binary = 16;
|
|
// Validation verdict from gateway-side static checks (prover output).
|
|
// Free-form summary string for human consumption in the inbox card.
|
|
// Empty until the prover has run for this chunk.
|
|
string validation_result = 17;
|
|
// Operator-supplied free-form text accompanying a rejection. Populated
|
|
// when the reviewer rejects via `RejectDraftChunkRequest.reason`; surfaced
|
|
// back to the in-sandbox agent so it can revise the proposal.
|
|
// Empty for non-rejected chunks.
|
|
string rejection_reason = 18;
|
|
// Gateway-side merge/application preflight failure. Kept separate from
|
|
// prover output and operator rejection so clients can explain why a
|
|
// prover-clean proposal is not currently applicable.
|
|
string application_error = 19;
|
|
// Opaque digest binding review to the exact live inputs and complete
|
|
// effective candidate evaluated by the gateway.
|
|
string review_token = 20;
|
|
// Deterministic hashes for compact candidate display and diagnostics.
|
|
string current_effective_policy_hash = 21;
|
|
string candidate_effective_policy_hash = 22;
|
|
// Complete effective policies used for review. These contain policy
|
|
// configuration only; credential secret values are never materialized.
|
|
openshell.sandbox.v1.SandboxPolicy current_effective_policy = 23;
|
|
openshell.sandbox.v1.SandboxPolicy candidate_effective_policy = 24;
|
|
}
|
|
|
|
// Notification that the draft policy was updated.
|
|
message DraftPolicyUpdate {
|
|
// Current draft version.
|
|
uint64 draft_version = 1;
|
|
// Number of new chunks added in this update.
|
|
uint32 new_chunks = 2;
|
|
// Total pending chunks awaiting approval.
|
|
uint32 total_pending = 3;
|
|
// Brief description of what changed.
|
|
string summary = 4;
|
|
}
|
|
|
|
// Submit analysis results from sandbox to gateway.
|
|
message SubmitPolicyAnalysisRequest {
|
|
// Aggregated denial summaries.
|
|
repeated DenialSummary summaries = 1;
|
|
// Proposed policy chunks (validated by sandbox OPA engine).
|
|
repeated PolicyChunk proposed_chunks = 2;
|
|
// Analysis mode. `mechanistic` is the observation-driven path from the
|
|
// denial aggregator — chunks targeting the same host|port|binary fold
|
|
// into one row with hit_count incremented. `agent_authored` is an
|
|
// intentional proposal from an in-sandbox agent — each submission lands
|
|
// as its own chunk so the redraft-after-rejection loop has a stable id
|
|
// to watch. Other values are treated as agent-style (no dedup) so a new
|
|
// mode does not silently collapse proposals.
|
|
string analysis_mode = 3;
|
|
// Sandbox name.
|
|
string name = 4;
|
|
// Anonymous network activity counters.
|
|
repeated NetworkActivitySummary network_activity_summaries = 5;
|
|
// Workspace scope. Empty defaults to "default".
|
|
string workspace = 6;
|
|
}
|
|
|
|
message SubmitPolicyAnalysisResponse {
|
|
// Number of chunks accepted by the gateway.
|
|
uint32 accepted_chunks = 1;
|
|
// Number of chunks rejected by gateway validation.
|
|
uint32 rejected_chunks = 2;
|
|
// Reasons for each rejected chunk.
|
|
repeated string rejection_reasons = 3;
|
|
// Server-assigned chunk IDs for the accepted chunks, in submission order.
|
|
// Agents use these to watch proposal state via policy.local's
|
|
// GET /v1/proposals/{id} and /wait endpoints.
|
|
repeated string accepted_chunk_ids = 4;
|
|
}
|
|
|
|
// Get draft policy for a sandbox.
|
|
message GetDraftPolicyRequest {
|
|
reserved 3;
|
|
reserved "workspace";
|
|
// Sandbox name.
|
|
string name = 1;
|
|
// Optional status filter: "pending", "approved", "rejected", or "" for all.
|
|
string status_filter = 2;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
}
|
|
|
|
message GetDraftPolicyResponse {
|
|
// Draft policy chunks.
|
|
repeated PolicyChunk chunks = 1;
|
|
// LLM-generated summary of all analysis (empty in mechanistic mode).
|
|
string rolling_summary = 2;
|
|
// Current draft version.
|
|
uint64 draft_version = 3;
|
|
// When the last analysis completed (ms since epoch).
|
|
int64 last_analyzed_at_ms = 4;
|
|
}
|
|
|
|
// Approve a single draft chunk.
|
|
message ApproveDraftChunkRequest {
|
|
reserved 3;
|
|
reserved "workspace";
|
|
// Sandbox name.
|
|
string name = 1;
|
|
// Chunk ID to approve.
|
|
string chunk_id = 2;
|
|
// Token returned with the reviewed PolicyChunk. Approval fails with
|
|
// FAILED_PRECONDITION if live decision inputs no longer match it.
|
|
string review_token = 4;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 5;
|
|
}
|
|
|
|
message ApproveDraftChunkResponse {
|
|
// New policy version after merge.
|
|
uint32 policy_version = 1;
|
|
// SHA-256 hash of the new policy.
|
|
string policy_hash = 2;
|
|
}
|
|
|
|
// Reject a single draft chunk.
|
|
message RejectDraftChunkRequest {
|
|
reserved 4;
|
|
reserved "workspace";
|
|
// Sandbox name.
|
|
string name = 1;
|
|
// Chunk ID to reject.
|
|
string chunk_id = 2;
|
|
// Optional reason for rejection (fed to LLM context in future analysis).
|
|
string reason = 3;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 5;
|
|
}
|
|
|
|
message RejectDraftChunkResponse {}
|
|
|
|
// Approve all pending chunks.
|
|
message DraftChunkApproval {
|
|
string chunk_id = 1;
|
|
string review_token = 2;
|
|
}
|
|
|
|
message ApproveAllDraftChunksRequest {
|
|
reserved 3;
|
|
reserved "workspace";
|
|
// Sandbox name.
|
|
string name = 1;
|
|
// Include chunks with security_notes (default false: skips them).
|
|
bool include_security_flagged = 2;
|
|
// Exact reviewed chunks and tokens. The server validates them against one
|
|
// live snapshot, stages compatible operations in order, and writes once.
|
|
repeated DraftChunkApproval approvals = 4;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 5;
|
|
}
|
|
|
|
message ApproveAllDraftChunksResponse {
|
|
// New policy version after merge.
|
|
uint32 policy_version = 1;
|
|
// SHA-256 hash of the new policy.
|
|
string policy_hash = 2;
|
|
// Number of chunks approved.
|
|
uint32 chunks_approved = 3;
|
|
// Number of chunks skipped for any reason, including security flags,
|
|
// stale or invalid candidates, and conflicts within the staged batch.
|
|
uint32 chunks_skipped = 4;
|
|
}
|
|
|
|
// Edit a pending chunk in-place.
|
|
message EditDraftChunkRequest {
|
|
reserved 4;
|
|
reserved "workspace";
|
|
// Sandbox name.
|
|
string name = 1;
|
|
// Chunk ID to edit.
|
|
string chunk_id = 2;
|
|
// The modified rule (replaces existing proposed_rule).
|
|
openshell.sandbox.v1.NetworkPolicyRule proposed_rule = 3;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 5;
|
|
}
|
|
|
|
message EditDraftChunkResponse {}
|
|
|
|
// Reverse an approval (remove merged rule from active policy).
|
|
message UndoDraftChunkRequest {
|
|
reserved 3;
|
|
reserved "workspace";
|
|
// Sandbox name.
|
|
string name = 1;
|
|
// Chunk ID to undo.
|
|
string chunk_id = 2;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
}
|
|
|
|
message UndoDraftChunkResponse {
|
|
// New policy version after removal.
|
|
uint32 policy_version = 1;
|
|
// SHA-256 hash of the updated policy.
|
|
string policy_hash = 2;
|
|
}
|
|
|
|
// Clear all pending draft chunks for a sandbox.
|
|
message ClearDraftChunksRequest {
|
|
reserved 2;
|
|
reserved "workspace";
|
|
// Sandbox name.
|
|
string name = 1;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
}
|
|
|
|
message ClearDraftChunksResponse {
|
|
// Number of chunks cleared.
|
|
uint32 chunks_cleared = 1;
|
|
}
|
|
|
|
// Get decision history for a sandbox's draft policy.
|
|
message GetDraftHistoryRequest {
|
|
reserved 2;
|
|
reserved "workspace";
|
|
// Sandbox name.
|
|
string name = 1;
|
|
// Explicit workspace scope. The all-workspaces selection is invalid.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
}
|
|
|
|
message DraftHistoryEntry {
|
|
// Event timestamp (ms since epoch).
|
|
int64 timestamp_ms = 1;
|
|
// Event type: "denial_detected", "analysis_cycle", "approved",
|
|
// "rejected", "edited", "undone", "cleared".
|
|
string event_type = 2;
|
|
// Human-readable description.
|
|
string description = 3;
|
|
// Associated chunk ID (if applicable).
|
|
string chunk_id = 4;
|
|
}
|
|
|
|
message GetDraftHistoryResponse {
|
|
// Chronological decision history.
|
|
repeated DraftHistoryEntry entries = 1;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Workspace messages
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Create workspace request.
|
|
message CreateWorkspaceRequest {
|
|
// Workspace name. Must be a valid DNS-1123 label.
|
|
string name = 1;
|
|
// Optional labels for the workspace (key-value metadata).
|
|
map<string, string> labels = 2;
|
|
}
|
|
|
|
// Create workspace response.
|
|
message CreateWorkspaceResponse {
|
|
openshell.datamodel.v1.Workspace workspace = 1;
|
|
}
|
|
|
|
// Get workspace request.
|
|
message GetWorkspaceRequest {
|
|
// Workspace name (canonical lookup key).
|
|
string name = 1;
|
|
}
|
|
|
|
// Get workspace response.
|
|
message GetWorkspaceResponse {
|
|
openshell.datamodel.v1.Workspace workspace = 1;
|
|
}
|
|
|
|
// List workspaces request.
|
|
message ListWorkspacesRequest {
|
|
// The maximum number of workspaces to return. Zero uses 100. Values above
|
|
// 1000 are coerced to 1000; negative values are invalid.
|
|
int32 page_size = 1;
|
|
// Token from a previous ListWorkspaces response. All other request parameters
|
|
// except page_size must match the request that produced it.
|
|
string page_token = 2;
|
|
// Optional label selector for filtering (format: "key1=value1,key2=value2").
|
|
string label_selector = 3;
|
|
}
|
|
|
|
// List workspaces response.
|
|
message ListWorkspacesResponse {
|
|
repeated openshell.datamodel.v1.Workspace workspaces = 1;
|
|
// Token for the next page. Empty when there are no subsequent pages.
|
|
string next_page_token = 2;
|
|
}
|
|
|
|
// Delete workspace request.
|
|
message DeleteWorkspaceRequest {
|
|
// Workspace name (canonical lookup key).
|
|
string name = 1;
|
|
}
|
|
|
|
// Delete workspace response.
|
|
message DeleteWorkspaceResponse {
|
|
bool deleted = 1;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Workspace membership messages
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Workspace-scoped role for members.
|
|
enum WorkspaceRole {
|
|
WORKSPACE_ROLE_UNSPECIFIED = 0;
|
|
WORKSPACE_ROLE_USER = 1;
|
|
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;
|
|
// OIDC subject claim identifying the principal.
|
|
string principal_subject = 2;
|
|
// Role assigned to the principal within the workspace.
|
|
WorkspaceRole role = 3;
|
|
}
|
|
|
|
// Add workspace member request.
|
|
message AddWorkspaceMemberRequest {
|
|
// Workspace name.
|
|
string workspace = 1;
|
|
// OIDC subject claim identifying the principal.
|
|
string principal_subject = 2;
|
|
// Role to assign.
|
|
WorkspaceRole role = 3;
|
|
}
|
|
|
|
// Add workspace member response.
|
|
message AddWorkspaceMemberResponse {
|
|
WorkspaceMember member = 1;
|
|
}
|
|
|
|
// Remove workspace member request.
|
|
message RemoveWorkspaceMemberRequest {
|
|
// Workspace name.
|
|
string workspace = 1;
|
|
// OIDC subject claim identifying the principal to remove.
|
|
string principal_subject = 2;
|
|
}
|
|
|
|
// Remove workspace member response.
|
|
message RemoveWorkspaceMemberResponse {
|
|
bool removed = 1;
|
|
}
|
|
|
|
// List workspace members request.
|
|
message ListWorkspaceMembersRequest {
|
|
// Workspace name.
|
|
string workspace = 1;
|
|
// The maximum number of members to return. Zero uses 100. Values above
|
|
// 1000 are coerced to 1000; negative values are invalid.
|
|
int32 page_size = 2;
|
|
// Token from a previous ListWorkspaceMembers response. All other request
|
|
// parameters except page_size must match the request that produced it.
|
|
string page_token = 3;
|
|
}
|
|
|
|
// List workspace members response.
|
|
message ListWorkspaceMembersResponse {
|
|
repeated WorkspaceMember members = 1;
|
|
// Token for the next page. Empty when there are no subsequent pages.
|
|
string next_page_token = 2;
|
|
}
|
|
|
|
// Short-lived credential for one policy-authorized extension service.
|
|
// Kept at the end of the file so adding it does not renumber existing
|
|
// generated message descriptors.
|
|
message ExtensionServiceCredential {
|
|
// Operator registration name used to correlate the credential with the
|
|
// stable service registration delivered by GetSandboxConfig.
|
|
string service_name = 1;
|
|
// Gateway-minted JWT with an audience derived from the registration.
|
|
string token = 2 [(openshell.options.v1.secret) = true];
|
|
// Absolute expiry of the token, milliseconds since the epoch.
|
|
int64 expires_at_ms = 3;
|
|
}
|
|
|
|
// Last observed network result for a configured external tool endpoint.
|
|
// Results describe accepted traffic observations, not present availability.
|
|
enum EndpointResult {
|
|
ENDPOINT_RESULT_UNSPECIFIED = 0;
|
|
// No exchange has been observed under the current configuration and session.
|
|
ENDPOINT_RESULT_NO_OBSERVED_EXCHANGE = 1;
|
|
// An upstream HTTP status below 400 was received. Its body can still contain
|
|
// an MCP error; this result does not establish tool-call success.
|
|
ENDPOINT_RESULT_HTTP_RESPONSE_RECEIVED = 2;
|
|
// OpenShell policy denied the request locally.
|
|
ENDPOINT_RESULT_POLICY_DENIED = 3;
|
|
// An applicable OpenShell-managed credential was unavailable.
|
|
ENDPOINT_RESULT_CREDENTIAL_UNAVAILABLE = 4;
|
|
// TLS setup for the upstream connection failed.
|
|
ENDPOINT_RESULT_TLS_FAILED = 5;
|
|
// The upstream transport failed before an HTTP response arrived.
|
|
ENDPOINT_RESULT_TRANSPORT_FAILED = 6;
|
|
// The upstream service returned an HTTP rejection.
|
|
ENDPOINT_RESULT_UPSTREAM_REJECTED = 7;
|
|
}
|
|
|
|
// One redacted endpoint result in a supervisor's complete status report.
|
|
message EndpointObservation {
|
|
// Stable identifier derived from the configured host, ports, and path.
|
|
string endpoint_id = 1;
|
|
// Latest result under the reported configuration and supervisor session.
|
|
EndpointResult result = 2;
|
|
}
|
|
|
|
// Complete endpoint status report for the caller's current configuration.
|
|
message ReportEndpointStatusRequest {
|
|
// Sandbox id. Must match the authenticated sandbox principal.
|
|
string sandbox_id = 1;
|
|
// Hash from the active effective policy delivered by the gateway.
|
|
string policy_hash = 2;
|
|
// Provider environment revision delivered with the active configuration.
|
|
uint64 provider_env_revision = 3;
|
|
// Exactly one result for every distinct observed endpoint in the policy.
|
|
repeated EndpointObservation observations = 4;
|
|
// Endpoints with a new observation in this batch. Omitted endpoints retain
|
|
// their prior report time; an identical retry never advances report time.
|
|
repeated string observed_endpoint_ids = 5;
|
|
// Active ConnectSupervisor session that owns these observations.
|
|
string supervisor_session_id = 6;
|
|
// Monotonically increasing sequence within the authenticated session. Gaps
|
|
// are allowed when an inventory reset supersedes a frozen snapshot. Retrying
|
|
// a report preserves its complete body and sequence for idempotent acknowledgement.
|
|
uint64 report_sequence = 7;
|
|
}
|
|
|
|
// Empty acknowledgement for a persisted endpoint status report.
|
|
message ReportEndpointStatusResponse {}
|
|
|
|
// A configured endpoint and its last accepted network result in one record.
|
|
// Address fields contain policy selectors, never request URLs or credentials.
|
|
message EndpointStatus {
|
|
// Stable identifier for selecting this endpoint without parsing display text.
|
|
string endpoint_id = 1;
|
|
// Lowercase configured endpoint host.
|
|
string host = 2;
|
|
// Sorted, deduplicated effective endpoint ports. Validated endpoints have at
|
|
// least one port.
|
|
repeated uint32 ports = 3;
|
|
// Canonical configured path selector; an unrestricted path is /**.
|
|
string path = 4;
|
|
// Last accepted result, aggregated across configured callers and ports.
|
|
// NoObservedExchange retains the address and has no report timestamp.
|
|
EndpointResult last_result = 5;
|
|
// RFC 3339 UTC time when the gateway accepted the observation. This is not
|
|
// the request time: still-valid evidence can be reaccepted after a reset.
|
|
// Identical same-sequence retries do not advance it.
|
|
string last_reported_at = 6;
|
|
}
|