Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 18 additions & 0 deletions Cargo.lock

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

14 changes: 13 additions & 1 deletion crates/openshell-bootstrap/src/pki.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,15 @@ pub struct PkiBundle {
///
/// Covers the host aliases used by every supported runtime: Kubernetes service DNS,
/// `host.docker.internal` for Docker Desktop and rootless Docker on Linux,
/// and `host.containers.internal` for Podman containers reaching their host.
/// `host.containers.internal` for Podman containers reaching their host, and
/// `host.container.internal` for Apple Container guests reaching their host.
///
/// The Apple Container alias is macOS-only: including it on other operating
/// systems would force a one-time CA rotation on every existing install
/// (including Linux/Podman users who will never run Apple Container) because
/// `missing_required_server_sans` in `openshell-server/src/certgen.rs` would
/// detect a new required name and regenerate the local CA, breaking every
/// existing CLI/sandbox that trusts the old CA.
pub const DEFAULT_SERVER_SANS: &[&str] = &[
"openshell",
"openshell.openshell.svc",
Expand All @@ -40,6 +48,8 @@ pub const DEFAULT_SERVER_SANS: &[&str] = &[
"*.openshell.localhost",
"host.docker.internal",
"host.containers.internal",
#[cfg(target_os = "macos")]
"host.container.internal",
"127.0.0.1",
"::1",
];
Expand Down Expand Up @@ -250,5 +260,7 @@ mod tests {
assert!(DEFAULT_SERVER_SANS.contains(&"host.containers.internal"));
assert!(DEFAULT_SERVER_SANS.contains(&"127.0.0.1"));
assert!(DEFAULT_SERVER_SANS.contains(&"::1"));
#[cfg(target_os = "macos")]
assert!(DEFAULT_SERVER_SANS.contains(&"host.container.internal"));
}
}
5 changes: 3 additions & 2 deletions crates/openshell-core/src/driver_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,9 @@ pub const SUPERVISOR_CONTAINER_BINARY: &str = "/opt/openshell/bin/openshell-sand
// ---------------------------------------------------------------------------
// In-container mount paths for guest TLS materials and the sandbox token.
//
// All container-based drivers (Docker, Podman, Kubernetes) mount the gateway's
// mTLS client credentials at these fixed paths inside every sandbox container.
// All container-based drivers (Docker, Podman, Apple Container, Kubernetes)
// mount the gateway's mTLS client credentials at these fixed paths inside every
// sandbox container.
// The supervisor reads these paths on startup to establish its gRPC-over-mTLS
// connection back to the gateway. The paths must remain stable across driver
// versions since the supervisor binary is built and packaged separately.
Expand Down
7 changes: 7 additions & 0 deletions crates/openshell-core/src/telemetry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ impl SandboxTemplateSource {
pub struct TelemetryComputeDriver(&'static str);

impl TelemetryComputeDriver {
/// Telemetry category for the first-party Apple Container compute driver.
pub const APPLE_CONTAINER: Self = Self("apple-container");

#[must_use]
pub const fn as_str(self) -> &'static str {
self.0
Expand Down Expand Up @@ -681,6 +684,10 @@ mod tests {
TelemetryComputeDriver::anonymous_category("first_party").as_str(),
"first_party"
);
assert_eq!(
TelemetryComputeDriver::APPLE_CONTAINER.as_str(),
"apple-container"
);
}

#[test]
Expand Down
30 changes: 30 additions & 0 deletions crates/openshell-driver-apple-container/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

[package]
name = "openshell-driver-apple-container"
description = "Apple Container compute driver for OpenShell"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true

[dependencies]
openshell-core = { path = "../openshell-core", default-features = false }

tokio = { workspace = true }
tonic = { workspace = true }
futures = { workspace = true }
tokio-stream = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
chrono = { version = "0.4", default-features = false, features = ["std"] }

[dev-dependencies]
prost-types = { workspace = true }

[lints]
workspace = true
50 changes: 50 additions & 0 deletions crates/openshell-driver-apple-container/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# OpenShell Apple Container Driver

This crate implements the OpenShell compute driver for Apple's `container` CLI.
It creates local macOS sandboxes as Linux containers inside Apple Container
lightweight VMs.

The driver intentionally shells out to the installed `container` CLI instead of
linking Swift or XPC APIs directly. Apple Container's public, supported operator
surface is the CLI, and the CLI exposes machine-readable JSON for the state that
OpenShell needs:

- `container system status --format json`
- `container list --all --format json`
- `container network list --format json`

The gateway must run on macOS with Apple Container installed and running. Set
`compute_driver = "apple-container"` in `[openshell.gateway]`; the gateway
does not auto-detect this driver.

When `grpc_endpoint` is empty, the driver builds the supervisor callback URL
from `host_callback_host` and the gateway bind port. The default callback host
is `host.container.internal`, which Apple Container resolves inside the guest
VM. The gateway also listens on the Apple Container default network gateway
address discovered from `container network list --format json`.

Apple Container accepts integer CPU counts. OpenShell therefore rejects
per-sandbox CPU limits such as `500m` or `1.5` that cannot be passed to
`container run --cpus`.

## Building the Linux supervisor binary

The driver bind-mounts `supervisor_bin_dir` into the sandbox guest at
`/opt/openshell/bin`. That directory must contain a **Linux** build of
`openshell-sandbox` for the guest's architecture; shipping the macOS binary
fails the launch with `Exec format error`.

On an Apple Silicon host, cross-compile for the ARM64 guest:

```sh
rustup target add aarch64-unknown-linux-musl
brew install filosottile/musl-cross/musl-cross
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-musl-gcc \
cargo build --release -p openshell-sandbox --target aarch64-unknown-linux-musl
install -m 0755 target/aarch64-unknown-linux-musl/release/openshell-sandbox \
/path/to/supervisor-bin/openshell-sandbox
```

(The linker override belongs in `~/.cargo/config.toml` or the `CARGO_TARGET_*`
env var — not the project `.cargo/config.toml`, which is for OpenShell-internal
settings like z3.)
Loading
Loading