mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-02 07:34:45 +08:00
* feat(service): add bearer authorization passthrough Signed-off-by: Derek Carr <decarr@redhat.com> * docs(sdk): add service authorization migration guide Signed-off-by: Derek Carr <decarr@redhat.com> * fix(server): remove stale version import Signed-off-by: Derek Carr <decarr@redhat.com> * docs(upgrade): remove service authorization SDK guide Signed-off-by: Derek Carr <decarr@redhat.com> * fix(e2e): relabel provider readiness TLS mount Signed-off-by: Derek Carr <decarr@redhat.com> * test(e2e): stabilize exposed service routing Signed-off-by: Derek Carr <decarr@redhat.com> * test(e2e): support HTTPS service routing Signed-off-by: Derek Carr <decarr@redhat.com> --------- Signed-off-by: Derek Carr <decarr@redhat.com>
3806 lines
138 KiB
Protocol Buffer
3806 lines
138 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 "google/protobuf/timestamp.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"
|
|
};
|
|
}
|
|
|
|
// Inspect the installed authority for one sandbox provider mutation.
|
|
rpc GetSandboxProviderStatus(GetSandboxProviderStatusRequest)
|
|
returns (GetSandboxProviderStatusResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "bearer"
|
|
scope: "sandbox:read"
|
|
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"
|
|
};
|
|
}
|
|
|
|
// Report installed provider state for the current ConnectSupervisor session.
|
|
// Replacing or losing that session invalidates its observations.
|
|
rpc ReportProviderReadiness(ReportProviderReadinessRequest)
|
|
returns (ReportProviderReadinessResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "sandbox"
|
|
};
|
|
}
|
|
|
|
// Register startup and acknowledge an exact validated runtime configuration.
|
|
rpc ReportSandboxConfiguration(ReportSandboxConfigurationRequest)
|
|
returns (ReportSandboxConfigurationResponse) {
|
|
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"
|
|
};
|
|
}
|
|
|
|
// Internal gateway-to-gateway relay forwarding.
|
|
//
|
|
// A gateway replica that receives a user request for a sandbox whose
|
|
// supervisor session is owned by a different replica opens this stream to the
|
|
// owner. The first frame carries PeerRelayInit; subsequent frames carry raw
|
|
// bytes in either direction. This RPC is authenticated as a gateway peer, not
|
|
// as a user or sandbox supervisor.
|
|
rpc PeerRelay(stream PeerRelayFrame) returns (stream PeerRelayFrame) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "peer"
|
|
};
|
|
}
|
|
|
|
// Internal gateway-to-owner forwarding for supervisor-bound state.
|
|
rpc PeerReportProviderReadiness(ReportProviderReadinessRequest)
|
|
returns (ReportProviderReadinessResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "peer"
|
|
};
|
|
}
|
|
|
|
rpc PeerReportEndpointStatus(ReportEndpointStatusRequest)
|
|
returns (ReportEndpointStatusResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "peer"
|
|
};
|
|
}
|
|
|
|
rpc PeerGetSandboxProviderStatus(GetSandboxProviderStatusRequest)
|
|
returns (GetSandboxProviderStatusResponse) {
|
|
option (openshell.options.v1.authorization) = {
|
|
auth_mode: "peer"
|
|
};
|
|
}
|
|
|
|
// 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 {
|
|
reserved 2;
|
|
reserved "expires_at_ms";
|
|
// Gateway-minted session JWT bound to the calling sandbox's UUID, active
|
|
// runtime generation, authorization epoch, and durable token lineage.
|
|
string token = 1 [(openshell.options.v1.secret) = true];
|
|
// Absolute expiry of the issued token. Absence means the token is non-expiring.
|
|
google.protobuf.Timestamp expiration_time = 102;
|
|
}
|
|
|
|
// 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 {
|
|
reserved 2, 5;
|
|
reserved "expires_at_ms", "sandbox_expires_at_ms";
|
|
// Fresh gateway-minted JWT bound to the same sandbox UUID.
|
|
string token = 1 [(openshell.options.v1.secret) = true];
|
|
// Absolute expiry of the new token. Absence means the token is non-expiring.
|
|
google.protobuf.Timestamp expiration_time = 102;
|
|
// Fresh credentials for the requested, policy-authorized extension
|
|
// services. These remain in supervisor memory and are never persisted.
|
|
repeated ExtensionServiceCredential extension_credentials = 3;
|
|
// Fresh Sandbox Protocol bearer token from the same atomic refresh.
|
|
string sandbox_token = 4 [(openshell.options.v1.secret) = true];
|
|
// Absolute Sandbox Protocol token expiry. Absence means the token is non-expiring.
|
|
google.protobuf.Timestamp sandbox_expiration_time = 105;
|
|
// Launch generation to which both refreshed credentials are bound.
|
|
string session_id = 6;
|
|
// Durable authorization epoch shared by the gateway and Sandbox Runtime.
|
|
uint64 credential_epoch = 7;
|
|
}
|
|
|
|
|
|
// 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;
|
|
|
|
// Negotiated non-secret metadata for every initialized extension.
|
|
repeated NegotiatedExtensionInfo extensions = 4;
|
|
}
|
|
|
|
enum ExtensionKind {
|
|
EXTENSION_KIND_UNSPECIFIED = 0;
|
|
EXTENSION_KIND_COMPUTE_DRIVER = 1;
|
|
EXTENSION_KIND_CREDENTIAL_DRIVER = 2;
|
|
EXTENSION_KIND_GATEWAY_INTERCEPTOR = 3;
|
|
EXTENSION_KIND_SUPERVISOR_MIDDLEWARE = 4;
|
|
}
|
|
|
|
// Public, non-secret snapshot of one successful startup negotiation.
|
|
message NegotiatedExtensionInfo {
|
|
ExtensionKind kind = 1;
|
|
// Gateway/operator-selected registration name.
|
|
string configured_name = 2;
|
|
// Extension-reported implementation identity.
|
|
string implementation_name = 3;
|
|
// Extension build version, distinct from the protocol version.
|
|
string implementation_version = 4;
|
|
uint32 protocol_major = 5;
|
|
uint32 protocol_minor = 6;
|
|
// Extension-supported optional capabilities, sorted for stable output.
|
|
repeated string supported_capabilities = 7;
|
|
// Capabilities the extension requires from the gateway.
|
|
repeated string required_capabilities = 8;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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;
|
|
// Gateway-owned attachment identity, changed atomically with the provider set.
|
|
// Equality only: detach and reattach must not revive an older receipt.
|
|
string provider_attachment_epoch = 14;
|
|
// Gateway-owned policy for replacing the sandbox runtime after the canonical
|
|
// main process exits. Unspecified is normalized to Never before persistence.
|
|
SandboxRestartPolicy restart_policy = 15;
|
|
}
|
|
|
|
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.
|
|
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 {
|
|
// 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;
|
|
// Most recent normalized main process result. Signal exits use 128 + signal number.
|
|
// Cleared when a replacement main process becomes ready.
|
|
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;
|
|
// Independent of infrastructure phase; retained across driver observations.
|
|
SandboxConfigurationAdmission configuration_admission = 11;
|
|
// Durable first-acceptance marker. Absent on legacy records; never reset by restart.
|
|
optional bool configuration_activated = 12;
|
|
// Gateway-owned repair window. Retained after timeout for inspection and retry.
|
|
SandboxProvisioning provisioning = 13;
|
|
// Consecutive policy-driven restart number in the current crash loop.
|
|
uint32 restart_count = 14;
|
|
// Deadline for the next restart attempt or recovery check.
|
|
google.protobuf.Timestamp next_restart_time = 15;
|
|
// Time when the current main process became ready.
|
|
google.protobuf.Timestamp main_process_started_time = 16;
|
|
}
|
|
|
|
// User-facing sandbox condition derived from platform or gateway observations.
|
|
message SandboxCondition {
|
|
reserved 5;
|
|
reserved "last_transition_time";
|
|
// 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;
|
|
// Timestamp reported by the condition owner for the last transition.
|
|
google.protobuf.Timestamp transition_time = 105;
|
|
}
|
|
|
|
// 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;
|
|
reserved 10;
|
|
reserved "SANDBOX_PHASE_RESTARTING";
|
|
}
|
|
|
|
// Public platform event exposed on the sandbox watch stream.
|
|
message PlatformEvent {
|
|
reserved 1;
|
|
reserved "timestamp_ms";
|
|
// Time when the event occurred.
|
|
google.protobuf.Timestamp event_time = 101;
|
|
// 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 {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 7;
|
|
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 = 5;
|
|
// Workspace-scoped SandboxWorkloadTemplate name to resolve at creation time.
|
|
string workload_template = 6;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 8;
|
|
// HTTP services to expose when the sandbox is created. Endpoints are
|
|
// registered after the sandbox has been persisted and route only while the
|
|
// sandbox is ready.
|
|
repeated SandboxServiceExposure service_exposures = 9;
|
|
}
|
|
|
|
message CreateSandboxTemplateRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
|
|
SandboxWorkloadTemplate template = 1;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 3;
|
|
}
|
|
|
|
message GetSandboxTemplateRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
|
|
string name = 1;
|
|
}
|
|
|
|
message ListSandboxTemplatesRequest {
|
|
// Workspace scope. Named and all-workspaces selections are accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
// 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 = 3;
|
|
}
|
|
|
|
message DeleteSandboxTemplateRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
|
|
string name = 1;
|
|
// Succeed with ALREADY_ABSENT if the target is missing. Authorization and
|
|
// parent-workspace checks still apply.
|
|
bool allow_missing = 3;
|
|
// Optional nonzero UUID. Same ID and payload replay success for 24 hours.
|
|
string request_id = 4;
|
|
}
|
|
|
|
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 {
|
|
reserved 1;
|
|
reserved "deleted";
|
|
DeletionOutcome outcome = 2;
|
|
}
|
|
|
|
// Request a gateway-owned staging slot for a local rootfs tar archive.
|
|
message BeginRootfsTarStagingRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
// 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 = 1;
|
|
// Size of the local archive in bytes, checked against the driver limit
|
|
// before the gateway allocates a slot.
|
|
uint64 size_bytes = 2;
|
|
}
|
|
|
|
// Gateway-issued staging slot.
|
|
message BeginRootfsTarStagingResponse {
|
|
reserved 4;
|
|
reserved "expires_at_ms";
|
|
// 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.
|
|
google.protobuf.Timestamp expiration_time = 104;
|
|
}
|
|
|
|
// Get sandbox request.
|
|
message GetSandboxRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
|
|
string name = 1;
|
|
}
|
|
|
|
// List sandboxes request.
|
|
message ListSandboxesRequest {
|
|
// Workspace scope. Named and all-workspaces selections are accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
// 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;
|
|
}
|
|
|
|
// List providers attached to a sandbox request.
|
|
message ListSandboxProvidersRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
|
|
string sandbox = 1;
|
|
// The maximum number of providers to return. Zero uses 100. Values above
|
|
// 1000 are coerced to 1000; negative values are invalid.
|
|
int32 page_size = 3;
|
|
// Token from a previous ListSandboxProviders response. All other request
|
|
// parameters except page_size must match the request that produced it.
|
|
string page_token = 4;
|
|
}
|
|
|
|
// Attach provider to sandbox request.
|
|
message AttachSandboxProviderRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
string sandbox = 1;
|
|
// Provider name to attach.
|
|
string provider = 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;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 5;
|
|
}
|
|
|
|
// Detach provider from sandbox request.
|
|
message DetachSandboxProviderRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
string sandbox = 1;
|
|
// Provider name to detach.
|
|
string provider = 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;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 5;
|
|
}
|
|
|
|
// Delete sandbox request.
|
|
message DeleteSandboxRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
|
|
// Canonical sandbox name.
|
|
string name = 1;
|
|
// Succeed with ALREADY_ABSENT if the target is missing. Does not wait for
|
|
// asynchronous cleanup and does not suppress authorization or parent errors.
|
|
bool allow_missing = 3;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 4;
|
|
}
|
|
|
|
// Stop sandbox request.
|
|
message StopSandboxRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
|
|
string name = 1;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 3;
|
|
}
|
|
|
|
// Start sandbox request.
|
|
message StartSandboxRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
|
|
string name = 1;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 3;
|
|
}
|
|
|
|
// Sandbox response.
|
|
message SandboxResponse {
|
|
Sandbox sandbox = 1;
|
|
// Service URLs created by CreateSandbox, keyed by service name. The empty
|
|
// key identifies the unnamed service. Other sandbox RPCs return an empty map.
|
|
map<string, string> service_urls = 2;
|
|
}
|
|
|
|
// 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;
|
|
// Token for the next page. Empty when there are no subsequent pages.
|
|
string next_page_token = 2;
|
|
}
|
|
|
|
// 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;
|
|
// Persisted intent; readiness requires current supervisor observations.
|
|
ProviderMutationReceipt receipt = 3;
|
|
}
|
|
|
|
// 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;
|
|
// Revocation is complete only when this receipt reports REVOKED.
|
|
ProviderMutationReceipt receipt = 3;
|
|
}
|
|
|
|
// Operation whose installed authority is tracked by a receipt.
|
|
enum ProviderMutationKind {
|
|
PROVIDER_MUTATION_KIND_UNSPECIFIED = 0;
|
|
PROVIDER_MUTATION_KIND_ATTACH = 1;
|
|
PROVIDER_MUTATION_KIND_DETACH = 2;
|
|
PROVIDER_MUTATION_KIND_UPDATE = 3;
|
|
// Reconstructed status for existing desired state without a mutation receipt.
|
|
PROVIDER_MUTATION_KIND_OBSERVE = 4;
|
|
}
|
|
|
|
// Readiness states describe persisted intent separately from installed state.
|
|
enum ProviderReadinessState {
|
|
PROVIDER_READINESS_STATE_UNSPECIFIED = 0;
|
|
PROVIDER_READINESS_STATE_PERSISTED = 1;
|
|
PROVIDER_READINESS_STATE_PENDING = 2;
|
|
PROVIDER_READINESS_STATE_READY = 3;
|
|
PROVIDER_READINESS_STATE_WITHHELD = 4;
|
|
PROVIDER_READINESS_STATE_REVOKED = 5;
|
|
PROVIDER_READINESS_STATE_FAILED = 6;
|
|
PROVIDER_READINESS_STATE_SUPERSEDED = 7;
|
|
}
|
|
|
|
// Closed reason categories are safe to display. Raw installation errors are
|
|
// never part of the readiness protocol.
|
|
enum ProviderReadinessReason {
|
|
PROVIDER_READINESS_REASON_UNSPECIFIED = 0;
|
|
PROVIDER_READINESS_REASON_WAITING_FOR_SUPERVISOR = 1;
|
|
PROVIDER_READINESS_REASON_WAITING_FOR_CREDENTIALS = 2;
|
|
PROVIDER_READINESS_REASON_WAITING_FOR_POLICY = 3;
|
|
PROVIDER_READINESS_REASON_WAITING_FOR_PROCESS = 4;
|
|
PROVIDER_READINESS_REASON_UNSUPPORTED_SUPERVISOR = 5;
|
|
PROVIDER_READINESS_REASON_CREDENTIALS_WITHHELD = 6;
|
|
PROVIDER_READINESS_REASON_CREDENTIAL_INSTALL_FAILED = 7;
|
|
PROVIDER_READINESS_REASON_POLICY_ACTIVATION_FAILED = 8;
|
|
PROVIDER_READINESS_REASON_PROCESS_INSTALL_FAILED = 9;
|
|
PROVIDER_READINESS_REASON_SUPERVISOR_DISCONNECTED = 10;
|
|
PROVIDER_READINESS_REASON_SUPERVISOR_LEASE_EXPIRED = 11;
|
|
PROVIDER_READINESS_REASON_DESIRED_STATE_CHANGED = 12;
|
|
PROVIDER_READINESS_REASON_CREDENTIAL_EXPIRED = 13;
|
|
PROVIDER_READINESS_REASON_LOCAL_POLICY = 14;
|
|
PROVIDER_READINESS_REASON_SNAPSHOT_MISMATCH = 15;
|
|
}
|
|
|
|
// Exact desired authority. Revisions are opaque identities, never ordered.
|
|
message ProviderDesiredIdentity {
|
|
string sandbox_id = 1;
|
|
string sandbox = 2;
|
|
string attachment_epoch = 3;
|
|
// Empty for a detached provider.
|
|
string provider_id = 4;
|
|
uint64 provider_resource_version = 5;
|
|
uint64 provider_env_revision = 6;
|
|
uint64 config_revision = 7;
|
|
string policy_hash = 8;
|
|
}
|
|
|
|
// Component whose desired state is tracked by a durable update operation.
|
|
enum ConfigComponent {
|
|
CONFIG_COMPONENT_UNSPECIFIED = 0;
|
|
CONFIG_COMPONENT_SANDBOX_CONFIG = 1;
|
|
CONFIG_COMPONENT_PROVIDER_ENVIRONMENT = 2;
|
|
}
|
|
|
|
// Identifies one component snapshot revision. Revisions are equality tokens,
|
|
// not members of one shared ordering domain.
|
|
message ConfigSnapshotRevision {
|
|
oneof component {
|
|
SandboxConfigRevision sandbox_config = 1;
|
|
uint64 provider_environment = 2;
|
|
// Complete desired authority for one sandbox-scoped provider mutation.
|
|
ProviderDesiredIdentity provider_target = 3;
|
|
}
|
|
}
|
|
|
|
// Identity needed to correlate effective sandbox configuration with the
|
|
// policy-history row whose apply status the gateway records.
|
|
message SandboxConfigRevision {
|
|
uint64 config_revision = 1;
|
|
uint32 policy_version = 2;
|
|
openshell.sandbox.v1.PolicySource policy_source = 3;
|
|
uint32 global_policy_version = 4;
|
|
// Monotonic revision of the sandbox-scoped settings row. This disambiguates
|
|
// setting operations whose effective config fingerprint is equality-only.
|
|
uint64 settings_revision = 5;
|
|
}
|
|
|
|
// Result of applying a component revision at its owning runtime boundary.
|
|
enum ConfigApplyOutcome {
|
|
CONFIG_APPLY_OUTCOME_UNSPECIFIED = 0;
|
|
CONFIG_APPLY_OUTCOME_APPLIED = 1;
|
|
CONFIG_APPLY_OUTCOME_IGNORED_DUPLICATE = 2;
|
|
CONFIG_APPLY_OUTCOME_IGNORED_STALE = 3;
|
|
CONFIG_APPLY_OUTCOME_RETAINED_LOCAL_OVERRIDE = 4;
|
|
CONFIG_APPLY_OUTCOME_DEGRADED = 5;
|
|
CONFIG_APPLY_OUTCOME_FAILED_RETAINED_LAST_KNOWN_GOOD = 6;
|
|
CONFIG_APPLY_OUTCOME_FAILED_CLOSED = 7;
|
|
CONFIG_APPLY_OUTCOME_UNSUPPORTED = 8;
|
|
}
|
|
|
|
// Durable lifecycle of one desired-state update operation.
|
|
enum ConfigUpdateOperationState {
|
|
CONFIG_UPDATE_OPERATION_STATE_UNSPECIFIED = 0;
|
|
CONFIG_UPDATE_OPERATION_STATE_PENDING = 1;
|
|
CONFIG_UPDATE_OPERATION_STATE_APPLIED = 2;
|
|
CONFIG_UPDATE_OPERATION_STATE_INACTIVE = 3;
|
|
CONFIG_UPDATE_OPERATION_STATE_FAILED = 4;
|
|
CONFIG_UPDATE_OPERATION_STATE_SUPERSEDED = 5;
|
|
CONFIG_UPDATE_OPERATION_STATE_CANCELLED = 6;
|
|
}
|
|
|
|
// Durable progress for one sandbox-scoped desired-state mutation. Snapshot
|
|
// contents and credentials are never stored in this resource.
|
|
message ConfigUpdateOperation {
|
|
string operation_id = 1;
|
|
string sandbox_id = 2;
|
|
ConfigComponent component = 3;
|
|
ConfigSnapshotRevision target_revision = 4;
|
|
ConfigUpdateOperationState state = 5;
|
|
ConfigApplyOutcome outcome = 6;
|
|
string sanitized_error = 7;
|
|
reserved 8, 9, 10;
|
|
reserved "created_at_ms", "updated_at_ms", "completed_at_ms";
|
|
google.protobuf.Timestamp created_time = 108;
|
|
google.protobuf.Timestamp updated_time = 109;
|
|
// Absent until the operation reaches a terminal state.
|
|
google.protobuf.Timestamp completed_time = 110;
|
|
}
|
|
|
|
// Immutable, secret-free record of one sandbox's intended provider mutation.
|
|
message ProviderMutationReceipt {
|
|
string receipt_id = 1;
|
|
// Shared by all sandbox receipts from one provider update.
|
|
string mutation_id = 2;
|
|
string provider = 3;
|
|
string workspace = 4;
|
|
ProviderMutationKind kind = 5;
|
|
ProviderDesiredIdentity desired = 6;
|
|
reserved 7;
|
|
reserved "persisted_at_ms";
|
|
google.protobuf.Timestamp persisted_time = 107;
|
|
}
|
|
|
|
// Installed state reported by the current supervisor. Process installation is
|
|
// acknowledged by the authenticated sandbox boundary after replacing its
|
|
// environment for future process launches.
|
|
message ProviderReadinessObservation {
|
|
// Gateway-issued identifier from the current ConnectSupervisor response.
|
|
string session_id = 1;
|
|
// Monotonic only within this connection; unrelated to revision fingerprints.
|
|
uint64 sequence = 2;
|
|
string attachment_epoch = 3;
|
|
uint64 provider_env_revision = 4;
|
|
uint64 config_revision = 5;
|
|
string policy_hash = 6;
|
|
bool credentials_installed = 7;
|
|
bool policy_active = 8;
|
|
bool launch_environment_installed = 9;
|
|
string process_instance_id = 10;
|
|
ProviderReadinessReason reason = 11;
|
|
}
|
|
|
|
// Operator view of desired and observed state; contains no credential material.
|
|
message ProviderReadinessStatus {
|
|
ProviderMutationReceipt receipt = 1;
|
|
ProviderReadinessState state = 2;
|
|
ProviderReadinessReason reason = 3;
|
|
ProviderReadinessObservation observed = 4;
|
|
string network_instance_id = 5;
|
|
reserved 6, 7;
|
|
reserved "observed_at_ms", "evaluated_at_ms";
|
|
// Absent until the current supervisor session supplies accepted evidence.
|
|
google.protobuf.Timestamp observed_time = 106;
|
|
google.protobuf.Timestamp evaluated_time = 107;
|
|
// Durable operation for the receipt, including its terminal apply outcome.
|
|
ConfigUpdateOperation operation = 8;
|
|
}
|
|
|
|
// Query an immutable receipt, or reconstruct the current desired state when
|
|
// receipt_id is empty. The sandbox identity must match the receipt.
|
|
message GetSandboxProviderStatusRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
string sandbox = 1;
|
|
string provider = 2;
|
|
string receipt_id = 3;
|
|
}
|
|
|
|
message GetSandboxProviderStatusResponse {
|
|
ProviderReadinessStatus status = 1;
|
|
}
|
|
|
|
// Installation evidence from the authenticated supervisor for this sandbox.
|
|
// A caller-provided instance identifier alone never establishes authority.
|
|
message ReportProviderReadinessRequest {
|
|
string sandbox_id = 1;
|
|
ProviderReadinessObservation observation = 2;
|
|
}
|
|
|
|
// Acknowledges accepted evidence without granting a separate session authority.
|
|
// Identical retries do not extend the evidence's original acceptance time.
|
|
message ReportProviderReadinessResponse {
|
|
uint64 accepted_sequence = 1;
|
|
reserved 2, 3;
|
|
reserved "report_interval_seconds", "observation_ttl_seconds";
|
|
google.protobuf.Duration report_interval = 102;
|
|
google.protobuf.Duration observation_ttl = 103;
|
|
}
|
|
|
|
// Delete sandbox response.
|
|
message DeleteSandboxResponse {
|
|
reserved 1;
|
|
reserved "deleted";
|
|
DeletionOutcome outcome = 2;
|
|
// Immutable identity of the targeted sandbox, empty for ALREADY_ABSENT.
|
|
// A same-name replacement is not part of this deletion.
|
|
string sandbox_id = 3;
|
|
}
|
|
|
|
// Create SSH session request.
|
|
message CreateSshSessionRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
string sandbox = 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 {
|
|
reserved 8;
|
|
reserved "expires_at_ms";
|
|
// 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;
|
|
|
|
// Absolute expiry. Absence means no expiry.
|
|
google.protobuf.Timestamp expiration_time = 108;
|
|
}
|
|
|
|
// Request to expose an HTTP service running inside a sandbox.
|
|
message ExposeServiceRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 5;
|
|
// Service name within the sandbox.
|
|
string name = 2;
|
|
// Loopback TCP port inside the sandbox.
|
|
uint32 target_port = 3;
|
|
// Whether to print/use the browser-facing service URL.
|
|
bool domain = 4;
|
|
string sandbox = 1;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 6;
|
|
// Application authorization behavior. Omission resolves to STRIP.
|
|
ServiceAuthorizationMode authorization_mode = 7;
|
|
}
|
|
|
|
// Request to fetch an exposed sandbox service endpoint.
|
|
message GetServiceRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
// Service name within the sandbox. Empty selects the unnamed endpoint.
|
|
string name = 2;
|
|
string sandbox = 1;
|
|
}
|
|
|
|
// Request to list exposed sandbox service endpoints.
|
|
message ListServicesRequest {
|
|
// Workspace scope. Named and all-workspaces selections are accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
// 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;
|
|
// Optional sandbox name. Empty lists endpoints for all sandboxes.
|
|
string sandbox = 1;
|
|
}
|
|
|
|
// 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 {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
// Service name within the sandbox. Empty selects the unnamed endpoint.
|
|
string name = 2;
|
|
string sandbox = 1;
|
|
bool allow_missing = 4;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 5;
|
|
}
|
|
|
|
// Response for deleting an exposed sandbox service endpoint.
|
|
message DeleteServiceResponse {
|
|
reserved 1;
|
|
reserved "deleted";
|
|
DeletionOutcome outcome = 2;
|
|
}
|
|
|
|
// 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 = 3;
|
|
// Service name within the sandbox.
|
|
string 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;
|
|
// Effective application authorization behavior for ingress requests.
|
|
ServiceAuthorizationMode authorization_mode = 7;
|
|
}
|
|
|
|
// 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];
|
|
// A missing token is NOT_FOUND unless this is true. Revoking an existing,
|
|
// already-revoked session succeeds with COMPLETED.
|
|
bool allow_missing = 2;
|
|
}
|
|
|
|
// Revoke SSH session response.
|
|
message RevokeSshSessionResponse {
|
|
reserved 1;
|
|
reserved "revoked";
|
|
DeletionOutcome outcome = 2;
|
|
}
|
|
|
|
// Execute command request.
|
|
message ExecSandboxRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 12;
|
|
reserved 5;
|
|
reserved "timeout_seconds";
|
|
// Canonical sandbox name.
|
|
string sandbox = 1;
|
|
// Command and arguments.
|
|
repeated string command = 2;
|
|
|
|
// Optional working directory.
|
|
string workdir = 3;
|
|
|
|
// Optional environment overrides.
|
|
map<string, string> environment = 4;
|
|
|
|
// Optional execution timeout. Absence means no timeout.
|
|
google.protobuf.Duration execution_timeout = 105;
|
|
|
|
// 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;
|
|
|
|
// Optional nonzero UUID for durable launch admission. Also applies to the
|
|
// initial ExecSandboxInteractive start message. Duplicates never relaunch,
|
|
// reattach, or replay output/stdin. Unconfirmed executions remain fenced;
|
|
// confirmed terminal executions retain the fence for 24 hours.
|
|
string request_id = 11;
|
|
}
|
|
|
|
// 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 {
|
|
string sandbox = 1;
|
|
string workspace = 3;
|
|
// 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 {
|
|
reserved 4;
|
|
reserved "expires_at_ms";
|
|
// 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];
|
|
|
|
// Absolute expiry. Absence means no expiry.
|
|
google.protobuf.Timestamp expiration_time = 104;
|
|
|
|
// Revoked flag.
|
|
bool revoked = 5;
|
|
}
|
|
|
|
// Watch sandbox request.
|
|
message WatchSandboxRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 11;
|
|
reserved 8;
|
|
reserved "log_since_ms";
|
|
// Canonical sandbox name.
|
|
string sandbox = 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 at or after this time. Absence means no time filter.
|
|
// Applies to both tail replay and live streaming.
|
|
google.protobuf.Timestamp since_time = 108;
|
|
|
|
// 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;
|
|
|
|
// Resume streaming after this cursor. Empty means no cursor resume: the
|
|
// server falls back to tail-limited replay controlled by log_tail_lines and
|
|
// event_tail. Otherwise set it to the highest `SandboxStreamEvent.cursor`
|
|
// already processed; the server replays only log and platform events after
|
|
// it, merged in cursor order, before resuming live delivery. If the requested
|
|
// cursor has already been trimmed from the server's buffer, the resume is
|
|
// unrecoverable and the stream terminates with OUT_OF_RANGE (see
|
|
// SandboxStreamWarning for the recoverable case).
|
|
//
|
|
// A cursor is bound to the cursor space that issued it. A gateway restart,
|
|
// teardown of the sandbox's buffers, or a reconnect to a different gateway
|
|
// replica starts a new space, and cursors from the previous one are rejected
|
|
// with OUT_OF_RANGE rather than silently suppressing live events beneath
|
|
// them. OUT_OF_RANGE is terminal for that cursor: restart the watch with an
|
|
// empty resume_after_cursor, because retrying the same token fails
|
|
// identically. A cursor this server could not have issued is rejected with
|
|
// INVALID_ARGUMENT.
|
|
string resume_after_cursor = 12;
|
|
}
|
|
|
|
// 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;
|
|
// Recoverable warning from the server, e.g. messages dropped because a
|
|
// broadcast receiver lagged. The stream continues after this warning; the
|
|
// client can detect the gap from the warning itself.
|
|
SandboxStreamWarning warning = 4;
|
|
// Draft policy update notification.
|
|
DraftPolicyUpdate draft_policy_update = 5;
|
|
}
|
|
// Opaque position in this sandbox's cursor space, shared across the resumable
|
|
// log and platform event sources. Empty for non-resumable events (status
|
|
// snapshots, warnings).
|
|
//
|
|
// Do not parse this token; its encoding is not part of the contract. The only
|
|
// supported operation is comparing two non-empty cursors observed on the same
|
|
// stream and keeping the greater one, then passing it as
|
|
// WatchSandboxRequest.resume_after_cursor to resume without loss or
|
|
// duplication. That comparison is a plain byte-wise string comparison. It is
|
|
// well defined only within one stream: a stream never spans two cursor
|
|
// spaces, because a reset ends it.
|
|
string cursor = 6;
|
|
}
|
|
|
|
// Log line correlated to a sandbox.
|
|
message SandboxLogLine {
|
|
reserved 2;
|
|
reserved "timestamp_ms";
|
|
string sandbox_id = 1;
|
|
google.protobuf.Timestamp event_time = 102;
|
|
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;
|
|
}
|
|
|
|
// Recoverable loss notification on a watch stream. Emitted when the server
|
|
// skips ahead after a broadcast lag instead of terminating; the stream keeps
|
|
// running. Cursors are opaque, so this message is the only signal that events
|
|
// were skipped. Unrecoverable loss (a trimmed or foreign resume cursor) is
|
|
// reported as an OUT_OF_RANGE stream status, not this message.
|
|
message SandboxStreamWarning {
|
|
string message = 1;
|
|
}
|
|
|
|
// Create provider request.
|
|
message CreateProviderRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
|
|
openshell.datamodel.v1.Provider provider = 1;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 3;
|
|
}
|
|
|
|
// Get provider request.
|
|
message GetProviderRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
|
|
string name = 1;
|
|
}
|
|
|
|
// List providers request.
|
|
message ListProvidersRequest {
|
|
// Workspace scope. Named and all-workspaces selections are accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
// 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;
|
|
}
|
|
|
|
// Update provider request.
|
|
message UpdateProviderRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
reserved 2;
|
|
reserved "credential_expires_at_ms";
|
|
openshell.datamodel.v1.Provider provider = 1;
|
|
// Optional per-credential expiry timestamps to merge into the provider.
|
|
// Omitted keys are unchanged. Use clear_credential_expiration_keys to remove
|
|
// an existing expiry.
|
|
map<string, google.protobuf.Timestamp> credential_expiration_times = 102;
|
|
// Credential keys whose existing expiry should be removed.
|
|
repeated string clear_credential_expiration_keys = 103;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 4;
|
|
}
|
|
|
|
// Delete provider request.
|
|
message DeleteProviderRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
string name = 1;
|
|
bool allow_missing = 4;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 5;
|
|
}
|
|
|
|
// Provider response.
|
|
message ProviderResponse {
|
|
openshell.datamodel.v1.Provider provider = 1;
|
|
// Selection-time sandbox target set for an update, with one receipt per target.
|
|
// Sandboxes attached later are outside this operation's readiness result.
|
|
repeated ProviderMutationReceipt target_receipts = 2;
|
|
// Identifies the update even when its target set is empty.
|
|
string mutation_id = 3;
|
|
}
|
|
|
|
// 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 {
|
|
// Workspace scope. Omit for platform profiles; otherwise select one named workspace.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
// 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;
|
|
}
|
|
|
|
// Fetch provider type profile request.
|
|
message GetProviderProfileRequest {
|
|
// Workspace scope. Omit for platform profiles; otherwise select one named workspace.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
|
|
string id = 1;
|
|
}
|
|
|
|
// 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 {
|
|
reserved 4;
|
|
reserved "cache_ttl_seconds";
|
|
// 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 token cache TTL override. If absent, use expires_in from the token response.
|
|
google.protobuf.Duration cache_ttl = 104;
|
|
|
|
// 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 {
|
|
reserved 4, 5;
|
|
reserved "refresh_before_seconds", "max_lifetime_seconds";
|
|
ProviderCredentialRefreshStrategy strategy = 1;
|
|
string token_url = 2;
|
|
repeated string scopes = 3;
|
|
google.protobuf.Duration refresh_before = 104;
|
|
google.protobuf.Duration max_lifetime = 105;
|
|
repeated ProviderCredentialRefreshMaterial material = 6;
|
|
repeated ProviderCredentialRefreshOutput additional_outputs = 7;
|
|
}
|
|
|
|
message ProviderCredentialRefreshStatus {
|
|
reserved 6, 7, 8, 13;
|
|
reserved "expires_at_ms", "next_refresh_at_ms", "last_refresh_at_ms", "last_error_at_ms";
|
|
string provider = 1;
|
|
string provider_id = 2;
|
|
string credential_key = 3;
|
|
ProviderCredentialRefreshStrategy strategy = 4;
|
|
string status = 5;
|
|
google.protobuf.Timestamp expiration_time = 106;
|
|
// Next automatic refresh time. Absence means no automatic retry is scheduled;
|
|
// use recovery_action to determine the required recovery workflow.
|
|
google.protobuf.Timestamp next_refresh_time = 107;
|
|
google.protobuf.Timestamp last_refresh_time = 108;
|
|
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;
|
|
google.protobuf.Timestamp last_error_time = 113;
|
|
}
|
|
|
|
// Provider profile local discovery declaration.
|
|
message ProviderProfileDiscovery {
|
|
// Credential names from ProviderProfile.credentials eligible for local discovery.
|
|
repeated string credentials = 1;
|
|
}
|
|
|
|
message GetProviderRefreshStatusRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
string provider = 1;
|
|
string credential_key = 2;
|
|
}
|
|
|
|
message GetProviderRefreshStatusResponse {
|
|
repeated ProviderCredentialRefreshStatus credentials = 1;
|
|
}
|
|
|
|
message ConfigureProviderRefreshRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 7;
|
|
reserved 6;
|
|
reserved "expires_at_ms";
|
|
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;
|
|
google.protobuf.Timestamp expiration_time = 106;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 8;
|
|
}
|
|
|
|
message ConfigureProviderRefreshResponse {
|
|
ProviderCredentialRefreshStatus status = 1;
|
|
}
|
|
|
|
message RotateProviderCredentialRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
string provider = 1;
|
|
string credential_key = 2;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 4;
|
|
}
|
|
|
|
message RotateProviderCredentialResponse {
|
|
ProviderCredentialRefreshStatus status = 1;
|
|
}
|
|
|
|
message DeleteProviderRefreshRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
string provider = 1;
|
|
string credential_key = 2;
|
|
bool allow_missing = 5;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 6;
|
|
}
|
|
|
|
message DeleteProviderRefreshResponse {
|
|
reserved 1;
|
|
reserved "deleted";
|
|
DeletionOutcome outcome = 2;
|
|
}
|
|
|
|
// 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;
|
|
// EXPERIMENTAL: Non-secret files rendered from provider configuration for the
|
|
// workload. This API and its behavior may change or be removed.
|
|
repeated ProviderProfileFile files = 14;
|
|
}
|
|
|
|
// EXPERIMENTAL: Provider file templates may change or be removed.
|
|
message ProviderProfileFile {
|
|
// One virtual file name below /run/openshell/providers/<provider>/.
|
|
string path = 1;
|
|
// UTF-8 template. Only {{config.KEY}} references are supported.
|
|
string content = 2;
|
|
// Optional environment variable containing the virtual absolute path.
|
|
string env_var = 3;
|
|
}
|
|
|
|
// 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 {
|
|
// Workspace scope. Omit for platform profiles; otherwise select one named workspace.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
|
|
repeated ProviderProfileImportItem profiles = 1;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 3;
|
|
}
|
|
|
|
// 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 {
|
|
// Workspace scope. Omit for platform profiles; otherwise select one named workspace.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
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;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 5;
|
|
}
|
|
|
|
// Update one custom provider profile response.
|
|
message UpdateProviderProfilesResponse {
|
|
repeated ProviderProfileDiagnostic diagnostics = 1;
|
|
ProviderProfile profile = 2;
|
|
bool updated = 3;
|
|
}
|
|
|
|
// Lint provider profiles request.
|
|
message LintProviderProfilesRequest {
|
|
// Workspace scope. Omit for platform profiles; otherwise select one named workspace.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
|
|
repeated ProviderProfileImportItem profiles = 1;
|
|
}
|
|
|
|
// Lint provider profiles response.
|
|
message LintProviderProfilesResponse {
|
|
repeated ProviderProfileDiagnostic diagnostics = 1;
|
|
bool valid = 2;
|
|
}
|
|
|
|
// Delete provider response.
|
|
message DeleteProviderResponse {
|
|
reserved 1;
|
|
reserved "deleted";
|
|
DeletionOutcome outcome = 2;
|
|
}
|
|
|
|
// Delete custom provider profile request.
|
|
message DeleteProviderProfileRequest {
|
|
// Workspace scope. Omit for platform profiles; otherwise select one named workspace.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
|
|
string id = 1;
|
|
bool allow_missing = 3;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 4;
|
|
}
|
|
|
|
// Delete custom provider profile response.
|
|
message DeleteProviderProfileResponse {
|
|
reserved 1;
|
|
reserved "deleted";
|
|
DeletionOutcome outcome = 2;
|
|
}
|
|
|
|
// 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 {
|
|
reserved 3;
|
|
reserved "credential_expires_at_ms";
|
|
// 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, google.protobuf.Timestamp> credential_expiration_times = 103;
|
|
// 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;
|
|
// Attachment identity captured with the returned provider records.
|
|
string provider_attachment_epoch = 7;
|
|
// Effective policy identity used to derive this snapshot's endpoint bindings.
|
|
string policy_hash = 8;
|
|
// Nonzero when material was withheld; installing an empty map is not readiness.
|
|
ProviderReadinessReason readiness_reason = 9;
|
|
// Complete desired set of non-secret managed files, keyed by absolute path.
|
|
map<string, string> files = 10;
|
|
}
|
|
|
|
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 {
|
|
reserved 2;
|
|
reserved "expires_in";
|
|
string access_token = 1 [(openshell.options.v1.secret) = true];
|
|
google.protobuf.Duration expires_after = 102;
|
|
string token_type = 3;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Policy update messages
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Update sandbox policy request.
|
|
message UpdateConfigRequest {
|
|
// Workspace scope. Omit for global updates; otherwise select one named workspace.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 10;
|
|
// 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;
|
|
// Required for sandbox-scoped updates and empty for global updates.
|
|
string sandbox = 1;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 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;
|
|
}
|
|
|
|
// Exact endpoint and complete authorization scope affected by an L7 append.
|
|
// All ports and binaries must match the stored target; omitted scope is invalid.
|
|
message L7RuleTarget {
|
|
string rule_name = 1;
|
|
string host = 2;
|
|
repeated uint32 ports = 3;
|
|
// An absent path requires a unique endpoint. An empty path selects an
|
|
// endpoint without a path selector. This is not the appended request path.
|
|
optional string path = 4;
|
|
// Declare either a nonempty binary list or any_binary, never both.
|
|
repeated openshell.sandbox.v1.NetworkBinary binaries = 5;
|
|
bool any_binary = 6;
|
|
}
|
|
|
|
message AddDenyRules {
|
|
reserved 1, 2;
|
|
reserved "host", "port";
|
|
repeated openshell.sandbox.v1.L7DenyRule deny_rules = 3;
|
|
L7RuleTarget target = 4;
|
|
}
|
|
|
|
message AddAllowRules {
|
|
reserved 1, 2;
|
|
reserved "host", "port";
|
|
repeated openshell.sandbox.v1.L7Rule rules = 3;
|
|
L7RuleTarget target = 4;
|
|
}
|
|
|
|
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 {
|
|
// Workspace scope. Omit for global queries; otherwise select one named workspace.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
// 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;
|
|
string sandbox = 1;
|
|
}
|
|
|
|
// 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 {
|
|
// Workspace scope. Omit for global queries; otherwise select one named workspace.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 5;
|
|
// 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;
|
|
string sandbox = 1;
|
|
}
|
|
|
|
// 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 {}
|
|
|
|
enum ConfigurationAdmissionState {
|
|
CONFIGURATION_ADMISSION_STATE_UNSPECIFIED = 0;
|
|
CONFIGURATION_ADMISSION_STATE_PENDING = 1;
|
|
CONFIGURATION_ADMISSION_STATE_ACCEPTED = 2;
|
|
CONFIGURATION_ADMISSION_STATE_REJECTED = 3;
|
|
}
|
|
|
|
message SandboxConfigurationAdmission {
|
|
string instance_id = 1;
|
|
ConfigurationAdmissionState state = 2;
|
|
uint32 policy_version = 3;
|
|
string policy_hash = 4;
|
|
uint64 config_revision = 5;
|
|
uint64 provider_env_revision = 6;
|
|
string error = 7;
|
|
}
|
|
|
|
message ReportSandboxConfigurationRequest {
|
|
string sandbox_id = 1;
|
|
SandboxConfigurationAdmission admission = 2;
|
|
// Pending registration replaces only this previously observed instance.
|
|
string expected_instance_id = 3;
|
|
}
|
|
|
|
message ReportSandboxConfigurationResponse {}
|
|
|
|
// A versioned policy revision with metadata.
|
|
message SandboxPolicyRevision {
|
|
reserved 5, 6;
|
|
reserved "created_at_ms", "loaded_at_ms";
|
|
// 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;
|
|
// Time when this revision was created.
|
|
google.protobuf.Timestamp created_time = 105;
|
|
// Time when this revision was loaded by the sandbox. Absent if not loaded.
|
|
google.protobuf.Timestamp loaded_time = 106;
|
|
// 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 {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 6;
|
|
reserved 3;
|
|
reserved "since_ms";
|
|
// Canonical sandbox name.
|
|
string sandbox = 1;
|
|
// Maximum number of log lines to return. 0 means use default (2000).
|
|
uint32 lines = 2;
|
|
// Only include logs at or after this time. Absence means no filter.
|
|
google.protobuf.Timestamp since_time = 103;
|
|
// 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;
|
|
}
|
|
|
|
// 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;
|
|
// Monotonic counter scoped to instance_id. Incremented for each reconnect so
|
|
// gateways can distinguish a fresh supervisor connection from stale cleanup.
|
|
uint64 connection_epoch = 3;
|
|
// The supervisor can report credential, policy, and launch-environment installation.
|
|
bool supports_provider_readiness = 4;
|
|
}
|
|
|
|
// Gateway accepts the supervisor session.
|
|
message SessionAccepted {
|
|
reserved 2;
|
|
reserved "heartbeat_interval_secs";
|
|
// Gateway-assigned session ID for this connection.
|
|
string session_id = 1;
|
|
// Recommended heartbeat interval.
|
|
google.protobuf.Duration heartbeat_interval = 102;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// Initial frame for gateway peer relay forwarding.
|
|
message PeerRelayInit {
|
|
// Stable sandbox UUID whose supervisor relay should be opened.
|
|
string sandbox_id = 1;
|
|
// Relay target to ask the owning gateway to open on its local supervisor
|
|
// session. The channel_id is assigned by the forwarding gateway.
|
|
RelayOpen relay_open = 2;
|
|
// Gateway replica id that initiated the peer relay.
|
|
string requester_replica_id = 3;
|
|
}
|
|
|
|
// A single frame on the gateway-to-gateway peer relay RPC.
|
|
message PeerRelayFrame {
|
|
oneof payload {
|
|
PeerRelayInit 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 {
|
|
reserved 7, 8;
|
|
reserved "first_seen_ms", "last_seen_ms";
|
|
// 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;
|
|
// Time of the first denial.
|
|
google.protobuf.Timestamp first_seen_time = 107;
|
|
// Time of the most recent denial.
|
|
google.protobuf.Timestamp last_seen_time = 108;
|
|
// 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 {
|
|
reserved 9, 10, 14, 15;
|
|
reserved "created_at_ms", "decided_at_ms", "first_seen_ms", "last_seen_ms";
|
|
// 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;
|
|
// Time when this chunk was created.
|
|
google.protobuf.Timestamp created_time = 109;
|
|
// Time when the user approved or rejected the chunk. Absent if undecided.
|
|
google.protobuf.Timestamp decided_time = 110;
|
|
// 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.
|
|
google.protobuf.Timestamp first_seen_time = 114;
|
|
// Most recent time this endpoint was proposed again.
|
|
google.protobuf.Timestamp last_seen_time = 115;
|
|
// 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 {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 6;
|
|
// 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. The authenticated sandbox principal remains authoritative
|
|
// for this internal callback.
|
|
string name = 4;
|
|
// Anonymous network activity counters.
|
|
repeated NetworkActivitySummary network_activity_summaries = 5;
|
|
}
|
|
|
|
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 {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
// Optional status filter: "pending", "approved", "rejected", or "" for all.
|
|
string status_filter = 2;
|
|
string sandbox = 1;
|
|
}
|
|
|
|
message GetDraftPolicyResponse {
|
|
reserved 4;
|
|
reserved "last_analyzed_at_ms";
|
|
// 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;
|
|
// Time when the last analysis completed.
|
|
google.protobuf.Timestamp last_analyzed_time = 104;
|
|
}
|
|
|
|
// Approve a single draft chunk.
|
|
message ApproveDraftChunkRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
// 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;
|
|
string sandbox = 1;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 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 {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
// Chunk ID to reject.
|
|
string chunk_id = 2;
|
|
// Optional reason for rejection (fed to LLM context in future analysis).
|
|
string reason = 3;
|
|
string sandbox = 1;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 5;
|
|
}
|
|
|
|
message RejectDraftChunkResponse {}
|
|
|
|
// Approve all pending chunks.
|
|
message DraftChunkApproval {
|
|
string chunk_id = 1;
|
|
string review_token = 2;
|
|
}
|
|
|
|
message ApproveAllDraftChunksRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
// 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 = 3;
|
|
string sandbox = 1;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 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 {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 4;
|
|
// Chunk ID to edit.
|
|
string chunk_id = 2;
|
|
// The modified rule (replaces existing proposed_rule).
|
|
openshell.sandbox.v1.NetworkPolicyRule proposed_rule = 3;
|
|
string sandbox = 1;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 5;
|
|
}
|
|
|
|
message EditDraftChunkResponse {}
|
|
|
|
// Reverse an approval (remove merged rule from active policy).
|
|
message UndoDraftChunkRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 3;
|
|
// Chunk ID to undo.
|
|
string chunk_id = 2;
|
|
string sandbox = 1;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 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 {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
|
|
string sandbox = 1;
|
|
// Optional nonzero UUID for durable at-most-once admission. Successful results
|
|
// can be replayed for 24 hours; see the API errors and retries reference.
|
|
string request_id = 3;
|
|
}
|
|
|
|
message ClearDraftChunksResponse {
|
|
// Number of chunks cleared.
|
|
uint32 chunks_cleared = 1;
|
|
}
|
|
|
|
// Get decision history for a sandbox's draft policy.
|
|
message GetDraftHistoryRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
|
|
string sandbox = 1;
|
|
}
|
|
|
|
message DraftHistoryEntry {
|
|
reserved 1;
|
|
reserved "timestamp_ms";
|
|
// Time when the event occurred.
|
|
google.protobuf.Timestamp event_time = 101;
|
|
// 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;
|
|
// Optional nonzero UUID. Same ID and payload replay success for 24 hours.
|
|
string request_id = 3;
|
|
}
|
|
|
|
// 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;
|
|
bool allow_missing = 2;
|
|
// Optional nonzero UUID. Same ID and payload replay success for 24 hours.
|
|
string request_id = 3;
|
|
}
|
|
|
|
// Delete workspace response.
|
|
message DeleteWorkspaceResponse {
|
|
reserved 1;
|
|
reserved "deleted";
|
|
DeletionOutcome outcome = 2;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 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;
|
|
}
|
|
|
|
// Policy applied when the canonical main process exits outside an intentional
|
|
// stop or delete operation. Kept after existing enums so generated enum
|
|
// descriptors remain stable.
|
|
enum SandboxRestartPolicy {
|
|
SANDBOX_RESTART_POLICY_UNSPECIFIED = 0;
|
|
SANDBOX_RESTART_POLICY_NEVER = 1;
|
|
SANDBOX_RESTART_POLICY_ON_FAILURE = 2;
|
|
SANDBOX_RESTART_POLICY_ALWAYS = 3;
|
|
}
|
|
|
|
// 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 scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 1;
|
|
// OIDC subject claim identifying the principal.
|
|
string principal_subject = 2;
|
|
// Role to assign.
|
|
WorkspaceRole role = 3;
|
|
// Optional nonzero UUID. Same ID and payload replay success for 24 hours.
|
|
string request_id = 4;
|
|
}
|
|
|
|
// Add workspace member response.
|
|
message AddWorkspaceMemberResponse {
|
|
WorkspaceMember member = 1;
|
|
}
|
|
|
|
// Remove workspace member request.
|
|
message RemoveWorkspaceMemberRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 1;
|
|
// OIDC subject claim identifying the principal to remove.
|
|
string principal_subject = 2;
|
|
bool allow_missing = 3;
|
|
// Optional nonzero UUID. Same ID and payload replay success for 24 hours.
|
|
string request_id = 4;
|
|
}
|
|
|
|
// Remove workspace member response.
|
|
message RemoveWorkspaceMemberResponse {
|
|
reserved 1;
|
|
reserved "removed";
|
|
DeletionOutcome outcome = 2;
|
|
}
|
|
|
|
// Result of a public delete, membership removal, or session revocation.
|
|
// Default requests return NOT_FOUND for a missing target. With allow_missing,
|
|
// only a missing target becomes ALREADY_ABSENT; parent lookup, authorization,
|
|
// validation, precondition, and backend errors retain their normal status.
|
|
// These results describe the targeted resource, not a same-name replacement.
|
|
enum DeletionOutcome {
|
|
// No outcome was supplied. Never infer completion from this value.
|
|
DELETION_OUTCOME_UNSPECIFIED = 0;
|
|
// The targeted gateway resource is removed (or the SSH session is revoked).
|
|
// Downstream platform garbage collection may still be finishing.
|
|
DELETION_OUTCOME_COMPLETED = 1;
|
|
// Sandbox deletion is accepted but its gateway record still exists.
|
|
// Observe the targeted sandbox ID until it disappears for completion.
|
|
DELETION_OUTCOME_ACCEPTED = 2;
|
|
// The target did not exist and allow_missing was true.
|
|
DELETION_OUTCOME_ALREADY_ABSENT = 3;
|
|
}
|
|
|
|
// List workspace members request.
|
|
message ListWorkspaceMembersRequest {
|
|
// Workspace scope. Only a named workspace selection is accepted.
|
|
openshell.datamodel.v1.WorkspaceSelector workspace_scope = 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 {
|
|
reserved 3;
|
|
reserved "expires_at_ms";
|
|
// 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.
|
|
google.protobuf.Timestamp expiration_time = 103;
|
|
}
|
|
|
|
// 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 {
|
|
reserved 6;
|
|
reserved "last_reported_at";
|
|
// 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;
|
|
// 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. Absent until a result is reported.
|
|
google.protobuf.Timestamp last_reported_time = 106;
|
|
}
|
|
|
|
// Durable provisioning attempt, independent of supervisor registration and polling.
|
|
message SandboxProvisioning {
|
|
string attempt_id = 1;
|
|
string configuration_change_id = 2;
|
|
google.protobuf.Timestamp configuration_change_time = 3;
|
|
google.protobuf.Timestamp first_rejection_time = 4;
|
|
// Present only while the repair window is armed.
|
|
google.protobuf.Timestamp deadline = 5;
|
|
google.protobuf.Timestamp timeout_time = 6;
|
|
// Set only after both supervisor and workload compute have been reclaimed.
|
|
google.protobuf.Timestamp cleanup_completed_time = 7;
|
|
// A safe gateway-authored diagnostic; never a raw driver error.
|
|
string cleanup_error = 8;
|
|
// Durable backoff for interrupted or failed reclamation.
|
|
google.protobuf.Timestamp cleanup_retry_time = 9;
|
|
// Attachment edits have their own durable clock; status writes do not change it.
|
|
string attachment_change_id = 10;
|
|
google.protobuf.Timestamp attachment_change_time = 11;
|
|
}
|
|
|
|
// Create-time request to expose one loopback HTTP service in a sandbox.
|
|
message SandboxServiceExposure {
|
|
// Service name within the sandbox. Empty selects the unnamed endpoint.
|
|
string service = 1;
|
|
// Loopback TCP port inside the sandbox.
|
|
uint32 target_port = 2;
|
|
// Application authorization behavior. Omission resolves to STRIP.
|
|
ServiceAuthorizationMode authorization_mode = 3;
|
|
}
|
|
|
|
// Controls whether an exposed service receives the request's application
|
|
// Authorization header.
|
|
enum ServiceAuthorizationMode {
|
|
// Omission preserves the secure legacy behavior and resolves to STRIP.
|
|
SERVICE_AUTHORIZATION_MODE_UNSPECIFIED = 0;
|
|
// Remove Authorization before forwarding to the sandbox service.
|
|
SERVICE_AUTHORIZATION_MODE_STRIP = 1;
|
|
// Forward one syntactically valid bearer Authorization header unchanged.
|
|
SERVICE_AUTHORIZATION_MODE_BEARER_PASSTHROUGH = 2;
|
|
}
|