Skip to content

Needs a live run: OpenShell v0.1.2 → v0.1.6, interceptor stubs regenerated - #9

Open
github-actions[bot] wants to merge 1 commit into
mainfrom
openshell/v0.1.6
Open

github-actions[bot] wants to merge 1 commit into
mainfrom
openshell/v0.1.6

Conversation

@github-actions

@github-actions github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Needs a live run before merging. Merging means upgrading the gateway to v0.1.6 first.

Proposed by the openshell-release workflow. It regenerated interceptor/proto, interceptor/_gen and the hash locks from OpenShell v0.1.6. The main suite FAILS, and the interceptor suite passes. Neither suite runs OpenShell.

Review the new proto fields on the mount path (CreateSandboxRequest, SandboxSpec, SandboxTemplate, and the Docker driver's mount schema): the rule's allowlists (R2, R3, R6) refuse a field they do not know until it is reviewed.

Changes under interceptor/

 interceptor/_gen/datamodel_pb2.py                |   10 +-
 interceptor/_gen/datamodel_pb2_grpc.py           |   10 +-
 interceptor/_gen/extension_pb2.py                |   10 +-
 interceptor/_gen/extension_pb2_grpc.py           |   10 +-
 interceptor/_gen/gateway_interceptor_pb2.py      |   10 +-
 interceptor/_gen/gateway_interceptor_pb2_grpc.py |   10 +-
 interceptor/_gen/openshell_pb2.py                | 1106 ++++++++++++----------
 interceptor/_gen/openshell_pb2_grpc.py           |  206 +++-
 interceptor/_gen/options_pb2.py                  |   10 +-
 interceptor/_gen/options_pb2_grpc.py             |   10 +-
 interceptor/_gen/sandbox_pb2.py                  |  128 +--
 interceptor/_gen/sandbox_pb2_grpc.py             |   10 +-
 interceptor/proto/SOURCE                         |   12 +-
 interceptor/proto/gateway_interceptor.proto      |    2 +
 interceptor/proto/openshell.proto                |  456 ++++++++-
 interceptor/proto/options.proto                  |    3 +-
 interceptor/proto/sandbox.proto                  |   64 +-
 17 files changed, 1410 insertions(+), 657 deletions(-)

The protos' diff

diff --git a/interceptor/proto/gateway_interceptor.proto b/interceptor/proto/gateway_interceptor.proto
index 1bd12d9..cf55619 100644
--- a/interceptor/proto/gateway_interceptor.proto
+++ b/interceptor/proto/gateway_interceptor.proto
@@ -111,6 +111,8 @@ message InterceptorManifest {
   // post-authentication consistency check.
   string expected_audience = 5;
   // Gateway-interceptor protocol metadata. Required for negotiation.
+  // Declare protocol 1.0 and list "openshell.gateway-interceptor.contract" in
+  // supported_capabilities.
   openshell.extension.v1.PeerMetadata extension = 6;
 }
 
diff --git a/interceptor/proto/openshell.proto b/interceptor/proto/openshell.proto
index a836e16..1be9dc7 100644
--- a/interceptor/proto/openshell.proto
+++ b/interceptor/proto/openshell.proto
@@ -195,6 +195,26 @@ service OpenShell {
     };
   }
 
+  // Freeze a ready sandbox and retain its memory state for a later resume.
+  // Requires a compute driver and isolation backend that support state
+  // retention.
+  rpc SuspendSandbox(SuspendSandboxRequest) returns (SandboxResponse) {
+    option (openshell.options.v1.authorization) = {
+      auth_mode: "bearer"
+      scope: "sandbox:write"
+      workspace_role: "user"
+    };
+  }
+
+  // Restore a suspended sandbox in a new runtime generation.
+  rpc ResumeSandbox(ResumeSandboxRequest) 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) = {
@@ -296,6 +316,13 @@ service OpenShell {
     };
   }
 
+  // Export selected usable runtime credentials. Only directly authenticated
+  // mTLS operators may call this method; refresh material is never returned.
+  rpc GetProviderCredentials(GetProviderCredentialsRequest)
+      returns (GetProviderCredentialsResponse) {
+    option (openshell.options.v1.authorization) = { auth_mode: "operator" };
+  }
+
   // List providers.
   rpc ListProviders(ListProvidersRequest) returns (ListProvidersResponse) {
     option (openshell.options.v1.authorization) = {
@@ -423,7 +450,9 @@ service OpenShell {
     };
   }
 
-  // Get sandbox settings by id (called by sandbox entrypoint and poll loop).
+  // Get sandbox settings by id (called by users, and by supervisors at startup
+  // and in their poll loop). A supervisor whose session pushes configuration
+  // receives later updates over ConnectSupervisor and stops polling.
   rpc GetSandboxConfig(openshell.sandbox.v1.GetSandboxConfigRequest)
       returns (openshell.sandbox.v1.GetSandboxConfigResponse) {
     option (openshell.options.v1.authorization) = {
@@ -509,7 +538,9 @@ service OpenShell {
     option (openshell.options.v1.authorization) = { auth_mode: "sandbox" };
   }
 
-  // Get provider environment for a sandbox (called by sandbox supervisor at startup).
+  // Get provider environment for a sandbox (called by the sandbox supervisor at
+  // startup and by its poll loop). Polling projection of the provider
+  // environment delivered over ConnectSupervisor; removed with the polling RPCs.
   rpc GetSandboxProviderEnvironment(GetSandboxProviderEnvironmentRequest)
       returns (GetSandboxProviderEnvironmentResponse) {
     option (openshell.options.v1.authorization) = {
@@ -546,9 +577,11 @@ service OpenShell {
   //
   // 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.
+  // SSH connect, ExecSandbox, targetable sandbox services, and configuration
+  // delivery. Supervisors advertise optional stream features in
+  // SupervisorHello, and the gateway sends only payloads the supervisor
+  // advertises. 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"
@@ -622,6 +655,15 @@ service OpenShell {
     };
   }
 
+  // Best-effort notification to the replica that owns a supervisor session.
+  // The receiver rebuilds snapshots from authoritative state.
+  rpc PeerNotifyConfigUpdate(PeerNotifyConfigUpdateRequest)
+      returns (PeerNotifyConfigUpdateResponse) {
+    option (openshell.options.v1.authorization) = {
+      auth_mode: "peer"
+    };
+  }
+
   // Watch a sandbox and stream updates.
   //
   // This stream can include:
@@ -1006,6 +1048,10 @@ message Sandbox {
   SandboxSpec spec = 2;
   // Latest user-facing observed status derived by the gateway.
   SandboxStatus status = 3;
+  // Read-only SSH host identity in OpenSSH SHA256:<base64> form. Stable for
+  // this sandbox's lifetime, including runtime restarts. Empty on legacy
+  // runtimes that do not provide a gateway-managed SSH identity.
+  string host_key_fingerprint = 6;
   // Read-only provenance for sandboxes created from a reusable workload template.
   SandboxWorkloadTemplateProvenance created_from_workload_template = 20;
 
@@ -1045,6 +1091,9 @@ message SandboxSpec {
   // 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 {
@@ -1170,9 +1219,8 @@ message SandboxStatus {
   // Supervisor instance currently associated with the canonical main process.
   // The gateway uses this to reject stale exit reports after a restart.
   string main_process_instance_id = 8;
-  // Normalized main process result. Signal exits use 128 + signal number.
-  // Presence indicates that the canonical main process exited. Exit code 0
-  // produces Completed; nonzero and signal-normalized exits produce Error.
+  // 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
@@ -1182,8 +1230,18 @@ message SandboxStatus {
   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.
+  // Gateway-owned provisioning deadlines, 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;
+  // Latest refused token grant across all outbound HTTP protocols, including
+  // failures before a configured endpoint or credential could be selected.
+  // Matching grant success or a policy, provider, or supervisor reset clears it.
+  TokenGrantFailureObservation last_token_grant_failure = 17;
 }
 
 // User-facing sandbox condition derived from platform or gateway observations.
@@ -1218,6 +1276,13 @@ enum SandboxPhase {
   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";
+  // The gateway requested suspension and the driver has not confirmed it.
+  SANDBOX_PHASE_SUSPENDING = 11;
+  // The workload is frozen and its memory state is retained by the isolation
+  // backend. ResumeSandbox restores it; StartSandbox discards it.
+  SANDBOX_PHASE_SUSPENDED = 12;
 }
 
 // Public platform event exposed on the sandbox watch stream.
@@ -1247,7 +1312,8 @@ message CreateSandboxRequest {
   string name = 2;
   // Optional labels for the sandbox (key-value metadata).
   map<string, string> labels = 3;
-  // Optional annotations for the sandbox (non-selector metadata).
+  // Optional annotations for the sandbox (non-selector metadata). Keys under
+  // the `internal.openshell.ai/` prefix are reserved for the gateway.
   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
@@ -1449,6 +1515,23 @@ message StartSandboxRequest {
   string request_id = 3;
 }
 
+// Suspend sandbox request.
+message SuspendSandboxRequest {
+  // Workspace scope. Only a named workspace selection is accepted.
+  openshell.datamodel.v1.WorkspaceSelector workspace_scope = 2;
+  string name = 1;
+}
+
+// Resume sandbox request.

The main suite

E           }

tests/test_repin.py:122: AssertionError
_________________ test_openshell_check_reads_the_vendored_tag __________________

    def test_openshell_check_reads_the_vendored_tag():
        seen = []
        repin.openshell_check(tags=lambda url: seen.append(url) or [])
        assert seen == [repin.OPENSHELL_URL]
>       assert repin.locks.read_source(str(repin.OPENSHELL_SOURCE))[0] == "v0.1.2"
E       AssertionError: assert 'v0.1.6' == 'v0.1.2'
E         
E         - v0.1.2
E         ?      ^
E         + v0.1.6
E         ?      ^

tests/test_repin.py:135: AssertionError
=========================== short test summary info ============================
FAILED tests/test_repin.py::test_openshell_check_reports_the_newest_release_after_the_vendored_tag - AssertionError: assert {'current': '...ag': 'v0.2.0'} == {'current': '...ag': 'v0.2.0'}
  
  Omitting 1 identical items, use -vv to show
  Differing items:
  {'current': 'v0.1.6'} != {'current': 'v0.1.2'}
  
  Full diff: (-: missing in left side, +: extra in left side)
    {
  -     'current': 'v0.1.2',
  ?                      ^
  +     'current': 'v0.1.6',
  ?                      ^
        'tag': 'v0.2.0',
    }
FAILED tests/test_repin.py::test_openshell_check_reads_the_vendored_tag - AssertionError: assert 'v0.1.6' == 'v0.1.2'
  
  - v0.1.2
  ?      ^
  + v0.1.6
  ?      ^
2 failed, 1108 passed, 7 deselected in 245.49s (0:04:05)

The interceptor suite

........                                                                 [100%]
8 passed in 2.36s

@github-actions
github-actions Bot requested a review from rappdw as a code owner October 31, 2026 06:47

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants