Files
OpenShell/crates/openshell-core
grsandsjenning 37712756de feat(sandbox): support storage-preserving sandbox configuration updates (#4873)
* feat(gateway): add storage-preserving sandbox image updates

Add image-only UpdateSandbox with conditional versions, durable replay, fenced pending intent, and Kubernetes storage retention. Enable non-destructive image repair after completed timeout cleanup and exercise both Kubernetes controller API lanes.

Closes #4623

Signed-off-by: sjenning <493891+sjenning@users.noreply.github.com>

* feat(gateway): add storage-preserving sandbox image updates

OpenClaw-Publication: b2036b09-b697-4d65-aba9-5c96a3198f9c
Signed-off-by: sjenning <493891+sjenning@users.noreply.github.com>

* fix(gateway): preserve image update patch-release compatibility

Move image updates to a separate authenticated service and retain the baseline public sandbox status. Persist image intent and revisions independently with atomic baseline-known lifecycle fencing and explicit post-upgrade activation.

Preserve concurrent provisioning clocks, fence replaced attempts and delayed callbacks, and verify private-state transactions plus both Kubernetes controller API lanes.

Signed-off-by: sjenning <493891+sjenning@users.noreply.github.com>

* fix(gateway): preserve optional driver compatibility and repair CI

Move image updates to a separate optional compute-driver service, preserving the baseline generated trait and streaming associated type. Add source and TCP/UDS transport regressions, capability negotiation coverage, and a default-unsupported in-process extension.

Keep Go compatibility tests outside generated output and update production reflection expectations for the separate sandbox update service.

Signed-off-by: sjenning <493891+sjenning@users.noreply.github.com>

* fix(gateway): preserve baseline factory and descriptor APIs

Restore the original in-process driver trait object and existing instance enum. Register optional image updates through a default factory hook without requiring changes to existing drivers or factories, and require registration plus capability before mutation.

Accept OpenShell-only descriptors while retaining strict validation of present update services. Cover baseline registration, missing-updater no-write behavior, descriptor validation, and both live Kubernetes retention lanes.

Signed-off-by: sjenning <493891+sjenning@users.noreply.github.com>

* chore(ci): use upstream Kubernetes E2E defaults

Drop the task-level fallback superseded by main's runner defaults and explicit e2e-conformance-only selector. Preserve current upstream task definitions and recovery waiver unchanged.

Signed-off-by: sjenning <493891+sjenning@users.noreply.github.com>

* fix(gateway): integrate image updates with backend protocol

Adapt non-launch image updates to the upstream remote admission API while retaining resource-policy verification before forwarding. Cover both negotiated extensions and preservation of backend pins and persisted launch identity.

Signed-off-by: sjenning <493891+sjenning@users.noreply.github.com>

* fix(gateway): remove sandbox image state on deletion

Signed-off-by: Gordon Sim <gsim@redhat.com>

* feat(gateway): expand UpdateSandbox configuration updates

Signed-off-by: Gordon Sim <gsim@redhat.com>

* fix(sandbox): recover pending updates and address review feedback

Signed-off-by: Gordon Sim <gsim@redhat.com>

* perf(server): index pending sandbox updates for recovery

Signed-off-by: Gordon Sim <gsim@redhat.com>

* docs(sandbox): clarify stop behavior when updates are disabled

Signed-off-by: Gordon Sim <gsim@redhat.com>

* fix(server): scope deletion bypass to the matching sandbox update

Signed-off-by: Gordon Sim <gsim@redhat.com>

* fix(server): back off pending sandbox update recovery

Signed-off-by: Gordon Sim <gsim@redhat.com>

* ci: restore combined-chart Kubernetes e2e coverage

Signed-off-by: Gordon Sim <gsim@redhat.com>

* test(kubernetes): use uuid for update request IDs

Signed-off-by: Gordon Sim <gsim@redhat.com>

* fix(kubernetes): reject oversized annotations before accepting sandbox updates

Signed-off-by: Gordon Sim <gsim@redhat.com>

* docs(skills): correct pending sandbox update recovery guidance

Signed-off-by: Gordon Sim <gsim@redhat.com>

* fix(sandbox): preserve terminal results when stopping missing compute

Signed-off-by: Gordon Sim <gsim@redhat.com>

* fix(server): bound sandbox update attempts to unblock recovery and deletion

Signed-off-by: Gordon Sim <gsim@redhat.com>

* fix(server): remove unintended sandbox label count limit

Signed-off-by: Gordon Sim <gsim@redhat.com>

* fix(kubernetes): check known pod names during sandbox update preflight

Signed-off-by: Gordon Sim <gsim@redhat.com>

* docs: clarify sandbox update permissions and driver support

Signed-off-by: Gordon Sim <gsim@redhat.com>

---------

Signed-off-by: sjenning <493891+sjenning@users.noreply.github.com>
Signed-off-by: Gordon Sim <gsim@redhat.com>
Co-authored-by: sjenning <493891+sjenning@users.noreply.github.com>
2026-10-30 17:01:49 +00:00
..

openshell-core

Shared types, constants, configuration, and helpers used across OpenShell crates.

Isolation registrations

isolation_registration owns the operator registry's backend names, byte-stream endpoints, and shared validation. Its fallible conversions carry registrations through the compute-driver protobuf and back into the supervisor's JSON launch file. These routes contain no credentials or driver-owned launch coordinates.

Object Metadata

Top-level user-facing objects use a Kubernetes-style metadata convention. The metadata shape provides:

  • Stable server-generated ID.
  • Human-readable name.
  • Creation timestamp.
  • Optional labels for filtering and automation.

Code that works with object metadata should use the traits in openshell_core::metadata instead of reaching into protobuf fields directly:

use openshell_core::{ObjectId, ObjectLabels, ObjectName};

let id = sandbox.object_id();
let name = sandbox.object_name();
let labels = sandbox.object_labels();

Trait methods must tolerate missing metadata and return safe empty values rather than panicking.

Label Rules

Labels follow Kubernetes-style key and value conventions:

  • Keys may include an optional DNS-prefix followed by /.
  • Names are limited to alphanumeric characters plus -, _, and ..
  • Values use the same character set and may be empty.
  • Selectors use comma-separated key=value pairs with AND semantics.

Validate labels at API ingress before persisting objects.

Inference Profiles

Provider inference profiles live in this crate so the gateway, sandbox, and router agree on provider defaults. Profiles define:

  • Auth header style.
  • Default upstream headers.
  • Client-supplied passthrough headers.
  • Supported inference protocol shapes.

Do not duplicate provider-specific inference behavior in callers. Add shared behavior here, then consume it from the gateway, sandbox, and router.

Middleware Contracts

Built-in supervisor middleware identifiers, host-selector matching, and pure configuration validation live in openshell_core::middleware. Policy admission and the supervisor runtime consume the same contract without introducing a dependency from the policy crate to the supervisor implementation.

Optional Compute Updates

The generated proto::compute::v1::compute_driver_server::ComputeDriver trait retains its baseline methods and streaming associated type. Supporting external drivers additionally serve ComputeDriverConfigurationUpdates on the same endpoint and advertise openshell.compute.update-sandbox-configuration. The request carries image and lifecycle preconditions plus optional command and environment replacements. Drivers without the capability need no new method.

Linked drivers opt in through openshell_core::compute::ComputeDriver and ComputeDriverFactory::build_with_configuration_updates. The default factory hook calls build without an updater. Registration and capability negotiation are both required. Metadata-only updates are handled by the gateway.

Protobuf Struct Conversion

Use openshell_core::proto_struct when crossing between serde_json values and prost_types::{Struct, Value}. Both conversion directions live in this module; JSON-to-protobuf conversion is fallible so callers cannot silently replace an unrepresentable number.