Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
b9635a6
feat(isolation): publish backend protocol contracts independently of …
drew Oct 9, 2026
2752ca6
feat(isolation): expose trusted launch and registry data contracts
drew Oct 9, 2026
ea5db96
refactor(isolation): name the required capability sandbox
drew Oct 14, 2026
f286a97
refactor(sandbox): align protocol and compute capability names
drew Oct 14, 2026
eb8bc7e
refactor(sandbox): finalize public protocol contract names
drew Oct 14, 2026
b1020b7
feat(isolation)!: activate backend contracts in sandbox runtime and d…
drew Oct 9, 2026
b8abe61
refactor(isolation): adopt sandbox capability throughout runtime
drew Oct 14, 2026
64a1e5f
refactor(sandbox): adopt consistent capability namespaces
drew Oct 14, 2026
ec9f01e
refactor(sandbox): adopt finalized protocol contracts
drew Oct 14, 2026
fb22a97
refactor(sandbox): simplify runtime and fix backend example
drew Oct 14, 2026
c9aa751
refactor(examples): organize WASI backend in Rust
drew Oct 14, 2026
408aa40
refactor(sandbox): defer protocol conformance fixtures
drew Oct 14, 2026
90345bb
feat(examples): add supervised V8 backend demo
drew Oct 15, 2026
710d7a8
fix(examples): stream V8 guest output with default policy
drew Oct 15, 2026
0827934
fix(examples): handle V8 rejections and roll back failed launches
drew Oct 16, 2026
0b600fa
fix(supervisor): merge main and preserve additional CA startup config
drew Oct 16, 2026
b1f17e1
fix(examples): align V8 crypto dependencies with CI AWS-LC
drew Oct 16, 2026
879f591
feat(sandbox): represent Sandbox Protocol operations in protobuf
drew Oct 16, 2026
77faa07
refactor(isolation): replace backend traits with RPC clients
drew Oct 16, 2026
2547fa2
refactor(proto): remove legacy isolation protocol reservations
drew Oct 16, 2026
f21b20c
refactor(proto): clarify Sandbox Protocol names
drew Oct 16, 2026
0bd1ee0
docs(isolation): clarify authentication bootstrap and supervisor APIs
drew Oct 16, 2026
ae13ab8
refactor(isolation): simplify policy enforcement confirmation
drew Oct 16, 2026
890a758
fix(sandbox): rotate launch generations and stabilize retry sessions
drew Oct 16, 2026
7bf776f
refactor(sandbox): remove redundant backend adapters and TCP flags
drew Oct 16, 2026
fbf18ff
fix(sandbox): repair Docker restarts and macOS lint
drew Oct 16, 2026
02df538
refactor(isolation): simplify RPC contracts and backend registration
drew Oct 16, 2026
6c5072a
fix(sandbox): deliver TCP denials before closing the RPC
drew Oct 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/branch-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ jobs:
cargo fmt --manifest-path e2e/rust/Cargo.toml --all -- --check
cargo fmt --manifest-path examples/governance-interceptor/Cargo.toml --all -- --check
cargo fmt --manifest-path examples/supervisor-middleware-content-guard/Cargo.toml --all -- --check
cargo fmt --manifest-path examples/isolation-backend-v8/Cargo.toml --all -- --check

cargo-lockfiles:
name: Cargo lockfiles
Expand Down Expand Up @@ -275,6 +276,7 @@ jobs:
cargo clippy --locked --manifest-path e2e/rust/Cargo.toml --all-targets -- -D warnings
cargo clippy --locked --manifest-path examples/governance-interceptor/Cargo.toml --all-targets -- -D warnings
cargo clippy --locked --manifest-path examples/supervisor-middleware-content-guard/Cargo.toml --all-targets -- -D warnings
cargo clippy --locked --manifest-path examples/isolation-backend-v8/Cargo.toml --all-targets -- -D warnings

rust-test:
name: Rust test (${{ matrix.system }})
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ Do not rely on this file for a full inventory. The detailed public and contribut
| `crates/openshell-sandbox/` | Sandbox runtime | Capability-free workload launcher, process identity, and seccomp-mediated I/O |
| `crates/openshell-supervisor/` | Supervisor runtime | Gateway session, policy evaluation, credentials, and upstream networking |
| `crates/openshell-binary-identity/` | Binary identity | Shared trusted procfs executable identity resolution for isolation backends |
| `crates/openshell-isolation-interface/` | Isolation backend interface | RFC 0012 `IsolationBackend` trait and types; the supervisor-facing runtime contract |
| `crates/openshell-sandbox-backend/` | OpenShell sandbox backend | `OpenShellRuntimeBackend` and the authenticated OpenShell Sandbox Protocol shared with `openshell-sandbox` |
| `crates/openshell-isolation-interface/` | Isolation backend interface | Shared data and validation for the Isolation Backend RPC contracts |
| `crates/openshell-sandbox-backend/` | OpenShell sandbox backend | Concrete Isolation Backend RPC client and authenticated Sandbox Protocol shared with `openshell-sandbox` |
| `crates/openshell-policy/` | Policy engine | Filesystem, network, and process constraints |
| `crates/openshell-policy-schema/` | Authored policy schema | Dependency-light YAML/JSON representation, bounded parsing, and pure authored-language semantics |
| `crates/openshell-bootstrap/` | Gateway metadata | Gateway registration metadata, auth token storage, mTLS bundle storage |
Expand Down
8 changes: 5 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions crates/openshell-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
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
Expand Down
1 change: 1 addition & 0 deletions crates/openshell-core/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

// Configure tonic/prost protobuf code generation.
tonic_prost_build::configure()
.btree_map(".openshell.sandbox.protocol.v1")
.build_server(true)
.build_client(true)
.include_file("openshell.rs")
Expand Down
43 changes: 34 additions & 9 deletions crates/openshell-core/src/extension_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ pub const PROTOCOL_MINOR: u32 = 0;
/// configured signer before accepting sandbox creation.
pub const COMPUTE_LAUNCH_AUTHENTICATION: &str = "openshell.compute.launch-authentication";

/// Driver stages operator-selected external isolation routes for the supervisor.
pub const COMPUTE_ISOLATION_BACKEND: &str = "openshell.compute.isolation-backend";

const MAX_IMPLEMENTATION_NAME_BYTES: usize = 128;
const MAX_IMPLEMENTATION_VERSION_BYTES: usize = 128;
const MAX_CAPABILITY_BYTES: usize = 128;
Expand Down Expand Up @@ -121,6 +124,7 @@ pub fn gateway_metadata(family: ExtensionFamily) -> PeerMetadata {
let mut supported_capabilities = vec![contract.clone()];
if family == ExtensionFamily::Compute {
supported_capabilities.push(COMPUTE_LAUNCH_AUTHENTICATION.to_string());
supported_capabilities.push(COMPUTE_ISOLATION_BACKEND.to_string());
}
PeerMetadata {
protocol_version: Some(ProtocolVersion {
Expand Down Expand Up @@ -157,10 +161,10 @@ pub fn extension_metadata(
}

/// Protocol version and base capability an extension of `family` must declare.
fn expected_contract(family: ExtensionFamily) -> String {
fn expected_contract(gateway: &PeerMetadata) -> String {
format!(
"protocol {PROTOCOL_MAJOR}.{PROTOCOL_MINOR} and capability {}",
family.contract_capability()
gateway.required_capabilities.join(", ")
)
}

Expand All @@ -171,8 +175,32 @@ pub fn negotiate(
extension: Option<PeerMetadata>,
) -> Result<NegotiatedExtension, NegotiationError> {
let configured_name = configured_name.into();
let family_name = family.as_str();
let expected = expected_contract(family);
let metadata = negotiate_metadata(family.as_str(), &configured_name, gateway, extension)?;
let version = metadata
.protocol_version
.expect("validated protocol version");
Ok(NegotiatedExtension {
family,
configured_name,
implementation_name: metadata.implementation_name,
implementation_version: metadata.implementation_version,
protocol_major: version.major,
protocol_minor: version.minor,
supported_capabilities: metadata.supported_capabilities,
required_capabilities: metadata.required_capabilities,
})
}

/// Validate a versioned peer contract, including families owned by the supervisor.
/// Both peers must advertise every capability required by the other peer.
pub fn negotiate_metadata(
family_name: &'static str,
configured_name: impl Into<String>,
gateway: &PeerMetadata,
extension: Option<PeerMetadata>,
) -> Result<PeerMetadata, NegotiationError> {
let configured_name = configured_name.into();
let expected = expected_contract(gateway);
let extension = extension.ok_or_else(|| NegotiationError::MissingMetadata {
family: family_name,
name: configured_name.clone(),
Expand Down Expand Up @@ -265,13 +293,10 @@ pub fn negotiate(
});
}

Ok(NegotiatedExtension {
family,
configured_name,
Ok(PeerMetadata {
protocol_version: Some(*version),
implementation_name: extension.implementation_name,
implementation_version: extension.implementation_version,
protocol_major: version.major,
protocol_minor: version.minor,
supported_capabilities: extension_supported.into_iter().collect(),
required_capabilities: extension_required.into_iter().collect(),
})
Expand Down
209 changes: 209 additions & 0 deletions crates/openshell-core/src/isolation_registration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

//! Operator-owned routes for external isolation backends.

use std::collections::BTreeSet;
use std::net::SocketAddr;
use std::path::PathBuf;

use serde::{Deserialize, Serialize};

/// Reserved selection for drivers that launch the built-in sandbox runtime.
pub const BUILTIN_BACKEND_NAME: &str = "openshell-sandbox";

/// Trusted byte-stream endpoint. Generation-specific TLS is supplied by the driver.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "kebab-case", deny_unknown_fields)]
pub enum SandboxTransport {
Unix {
socket_path: PathBuf,
},
Tcp {
/// Logical HTTP authority used for routing.
authority: String,
/// Concrete connection candidates pinned by the operator or driver.
addresses: Vec<SocketAddr>,
},
Vsock {
guest_cid: u32,
port: u32,
},
}

impl SandboxTransport {
pub fn validate(&self) -> Result<(), &'static str> {
let valid = match self {
Self::Unix { socket_path } => socket_path.is_absolute(),
Self::Tcp {
authority,
addresses,
} => {
!authority.is_empty()
&& !addresses.is_empty()
&& addresses
.iter()
.all(|address| address.port() != 0 && !address.ip().is_unspecified())
}
Self::Vsock { guest_cid, port } => {
*guest_cid >= 3 && *guest_cid != u32::MAX && *port != 0 && *port != u32::MAX
}
};
if valid {
Ok(())
} else {
Err("invalid boundary control transport endpoint")
}
}
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BackendRegistration {
pub name: String,
pub endpoint: SandboxTransport,
}

pub fn validate_backend_name(name: &str) -> Result<(), &'static str> {
if name == BUILTIN_BACKEND_NAME
|| name.is_empty()
|| name.len() > 128
|| !name
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.'))
{
return Err("invalid or reserved delegated backend name");
}
Ok(())
}

/// Validate every entry, including unused registrations.
pub fn validate_registrations(registrations: &[BackendRegistration]) -> Result<(), &'static str> {
let mut names = BTreeSet::new();
for registration in registrations {
validate_backend_name(&registration.name)?;
if !names.insert(&registration.name) {
return Err("duplicate isolation backend registration");
}
registration.endpoint.validate()?;
}
Ok(())
}

impl TryFrom<&BackendRegistration> for crate::proto::compute::v1::IsolationBackendRegistration {
type Error = &'static str;

fn try_from(registration: &BackendRegistration) -> Result<Self, Self::Error> {
use crate::proto::compute::v1::{
IsolationBackendTcpEndpoint, IsolationBackendVsockEndpoint,
isolation_backend_registration::Endpoint,
};
validate_backend_name(&registration.name)?;
registration.endpoint.validate()?;
let endpoint = match &registration.endpoint {
SandboxTransport::Unix { socket_path } => Endpoint::UnixSocketPath(
socket_path
.to_str()
.ok_or("isolation backend Unix path must be UTF-8")?
.into(),
),
SandboxTransport::Tcp {
authority,
addresses,
} => Endpoint::Tcp(IsolationBackendTcpEndpoint {
authority: authority.clone(),
addresses: addresses.iter().map(ToString::to_string).collect(),
}),
SandboxTransport::Vsock { guest_cid, port } => {
Endpoint::Vsock(IsolationBackendVsockEndpoint {
guest_cid: *guest_cid,
port: *port,
})
}
};
Ok(Self {
name: registration.name.clone(),
endpoint: Some(endpoint),
})
}
}

impl TryFrom<&crate::proto::compute::v1::IsolationBackendRegistration> for BackendRegistration {
type Error = &'static str;

fn try_from(
registration: &crate::proto::compute::v1::IsolationBackendRegistration,
) -> Result<Self, Self::Error> {
use crate::proto::compute::v1::isolation_backend_registration::Endpoint;
let endpoint = match registration
.endpoint
.as_ref()
.ok_or("isolation backend endpoint is missing")?
{
Endpoint::UnixSocketPath(path) => SandboxTransport::Unix {
socket_path: path.into(),
},
Endpoint::Tcp(tcp) => SandboxTransport::Tcp {
authority: tcp.authority.clone(),
addresses: tcp
.addresses
.iter()
.map(|address| {
address
.parse()
.map_err(|_| "invalid isolation backend TCP address")
})
.collect::<Result<_, _>>()?,
},
Endpoint::Vsock(vsock) => SandboxTransport::Vsock {
guest_cid: vsock.guest_cid,
port: vsock.port,
},
};
validate_backend_name(&registration.name)?;
endpoint.validate()?;
Ok(Self {
name: registration.name.clone(),
endpoint,
})
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::proto::compute::v1::IsolationBackendRegistration;

#[test]
fn operator_routes_survive_compute_protocol_and_supervisor_json() {
for endpoint in [
SandboxTransport::Unix {
socket_path: "/run/backend.sock".into(),
},
SandboxTransport::Tcp {
authority: "backend.example:443".into(),
addresses: vec![
"127.0.0.1:47000".parse().unwrap(),
"[::1]:47000".parse().unwrap(),
],
},
SandboxTransport::Vsock {
guest_cid: 3,
port: 47000,
},
] {
let original = BackendRegistration {
name: "agent-substrate".into(),
endpoint,
};
let wire = IsolationBackendRegistration::try_from(&original).unwrap();
let decoded = BackendRegistration::try_from(&wire).unwrap();
assert_eq!(decoded, original);
let json = serde_json::to_string(&decoded).unwrap();
assert_eq!(
serde_json::from_str::<BackendRegistration>(&json).unwrap(),
original
);
}
}
}
1 change: 1 addition & 0 deletions crates/openshell-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub mod gpu;
pub mod grpc_client;
pub mod host_pattern;
pub mod image;
pub mod isolation_registration;
pub mod jwt;
pub mod local_api_socket;
pub mod mcp;
Expand Down
Loading
Loading