From ba15e43a06b03852e18f44d9b4a8d46886f4b3fd Mon Sep 17 00:00:00 2001 From: Drew Newberry Date: Wed, 15 Apr 2026 12:28:09 -0700 Subject: [PATCH 1/9] feat(release): publish standalone openshell-gateway binaries Signed-off-by: Drew Newberry --- .github/workflows/release-dev.yml | 184 ++++++++++++- .github/workflows/release-tag.yml | 186 ++++++++++++- architecture/build-containers.md | 12 + crates/openshell-server/Cargo.toml | 4 + .../src/bin/openshell-gateway.rs | 9 + crates/openshell-server/src/cli.rs | 256 ++++++++++++++++++ crates/openshell-server/src/lib.rs | 1 + crates/openshell-server/src/main.rs | 216 +-------------- .../src/persistence/postgres.rs | 11 +- .../src/persistence/sqlite.rs | 11 +- .../openshell-server/src/persistence/tests.rs | 10 + deploy/docker/Dockerfile.gateway-macos | 107 ++++++++ docs/reference/support-matrix.mdx | 14 +- 13 files changed, 787 insertions(+), 234 deletions(-) create mode 100644 crates/openshell-server/src/bin/openshell-gateway.rs create mode 100644 crates/openshell-server/src/cli.rs create mode 100644 deploy/docker/Dockerfile.gateway-macos diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index bdc8b1a71..16650bfce 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -378,12 +378,168 @@ jobs: path: artifacts/*.tar.gz retention-days: 5 + # --------------------------------------------------------------------------- + # Build standalone gateway binaries (Linux GNU — native on each arch) + # --------------------------------------------------------------------------- + build-gateway-binary-linux: + name: Build Gateway Binary (Linux ${{ matrix.arch }}) + needs: [compute-versions] + strategy: + matrix: + include: + - arch: amd64 + runner: build-amd64 + target: x86_64-unknown-linux-gnu + - arch: arm64 + runner: build-arm64 + target: aarch64-unknown-linux-gnu + runs-on: ${{ matrix.runner }} + timeout-minutes: 60 + container: + image: ghcr.io/nvidia/openshell/ci:latest + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + options: --privileged + env: + MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SCCACHE_MEMCACHED_ENDPOINT: ${{ vars.SCCACHE_MEMCACHED_ENDPOINT }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Mark workspace safe for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + + - name: Fetch tags + run: git fetch --tags --force + + - name: Install tools + run: mise install + + - name: Cache Rust target and registry + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + with: + shared-key: gateway-binary-gnu-${{ matrix.arch }} + cache-directories: .cache/sccache + cache-targets: "true" + + - name: Scope workspace to gateway crates + run: | + set -euo pipefail + sed -i 's|members = \["crates/\*"\]|members = ["crates/openshell-server", "crates/openshell-core", "crates/openshell-driver-kubernetes", "crates/openshell-policy", "crates/openshell-router"]|' Cargo.toml + + - name: Patch workspace version + if: needs.compute-versions.outputs.cargo_version != '' + run: | + set -euo pipefail + sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${{ needs.compute-versions.outputs.cargo_version }}"'"/}' Cargo.toml + + - name: Build ${{ matrix.target }} + run: | + set -euo pipefail + mise x -- cargo build --release --target ${{ matrix.target }} -p openshell-server --bin openshell-gateway + + - name: Verify packaged binary + run: | + set -euo pipefail + OUTPUT="$(target/${{ matrix.target }}/release/openshell-gateway --version)" + echo "$OUTPUT" + grep -q '^openshell-gateway ' <<<"$OUTPUT" + + - name: sccache stats + if: always() + run: mise x -- sccache --show-stats + + - name: Package binary + run: | + set -euo pipefail + mkdir -p artifacts + tar -czf artifacts/openshell-gateway-${{ matrix.target }}.tar.gz \ + -C target/${{ matrix.target }}/release openshell-gateway + ls -lh artifacts/ + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: gateway-binary-linux-${{ matrix.arch }} + path: artifacts/*.tar.gz + retention-days: 5 + + # --------------------------------------------------------------------------- + # Build standalone gateway binary (macOS aarch64 via osxcross) + # --------------------------------------------------------------------------- + build-gateway-binary-macos: + name: Build Gateway Binary (macOS) + needs: [compute-versions] + runs-on: build-amd64 + timeout-minutes: 60 + container: + image: ghcr.io/nvidia/openshell/ci:latest + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + options: --privileged + volumes: + - /var/run/docker.sock:/var/run/docker.sock + env: + MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SCCACHE_MEMCACHED_ENDPOINT: ${{ vars.SCCACHE_MEMCACHED_ENDPOINT }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Mark workspace safe for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + + - name: Fetch tags + run: git fetch --tags --force + + - name: Log in to GHCR + run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin + + - name: Set up Docker Buildx + uses: ./.github/actions/setup-buildx + + - name: Build macOS binary via Docker + run: | + set -euo pipefail + docker buildx build \ + --file deploy/docker/Dockerfile.gateway-macos \ + --build-arg OPENSHELL_CARGO_VERSION="${{ needs.compute-versions.outputs.cargo_version }}" \ + --build-arg CARGO_TARGET_CACHE_SCOPE="${{ github.sha }}" \ + --target binary \ + --output type=local,dest=out/ \ + . + + - name: Verify packaged binary shape + run: | + set -euo pipefail + test -x out/openshell-gateway + + - name: Package binary + run: | + set -euo pipefail + mkdir -p artifacts + tar -czf artifacts/openshell-gateway-aarch64-apple-darwin.tar.gz \ + -C out openshell-gateway + ls -lh artifacts/ + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: gateway-binary-macos + path: artifacts/*.tar.gz + retention-days: 5 + # --------------------------------------------------------------------------- # Create / update the dev GitHub Release with CLI binaries and wheels # --------------------------------------------------------------------------- release-dev: name: Release Dev - needs: [compute-versions, build-cli-linux, build-cli-macos, build-python-wheels-linux, build-python-wheel-macos] + needs: [compute-versions, build-cli-linux, build-cli-macos, build-gateway-binary-linux, build-gateway-binary-macos, build-python-wheels-linux, build-python-wheel-macos] runs-on: build-amd64 timeout-minutes: 10 outputs: @@ -398,6 +554,13 @@ jobs: path: release/ merge-multiple: true + - name: Download gateway binary artifacts + uses: actions/download-artifact@v4 + with: + pattern: gateway-binary-* + path: release/ + merge-multiple: true + - name: Download wheel artifacts uses: actions/download-artifact@v4 with: @@ -417,8 +580,17 @@ jobs: run: | set -euo pipefail cd release - sha256sum *.tar.gz *.whl > openshell-checksums-sha256.txt + sha256sum \ + openshell-x86_64-unknown-linux-musl.tar.gz \ + openshell-aarch64-unknown-linux-musl.tar.gz \ + openshell-aarch64-apple-darwin.tar.gz \ + *.whl > openshell-checksums-sha256.txt cat openshell-checksums-sha256.txt + sha256sum \ + openshell-gateway-x86_64-unknown-linux-gnu.tar.gz \ + openshell-gateway-aarch64-unknown-linux-gnu.tar.gz \ + openshell-gateway-aarch64-apple-darwin.tar.gz > openshell-gateway-checksums-sha256.txt + cat openshell-gateway-checksums-sha256.txt - name: Prune stale wheel assets from dev release uses: actions/github-script@v7 @@ -492,12 +664,20 @@ jobs: curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install.sh | OPENSHELL_VERSION=dev sh ``` + ### Standalone gateway binary + + Manual-download `openshell-gateway` tarballs are attached for Linux amd64/arm64 and macOS ARM64. This release track does not yet include a gateway installer. + files: | release/openshell-x86_64-unknown-linux-musl.tar.gz release/openshell-aarch64-unknown-linux-musl.tar.gz release/openshell-aarch64-apple-darwin.tar.gz + release/openshell-gateway-x86_64-unknown-linux-gnu.tar.gz + release/openshell-gateway-aarch64-unknown-linux-gnu.tar.gz + release/openshell-gateway-aarch64-apple-darwin.tar.gz release/*.whl release/openshell-checksums-sha256.txt + release/openshell-gateway-checksums-sha256.txt trigger-wheel-publish: name: Trigger Wheel Publish diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index d89d67032..6ab0895d9 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -402,12 +402,170 @@ jobs: path: artifacts/*.tar.gz retention-days: 5 + # --------------------------------------------------------------------------- + # Build standalone gateway binaries (Linux GNU — native on each arch) + # --------------------------------------------------------------------------- + build-gateway-binary-linux: + name: Build Gateway Binary (Linux ${{ matrix.arch }}) + needs: [compute-versions] + strategy: + matrix: + include: + - arch: amd64 + runner: build-amd64 + target: x86_64-unknown-linux-gnu + - arch: arm64 + runner: build-arm64 + target: aarch64-unknown-linux-gnu + runs-on: ${{ matrix.runner }} + timeout-minutes: 60 + container: + image: ghcr.io/nvidia/openshell/ci:latest + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + options: --privileged + env: + MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SCCACHE_MEMCACHED_ENDPOINT: ${{ vars.SCCACHE_MEMCACHED_ENDPOINT }} + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.tag || github.ref }} + fetch-depth: 0 + + - name: Mark workspace safe for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + + - name: Fetch tags + run: git fetch --tags --force + + - name: Install tools + run: mise install + + - name: Cache Rust target and registry + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + with: + shared-key: gateway-binary-gnu-${{ matrix.arch }} + cache-directories: .cache/sccache + cache-targets: "true" + + - name: Scope workspace to gateway crates + run: | + set -euo pipefail + sed -i 's|members = \["crates/\*"\]|members = ["crates/openshell-server", "crates/openshell-core", "crates/openshell-driver-kubernetes", "crates/openshell-policy", "crates/openshell-router"]|' Cargo.toml + + - name: Patch workspace version + if: needs.compute-versions.outputs.cargo_version != '' + run: | + set -euo pipefail + sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${{ needs.compute-versions.outputs.cargo_version }}"'"/}' Cargo.toml + + - name: Build ${{ matrix.target }} + run: | + set -euo pipefail + mise x -- cargo build --release --target ${{ matrix.target }} -p openshell-server --bin openshell-gateway + + - name: Verify packaged binary + run: | + set -euo pipefail + OUTPUT="$(target/${{ matrix.target }}/release/openshell-gateway --version)" + echo "$OUTPUT" + grep -q '^openshell-gateway ' <<<"$OUTPUT" + + - name: sccache stats + if: always() + run: mise x -- sccache --show-stats + + - name: Package binary + run: | + set -euo pipefail + mkdir -p artifacts + tar -czf artifacts/openshell-gateway-${{ matrix.target }}.tar.gz \ + -C target/${{ matrix.target }}/release openshell-gateway + ls -lh artifacts/ + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: gateway-binary-linux-${{ matrix.arch }} + path: artifacts/*.tar.gz + retention-days: 5 + + # --------------------------------------------------------------------------- + # Build standalone gateway binary (macOS aarch64 via osxcross) + # --------------------------------------------------------------------------- + build-gateway-binary-macos: + name: Build Gateway Binary (macOS) + needs: [compute-versions] + runs-on: build-amd64 + timeout-minutes: 60 + container: + image: ghcr.io/nvidia/openshell/ci:latest + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + options: --privileged + volumes: + - /var/run/docker.sock:/var/run/docker.sock + env: + MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SCCACHE_MEMCACHED_ENDPOINT: ${{ vars.SCCACHE_MEMCACHED_ENDPOINT }} + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.tag || github.ref }} + fetch-depth: 0 + + - name: Mark workspace safe for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + + - name: Fetch tags + run: git fetch --tags --force + + - name: Log in to GHCR + run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin + + - name: Set up Docker Buildx + uses: ./.github/actions/setup-buildx + + - name: Build macOS binary via Docker + run: | + set -euo pipefail + docker buildx build \ + --file deploy/docker/Dockerfile.gateway-macos \ + --build-arg OPENSHELL_CARGO_VERSION="${{ needs.compute-versions.outputs.cargo_version }}" \ + --build-arg CARGO_TARGET_CACHE_SCOPE="${{ github.sha }}" \ + --target binary \ + --output type=local,dest=out/ \ + . + + - name: Verify packaged binary shape + run: | + set -euo pipefail + test -x out/openshell-gateway + + - name: Package binary + run: | + set -euo pipefail + mkdir -p artifacts + tar -czf artifacts/openshell-gateway-aarch64-apple-darwin.tar.gz \ + -C out openshell-gateway + ls -lh artifacts/ + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: gateway-binary-macos + path: artifacts/*.tar.gz + retention-days: 5 + # --------------------------------------------------------------------------- # Create a tagged GitHub Release with CLI binaries and wheels # --------------------------------------------------------------------------- release: name: Release - needs: [compute-versions, build-cli-linux, build-cli-macos, build-python-wheels-linux, build-python-wheel-macos, tag-ghcr-release] + needs: [compute-versions, build-cli-linux, build-cli-macos, build-gateway-binary-linux, build-gateway-binary-macos, build-python-wheels-linux, build-python-wheel-macos, tag-ghcr-release] runs-on: build-amd64 timeout-minutes: 10 outputs: @@ -424,6 +582,13 @@ jobs: path: release/ merge-multiple: true + - name: Download gateway binary artifacts + uses: actions/download-artifact@v4 + with: + pattern: gateway-binary-* + path: release/ + merge-multiple: true + - name: Download wheel artifacts uses: actions/download-artifact@v4 with: @@ -443,8 +608,17 @@ jobs: run: | set -euo pipefail cd release - sha256sum *.tar.gz *.whl > openshell-checksums-sha256.txt + sha256sum \ + openshell-x86_64-unknown-linux-musl.tar.gz \ + openshell-aarch64-unknown-linux-musl.tar.gz \ + openshell-aarch64-apple-darwin.tar.gz \ + *.whl > openshell-checksums-sha256.txt cat openshell-checksums-sha256.txt + sha256sum \ + openshell-gateway-x86_64-unknown-linux-gnu.tar.gz \ + openshell-gateway-aarch64-unknown-linux-gnu.tar.gz \ + openshell-gateway-aarch64-apple-darwin.tar.gz > openshell-gateway-checksums-sha256.txt + cat openshell-gateway-checksums-sha256.txt - name: Create GitHub Release uses: softprops/action-gh-release@v2 @@ -462,12 +636,20 @@ jobs: curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install.sh | OPENSHELL_VERSION=${{ env.RELEASE_TAG }} sh ``` + ### Standalone gateway binary + + Manual-download `openshell-gateway` tarballs are attached for Linux amd64/arm64 and macOS ARM64. This release track does not yet include a gateway installer. + files: | release/openshell-x86_64-unknown-linux-musl.tar.gz release/openshell-aarch64-unknown-linux-musl.tar.gz release/openshell-aarch64-apple-darwin.tar.gz + release/openshell-gateway-x86_64-unknown-linux-gnu.tar.gz + release/openshell-gateway-aarch64-unknown-linux-gnu.tar.gz + release/openshell-gateway-aarch64-apple-darwin.tar.gz release/*.whl release/openshell-checksums-sha256.txt + release/openshell-gateway-checksums-sha256.txt publish-fern-docs: name: Publish Fern Docs diff --git a/architecture/build-containers.md b/architecture/build-containers.md index 493e7207a..2db9445f8 100644 --- a/architecture/build-containers.md +++ b/architecture/build-containers.md @@ -21,6 +21,18 @@ The cluster image is a single-container Kubernetes distribution that bundles the The supervisor binary (`openshell-sandbox`) is built by the shared `supervisor-builder` stage in `deploy/docker/Dockerfile.images` and placed at `/opt/openshell/bin/openshell-sandbox`. It is exposed to sandbox pods at runtime via a read-only `hostPath` volume mount — it is not baked into sandbox images. +## Standalone Gateway Binary + +OpenShell also publishes a standalone `openshell-gateway` binary as a GitHub release asset. + +- **Source crate**: `crates/openshell-server` +- **Artifact name**: `openshell-gateway-.tar.gz` +- **Targets**: `x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu`, `aarch64-apple-darwin` +- **Release workflows**: `.github/workflows/release-dev.yml`, `.github/workflows/release-tag.yml` +- **Installer**: None yet. The binary is a manual-download asset. + +The standalone artifact is user-facing only. The deployed container image keeps using the internal `openshell-server` binary name and entrypoint. + ## Python Wheels OpenShell also publishes Python wheels for `linux/amd64`, `linux/arm64`, and macOS ARM64. diff --git a/crates/openshell-server/Cargo.toml b/crates/openshell-server/Cargo.toml index b4e8b9e2f..a13b339d5 100644 --- a/crates/openshell-server/Cargo.toml +++ b/crates/openshell-server/Cargo.toml @@ -14,6 +14,10 @@ repository.workspace = true name = "openshell-server" path = "src/main.rs" +[[bin]] +name = "openshell-gateway" +path = "src/bin/openshell-gateway.rs" + [dependencies] openshell-core = { path = "../openshell-core" } openshell-driver-kubernetes = { path = "../openshell-driver-kubernetes" } diff --git a/crates/openshell-server/src/bin/openshell-gateway.rs b/crates/openshell-server/src/bin/openshell-gateway.rs new file mode 100644 index 000000000..cff048b0f --- /dev/null +++ b/crates/openshell-server/src/bin/openshell-gateway.rs @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +use miette::Result; + +#[tokio::main] +async fn main() -> Result<()> { + openshell_server::cli::run_cli("openshell-gateway").await +} diff --git a/crates/openshell-server/src/cli.rs b/crates/openshell-server/src/cli.rs new file mode 100644 index 000000000..f8e870f7a --- /dev/null +++ b/crates/openshell-server/src/cli.rs @@ -0,0 +1,256 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Shared CLI entrypoint for the gateway binaries. + +use clap::{Command, CommandFactory, FromArgMatches, Parser}; +use miette::{IntoDiagnostic, Result}; +use openshell_core::ComputeDriverKind; +use std::net::SocketAddr; +use std::path::PathBuf; +use tracing::info; +use tracing_subscriber::EnvFilter; + +use crate::{run_server, tracing_bus::TracingLogBus}; + +/// `OpenShell` gateway process - gRPC and HTTP server with protocol multiplexing. +#[derive(Parser, Debug)] +#[command(version = openshell_core::VERSION)] +#[command(about = "OpenShell gRPC/HTTP server", long_about = None)] +struct Args { + /// Port to bind the server to (all interfaces). + #[arg(long, default_value_t = 8080, env = "OPENSHELL_SERVER_PORT")] + port: u16, + + /// Log level (trace, debug, info, warn, error). + #[arg(long, default_value = "info", env = "OPENSHELL_LOG_LEVEL")] + log_level: String, + + /// Path to TLS certificate file (required unless --disable-tls). + #[arg(long, env = "OPENSHELL_TLS_CERT")] + tls_cert: Option, + + /// Path to TLS private key file (required unless --disable-tls). + #[arg(long, env = "OPENSHELL_TLS_KEY")] + tls_key: Option, + + /// Path to CA certificate for client certificate verification (mTLS). + #[arg(long, env = "OPENSHELL_TLS_CLIENT_CA")] + tls_client_ca: Option, + + /// Database URL for persistence. + #[arg(long, env = "OPENSHELL_DB_URL", required = true)] + db_url: String, + + /// Compute drivers configured for this gateway. + /// + /// Accepts a comma-delimited list such as `kubernetes` or + /// `kubernetes,podman`. The configuration format is future-proofed for + /// multiple drivers, but the gateway currently requires exactly one. + #[arg( + long, + alias = "driver", + env = "OPENSHELL_DRIVERS", + value_delimiter = ',', + default_value = "kubernetes", + value_parser = parse_compute_driver + )] + drivers: Vec, + + /// Kubernetes namespace for sandboxes. + #[arg(long, env = "OPENSHELL_SANDBOX_NAMESPACE", default_value = "default")] + sandbox_namespace: String, + + /// Default container image for sandboxes. + #[arg(long, env = "OPENSHELL_SANDBOX_IMAGE")] + sandbox_image: Option, + + /// Kubernetes imagePullPolicy for sandbox pods (Always, IfNotPresent, Never). + #[arg(long, env = "OPENSHELL_SANDBOX_IMAGE_PULL_POLICY")] + sandbox_image_pull_policy: Option, + + /// gRPC endpoint for sandboxes to callback to `OpenShell`. + /// This should be reachable from within the Kubernetes cluster. + #[arg(long, env = "OPENSHELL_GRPC_ENDPOINT")] + grpc_endpoint: Option, + + /// Public host for the SSH gateway. + #[arg(long, env = "OPENSHELL_SSH_GATEWAY_HOST", default_value = "127.0.0.1")] + ssh_gateway_host: String, + + /// Public port for the SSH gateway. + #[arg(long, env = "OPENSHELL_SSH_GATEWAY_PORT", default_value_t = 8080)] + ssh_gateway_port: u16, + + /// HTTP path for SSH CONNECT/upgrade. + #[arg( + long, + env = "OPENSHELL_SSH_CONNECT_PATH", + default_value = "/connect/ssh" + )] + ssh_connect_path: String, + + /// SSH port inside sandbox pods. + #[arg(long, env = "OPENSHELL_SANDBOX_SSH_PORT", default_value_t = 2222)] + sandbox_ssh_port: u16, + + /// Shared secret for gateway-to-sandbox SSH handshake. + #[arg(long, env = "OPENSHELL_SSH_HANDSHAKE_SECRET")] + ssh_handshake_secret: Option, + + /// Allowed clock skew in seconds for SSH handshake. + #[arg(long, env = "OPENSHELL_SSH_HANDSHAKE_SKEW_SECS", default_value_t = 300)] + ssh_handshake_skew_secs: u64, + + /// Kubernetes secret name containing client TLS materials for sandbox pods. + #[arg(long, env = "OPENSHELL_CLIENT_TLS_SECRET_NAME")] + client_tls_secret_name: Option, + + /// Host gateway IP for sandbox pod hostAliases. + /// When set, sandbox pods get hostAliases entries mapping + /// host.docker.internal and host.openshell.internal to this IP. + #[arg(long, env = "OPENSHELL_HOST_GATEWAY_IP")] + host_gateway_ip: Option, + + /// Disable TLS entirely — listen on plaintext HTTP. + /// Use this when the gateway sits behind a reverse proxy or tunnel + /// (e.g. Cloudflare Tunnel) that terminates TLS at the edge. + #[arg(long, env = "OPENSHELL_DISABLE_TLS")] + disable_tls: bool, + + /// Disable gateway authentication (mTLS client certificate requirement). + /// When set, the TLS handshake accepts connections without a client + /// certificate. Ignored when --disable-tls is set. + #[arg(long, env = "OPENSHELL_DISABLE_GATEWAY_AUTH")] + disable_gateway_auth: bool, +} + +pub fn command(program_name: &'static str) -> Command { + Args::command().name(program_name).bin_name(program_name) +} + +pub async fn run_cli(program_name: &'static str) -> Result<()> { + rustls::crypto::ring::default_provider() + .install_default() + .map_err(|e| miette::miette!("failed to install rustls crypto provider: {e:?}"))?; + + let args = + Args::from_arg_matches(&command(program_name).get_matches()).expect("clap validated args"); + + run_from_args(args).await +} + +async fn run_from_args(args: Args) -> Result<()> { + let tracing_log_bus = TracingLogBus::new(); + tracing_log_bus.install_subscriber( + EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(&args.log_level)), + ); + + let bind = SocketAddr::from(([0, 0, 0, 0], args.port)); + + let tls = if args.disable_tls { + None + } else { + let cert_path = args.tls_cert.ok_or_else(|| { + miette::miette!( + "--tls-cert is required when TLS is enabled (use --disable-tls to skip)" + ) + })?; + let key_path = args.tls_key.ok_or_else(|| { + miette::miette!("--tls-key is required when TLS is enabled (use --disable-tls to skip)") + })?; + let client_ca_path = args.tls_client_ca.ok_or_else(|| { + miette::miette!( + "--tls-client-ca is required when TLS is enabled (use --disable-tls to skip)" + ) + })?; + Some(openshell_core::TlsConfig { + cert_path, + key_path, + client_ca_path, + allow_unauthenticated: args.disable_gateway_auth, + }) + }; + + let mut config = openshell_core::Config::new(tls) + .with_bind_address(bind) + .with_log_level(&args.log_level); + + config = config + .with_database_url(args.db_url) + .with_compute_drivers(args.drivers) + .with_sandbox_namespace(args.sandbox_namespace) + .with_ssh_gateway_host(args.ssh_gateway_host) + .with_ssh_gateway_port(args.ssh_gateway_port) + .with_ssh_connect_path(args.ssh_connect_path) + .with_sandbox_ssh_port(args.sandbox_ssh_port) + .with_ssh_handshake_skew_secs(args.ssh_handshake_skew_secs); + + if let Some(image) = args.sandbox_image { + config = config.with_sandbox_image(image); + } + + if let Some(policy) = args.sandbox_image_pull_policy { + config = config.with_sandbox_image_pull_policy(policy); + } + + if let Some(endpoint) = args.grpc_endpoint { + config = config.with_grpc_endpoint(endpoint); + } + + if let Some(secret) = args.ssh_handshake_secret { + config = config.with_ssh_handshake_secret(secret); + } + + if let Some(name) = args.client_tls_secret_name { + config = config.with_client_tls_secret_name(name); + } + + if let Some(ip) = args.host_gateway_ip { + config = config.with_host_gateway_ip(ip); + } + + if args.disable_tls { + info!("TLS disabled — listening on plaintext HTTP"); + } else if args.disable_gateway_auth { + info!("Gateway auth disabled — accepting connections without client certificates"); + } + + info!(bind = %config.bind_address, "Starting OpenShell server"); + + run_server(config, tracing_log_bus).await.into_diagnostic() +} + +fn parse_compute_driver(value: &str) -> std::result::Result { + value.parse() +} + +#[cfg(test)] +mod tests { + use super::command; + + #[test] + fn command_uses_requested_binary_name() { + let mut server_help = Vec::new(); + command("openshell-server") + .write_long_help(&mut server_help) + .unwrap(); + let server_help = String::from_utf8(server_help).unwrap(); + assert!(server_help.contains("openshell-server")); + + let mut gateway_help = Vec::new(); + command("openshell-gateway") + .write_long_help(&mut gateway_help) + .unwrap(); + let gateway_help = String::from_utf8(gateway_help).unwrap(); + assert!(gateway_help.contains("openshell-gateway")); + assert!(!gateway_help.contains("openshell-server")); + } + + #[test] + fn command_exposes_version() { + let gateway_command = command("openshell-gateway"); + let version = gateway_command.get_version().unwrap(); + assert_eq!(version.to_string(), openshell_core::VERSION); + } +} diff --git a/crates/openshell-server/src/lib.rs b/crates/openshell-server/src/lib.rs index a8d820b4d..7549a1774 100644 --- a/crates/openshell-server/src/lib.rs +++ b/crates/openshell-server/src/lib.rs @@ -10,6 +10,7 @@ //! - mTLS support mod auth; +pub mod cli; mod compute; mod grpc; mod http; diff --git a/crates/openshell-server/src/main.rs b/crates/openshell-server/src/main.rs index ed6c73825..29c4a26fd 100644 --- a/crates/openshell-server/src/main.rs +++ b/crates/openshell-server/src/main.rs @@ -1,221 +1,11 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -//! `OpenShell` Server - gRPC/HTTP server with protocol multiplexing. +//! `OpenShell` Server binary entrypoint. -use clap::Parser; -use miette::{IntoDiagnostic, Result}; -use openshell_core::ComputeDriverKind; -use std::net::SocketAddr; -use std::path::PathBuf; -use tracing::info; -use tracing_subscriber::EnvFilter; - -use openshell_server::{run_server, tracing_bus::TracingLogBus}; - -/// `OpenShell` Server - gRPC and HTTP server with protocol multiplexing. -#[derive(Parser, Debug)] -#[command(name = "openshell-server")] -#[command(version = openshell_core::VERSION)] -#[command(about = "OpenShell gRPC/HTTP server", long_about = None)] -struct Args { - /// Port to bind the server to (all interfaces). - #[arg(long, default_value_t = 8080, env = "OPENSHELL_SERVER_PORT")] - port: u16, - - /// Log level (trace, debug, info, warn, error). - #[arg(long, default_value = "info", env = "OPENSHELL_LOG_LEVEL")] - log_level: String, - - /// Path to TLS certificate file (required unless --disable-tls). - #[arg(long, env = "OPENSHELL_TLS_CERT")] - tls_cert: Option, - - /// Path to TLS private key file (required unless --disable-tls). - #[arg(long, env = "OPENSHELL_TLS_KEY")] - tls_key: Option, - - /// Path to CA certificate for client certificate verification (mTLS). - #[arg(long, env = "OPENSHELL_TLS_CLIENT_CA")] - tls_client_ca: Option, - - /// Database URL for persistence. - #[arg(long, env = "OPENSHELL_DB_URL", required = true)] - db_url: String, - - /// Compute drivers configured for this gateway. - /// - /// Accepts a comma-delimited list such as `kubernetes` or - /// `kubernetes,podman`. The configuration format is future-proofed for - /// multiple drivers, but the gateway currently requires exactly one. - #[arg( - long, - alias = "driver", - env = "OPENSHELL_DRIVERS", - value_delimiter = ',', - default_value = "kubernetes", - value_parser = parse_compute_driver - )] - drivers: Vec, - - /// Kubernetes namespace for sandboxes. - #[arg(long, env = "OPENSHELL_SANDBOX_NAMESPACE", default_value = "default")] - sandbox_namespace: String, - - /// Default container image for sandboxes. - #[arg(long, env = "OPENSHELL_SANDBOX_IMAGE")] - sandbox_image: Option, - - /// Kubernetes imagePullPolicy for sandbox pods (Always, IfNotPresent, Never). - #[arg(long, env = "OPENSHELL_SANDBOX_IMAGE_PULL_POLICY")] - sandbox_image_pull_policy: Option, - - /// gRPC endpoint for sandboxes to callback to `OpenShell`. - /// This should be reachable from within the Kubernetes cluster. - #[arg(long, env = "OPENSHELL_GRPC_ENDPOINT")] - grpc_endpoint: Option, - - /// Public host for the SSH gateway. - #[arg(long, env = "OPENSHELL_SSH_GATEWAY_HOST", default_value = "127.0.0.1")] - ssh_gateway_host: String, - - /// Public port for the SSH gateway. - #[arg(long, env = "OPENSHELL_SSH_GATEWAY_PORT", default_value_t = 8080)] - ssh_gateway_port: u16, - - /// HTTP path for SSH CONNECT/upgrade. - #[arg( - long, - env = "OPENSHELL_SSH_CONNECT_PATH", - default_value = "/connect/ssh" - )] - ssh_connect_path: String, - - /// SSH port inside sandbox pods. - #[arg(long, env = "OPENSHELL_SANDBOX_SSH_PORT", default_value_t = 2222)] - sandbox_ssh_port: u16, - - /// Shared secret for gateway-to-sandbox SSH handshake. - #[arg(long, env = "OPENSHELL_SSH_HANDSHAKE_SECRET")] - ssh_handshake_secret: Option, - - /// Allowed clock skew in seconds for SSH handshake. - #[arg(long, env = "OPENSHELL_SSH_HANDSHAKE_SKEW_SECS", default_value_t = 300)] - ssh_handshake_skew_secs: u64, - - /// Kubernetes secret name containing client TLS materials for sandbox pods. - #[arg(long, env = "OPENSHELL_CLIENT_TLS_SECRET_NAME")] - client_tls_secret_name: Option, - - /// Host gateway IP for sandbox pod hostAliases. - /// When set, sandbox pods get hostAliases entries mapping - /// host.docker.internal and host.openshell.internal to this IP. - #[arg(long, env = "OPENSHELL_HOST_GATEWAY_IP")] - host_gateway_ip: Option, - - /// Disable TLS entirely — listen on plaintext HTTP. - /// Use this when the gateway sits behind a reverse proxy or tunnel - /// (e.g. Cloudflare Tunnel) that terminates TLS at the edge. - #[arg(long, env = "OPENSHELL_DISABLE_TLS")] - disable_tls: bool, - - /// Disable gateway authentication (mTLS client certificate requirement). - /// When set, the TLS handshake accepts connections without a client - /// certificate. Ignored when --disable-tls is set. - #[arg(long, env = "OPENSHELL_DISABLE_GATEWAY_AUTH")] - disable_gateway_auth: bool, -} +use miette::Result; #[tokio::main] async fn main() -> Result<()> { - rustls::crypto::ring::default_provider() - .install_default() - .map_err(|e| miette::miette!("failed to install rustls crypto provider: {e:?}"))?; - - let args = Args::parse(); - - // Initialize tracing - let tracing_log_bus = TracingLogBus::new(); - tracing_log_bus.install_subscriber( - EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(&args.log_level)), - ); - - // Build configuration - let bind = SocketAddr::from(([0, 0, 0, 0], args.port)); - - let tls = if args.disable_tls { - None - } else { - let cert_path = args.tls_cert.ok_or_else(|| { - miette::miette!( - "--tls-cert is required when TLS is enabled (use --disable-tls to skip)" - ) - })?; - let key_path = args.tls_key.ok_or_else(|| { - miette::miette!("--tls-key is required when TLS is enabled (use --disable-tls to skip)") - })?; - let client_ca_path = args.tls_client_ca.ok_or_else(|| { - miette::miette!( - "--tls-client-ca is required when TLS is enabled (use --disable-tls to skip)" - ) - })?; - Some(openshell_core::TlsConfig { - cert_path, - key_path, - client_ca_path, - allow_unauthenticated: args.disable_gateway_auth, - }) - }; - - let mut config = openshell_core::Config::new(tls) - .with_bind_address(bind) - .with_log_level(&args.log_level); - - config = config - .with_database_url(args.db_url) - .with_compute_drivers(args.drivers) - .with_sandbox_namespace(args.sandbox_namespace) - .with_ssh_gateway_host(args.ssh_gateway_host) - .with_ssh_gateway_port(args.ssh_gateway_port) - .with_ssh_connect_path(args.ssh_connect_path) - .with_sandbox_ssh_port(args.sandbox_ssh_port) - .with_ssh_handshake_skew_secs(args.ssh_handshake_skew_secs); - - if let Some(image) = args.sandbox_image { - config = config.with_sandbox_image(image); - } - - if let Some(policy) = args.sandbox_image_pull_policy { - config = config.with_sandbox_image_pull_policy(policy); - } - - if let Some(endpoint) = args.grpc_endpoint { - config = config.with_grpc_endpoint(endpoint); - } - - if let Some(secret) = args.ssh_handshake_secret { - config = config.with_ssh_handshake_secret(secret); - } - - if let Some(name) = args.client_tls_secret_name { - config = config.with_client_tls_secret_name(name); - } - - if let Some(ip) = args.host_gateway_ip { - config = config.with_host_gateway_ip(ip); - } - - if args.disable_tls { - info!("TLS disabled — listening on plaintext HTTP"); - } else if args.disable_gateway_auth { - info!("Gateway auth disabled — accepting connections without client certificates"); - } - - info!(bind = %config.bind_address, "Starting OpenShell server"); - - run_server(config, tracing_log_bus).await.into_diagnostic() -} - -fn parse_compute_driver(value: &str) -> std::result::Result { - value.parse() + openshell_server::cli::run_cli("openshell-server").await } diff --git a/crates/openshell-server/src/persistence/postgres.rs b/crates/openshell-server/src/persistence/postgres.rs index 4b62516c4..509b028d7 100644 --- a/crates/openshell-server/src/persistence/postgres.rs +++ b/crates/openshell-server/src/persistence/postgres.rs @@ -7,7 +7,8 @@ use super::{ use openshell_core::Result; use sqlx::postgres::PgPoolOptions; use sqlx::{PgPool, Row}; -use std::path::PathBuf; + +static POSTGRES_MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate!("./migrations/postgres"); #[derive(Debug, Clone)] pub struct PostgresStore { @@ -26,13 +27,7 @@ impl PostgresStore { } pub async fn migrate(&self) -> Result<()> { - let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("migrations") - .join("postgres"); - let migrator = sqlx::migrate::Migrator::new(path) - .await - .map_err(|e| map_migrate_error(&e))?; - migrator + POSTGRES_MIGRATOR .run(&self.pool) .await .map_err(|e| map_migrate_error(&e)) diff --git a/crates/openshell-server/src/persistence/sqlite.rs b/crates/openshell-server/src/persistence/sqlite.rs index 167f3521d..3ee7799d7 100644 --- a/crates/openshell-server/src/persistence/sqlite.rs +++ b/crates/openshell-server/src/persistence/sqlite.rs @@ -7,9 +7,10 @@ use super::{ use openshell_core::Result; use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; use sqlx::{Row, SqlitePool}; -use std::path::PathBuf; use std::str::FromStr; +static SQLITE_MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate!("./migrations/sqlite"); + #[derive(Debug, Clone)] pub struct SqliteStore { pool: SqlitePool, @@ -38,13 +39,7 @@ impl SqliteStore { } pub async fn migrate(&self) -> Result<()> { - let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("migrations") - .join("sqlite"); - let migrator = sqlx::migrate::Migrator::new(path) - .await - .map_err(|e| map_migrate_error(&e))?; - migrator + SQLITE_MIGRATOR .run(&self.pool) .await .map_err(|e| map_migrate_error(&e)) diff --git a/crates/openshell-server/src/persistence/tests.rs b/crates/openshell-server/src/persistence/tests.rs index a5223a9f4..b3bfe2fa4 100644 --- a/crates/openshell-server/src/persistence/tests.rs +++ b/crates/openshell-server/src/persistence/tests.rs @@ -22,6 +22,16 @@ async fn sqlite_put_get_round_trip() { assert_eq!(record.payload, b"payload"); } +#[tokio::test] +async fn sqlite_connect_runs_embedded_migrations() { + let store = Store::connect("sqlite::memory:?cache=shared") + .await + .unwrap(); + + let records = store.list("sandbox", 10, 0).await.unwrap(); + assert!(records.is_empty()); +} + #[tokio::test] async fn sqlite_updates_timestamp() { let store = Store::connect("sqlite::memory:?cache=shared") diff --git a/deploy/docker/Dockerfile.gateway-macos b/deploy/docker/Dockerfile.gateway-macos new file mode 100644 index 000000000..b6e24684d --- /dev/null +++ b/deploy/docker/Dockerfile.gateway-macos @@ -0,0 +1,107 @@ +# syntax=docker/dockerfile:1.6 + +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Cross-compile the standalone openshell-gateway binary for macOS aarch64 +# (Apple Silicon) using the osxcross toolchain. + +ARG OSXCROSS_IMAGE=crazymax/osxcross:latest + +FROM ${OSXCROSS_IMAGE} AS osxcross + +FROM python:3.12-slim AS builder + +ARG CARGO_TARGET_CACHE_SCOPE=default + +ENV PATH="/root/.cargo/bin:/usr/local/bin:/osxcross/bin:${PATH}" +ENV LD_LIBRARY_PATH="/osxcross/lib" + +COPY --from=osxcross /osxcross /osxcross + +RUN SDKROOT="$(echo /osxcross/SDK/MacOSX*.sdk)" && ln -sfn "${SDKROOT}" /osxcross/SDK/MacOSX.sdk + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + clang \ + cmake \ + curl \ + libclang-dev \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* + +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.88.0 + +RUN ln -sf /osxcross/bin/arm64-apple-darwin25.1-ld /usr/local/bin/arm64-apple-macosx-ld + +RUN rustup target add aarch64-apple-darwin + +WORKDIR /build + +ENV CC_aarch64_apple_darwin=oa64-clang +ENV CXX_aarch64_apple_darwin=oa64-clang++ +ENV AR_aarch64_apple_darwin=aarch64-apple-darwin25.1-ar +ENV CARGO_TARGET_AARCH64_APPLE_DARWIN_LINKER=oa64-clang +ENV CARGO_TARGET_AARCH64_APPLE_DARWIN_AR=aarch64-apple-darwin25.1-ar +ENV SDKROOT=/osxcross/SDK/MacOSX.sdk +ENV MACOSX_DEPLOYMENT_TARGET=13.3 +ENV CFLAGS_aarch64_apple_darwin=--target=arm64-apple-macosx\ -mmacosx-version-min=13.3 +ENV CXXFLAGS_aarch64_apple_darwin=--target=arm64-apple-macosx\ -mmacosx-version-min=13.3 +ENV BINDGEN_EXTRA_CLANG_ARGS_aarch64_apple_darwin=--target=arm64-apple-macosx\ -isysroot\ ${SDKROOT} + +COPY Cargo.toml Cargo.lock ./ +COPY crates/openshell-core/Cargo.toml crates/openshell-core/Cargo.toml +COPY crates/openshell-driver-kubernetes/Cargo.toml crates/openshell-driver-kubernetes/Cargo.toml +COPY crates/openshell-policy/Cargo.toml crates/openshell-policy/Cargo.toml +COPY crates/openshell-router/Cargo.toml crates/openshell-router/Cargo.toml +COPY crates/openshell-server/Cargo.toml crates/openshell-server/Cargo.toml +COPY crates/openshell-core/build.rs crates/openshell-core/build.rs +COPY proto/ proto/ + +RUN sed -i 's|members = \["crates/\*"\]|members = ["crates/openshell-server", "crates/openshell-core", "crates/openshell-driver-kubernetes", "crates/openshell-policy", "crates/openshell-router"]|' Cargo.toml + +RUN mkdir -p crates/openshell-core/src \ + crates/openshell-driver-kubernetes/src \ + crates/openshell-policy/src \ + crates/openshell-router/src \ + crates/openshell-server/src/bin && \ + touch crates/openshell-core/src/lib.rs && \ + touch crates/openshell-driver-kubernetes/src/lib.rs && \ + printf 'fn main() {}\n' > crates/openshell-driver-kubernetes/src/main.rs && \ + touch crates/openshell-policy/src/lib.rs && \ + touch crates/openshell-router/src/lib.rs && \ + touch crates/openshell-server/src/lib.rs && \ + printf 'fn main() {}\n' > crates/openshell-server/src/main.rs && \ + printf 'fn main() {}\n' > crates/openshell-server/src/bin/openshell-gateway.rs + +RUN --mount=type=cache,id=cargo-registry-gateway-macos,sharing=locked,target=/root/.cargo/registry \ + --mount=type=cache,id=cargo-git-gateway-macos,sharing=locked,target=/root/.cargo/git \ + --mount=type=cache,id=cargo-target-gateway-macos-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \ + cargo build --release --target aarch64-apple-darwin -p openshell-server --bin openshell-gateway 2>/dev/null || true + +COPY crates/ crates/ + +RUN touch crates/openshell-core/src/lib.rs \ + crates/openshell-driver-kubernetes/src/lib.rs \ + crates/openshell-driver-kubernetes/src/main.rs \ + crates/openshell-policy/src/lib.rs \ + crates/openshell-router/src/lib.rs \ + crates/openshell-server/src/lib.rs \ + crates/openshell-server/src/main.rs \ + crates/openshell-server/src/bin/openshell-gateway.rs \ + crates/openshell-core/build.rs \ + proto/*.proto + +ARG OPENSHELL_CARGO_VERSION +RUN --mount=type=cache,id=cargo-registry-gateway-macos,sharing=locked,target=/root/.cargo/registry \ + --mount=type=cache,id=cargo-git-gateway-macos,sharing=locked,target=/root/.cargo/git \ + --mount=type=cache,id=cargo-target-gateway-macos-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \ + if [ -n "${OPENSHELL_CARGO_VERSION:-}" ]; then \ + sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${OPENSHELL_CARGO_VERSION}"'"/}' Cargo.toml; \ + fi && \ + cargo build --release --target aarch64-apple-darwin -p openshell-server --bin openshell-gateway && \ + cp target/aarch64-apple-darwin/release/openshell-gateway /openshell-gateway + +FROM scratch AS binary +COPY --from=builder /openshell-gateway /openshell-gateway diff --git a/docs/reference/support-matrix.mdx b/docs/reference/support-matrix.mdx index 744fba53c..c5eeee567 100644 --- a/docs/reference/support-matrix.mdx +++ b/docs/reference/support-matrix.mdx @@ -10,7 +10,7 @@ This page lists the platform, software, runtime, and kernel requirements for run ## Supported Platforms -OpenShell publishes multi-architecture container images for `linux/amd64` and `linux/arm64`. The CLI is supported on the following host platforms: +OpenShell publishes multi-architecture container images for `linux/amd64` and `linux/arm64`. The CLI and standalone gateway binary are supported on the following host platforms: | Platform | Architecture | Status | | -------------------------------- | --------------------- | --------- | @@ -19,6 +19,18 @@ OpenShell publishes multi-architecture container images for `linux/amd64` and `l | macOS (Docker Desktop) | Apple Silicon (arm64) | Supported | | Windows (WSL 2 + Docker Desktop) | x86_64 | Experimental | +## Standalone Gateway Binary + +OpenShell publishes standalone `openshell-gateway` release assets for manual download on these platforms: + +| Platform | Artifact pattern | +| --------------------- | ---------------------------------------------- | +| Linux x86_64 (amd64) | `openshell-gateway-x86_64-unknown-linux-gnu` | +| Linux aarch64 (arm64) | `openshell-gateway-aarch64-unknown-linux-gnu` | +| macOS Apple Silicon | `openshell-gateway-aarch64-apple-darwin` | + +These artifacts are attached to GitHub releases. `openshell gateway start` continues to use the published cluster and gateway container images. + ## Software Prerequisites The following software must be installed on the host before using the OpenShell CLI: From a48cad6e3246651f9c8d70fa506b585e235a2400 Mon Sep 17 00:00:00 2001 From: Drew Newberry Date: Thu, 16 Apr 2026 00:15:03 -0700 Subject: [PATCH 2/9] cleanup copy --- .github/workflows/release-dev.yml | 4 ---- .github/workflows/release-tag.yml | 4 ---- 2 files changed, 8 deletions(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 16650bfce..afc1563ef 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -664,10 +664,6 @@ jobs: curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install.sh | OPENSHELL_VERSION=dev sh ``` - ### Standalone gateway binary - - Manual-download `openshell-gateway` tarballs are attached for Linux amd64/arm64 and macOS ARM64. This release track does not yet include a gateway installer. - files: | release/openshell-x86_64-unknown-linux-musl.tar.gz release/openshell-aarch64-unknown-linux-musl.tar.gz diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 6ab0895d9..9ae661114 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -636,10 +636,6 @@ jobs: curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install.sh | OPENSHELL_VERSION=${{ env.RELEASE_TAG }} sh ``` - ### Standalone gateway binary - - Manual-download `openshell-gateway` tarballs are attached for Linux amd64/arm64 and macOS ARM64. This release track does not yet include a gateway installer. - files: | release/openshell-x86_64-unknown-linux-musl.tar.gz release/openshell-aarch64-unknown-linux-musl.tar.gz From 337d558e455122f774184b532ff6b0d5c276a59f Mon Sep 17 00:00:00 2001 From: Drew Newberry Date: Thu, 16 Apr 2026 09:08:51 -0700 Subject: [PATCH 3/9] feat(release): publish standalone supervisor binary and container image - Add build-supervisor-binary-linux job for amd64/arm64 standalone binaries - Add supervisor Docker image target to Dockerfile.images - Add build-supervisor docker build job and GHCR tagging - Wire supervisor artifacts into GitHub Release (tarballs + checksums) - Remove unnecessary workspace scoping sed from CLI and gateway builds --- .github/workflows/docker-build.yml | 2 +- .github/workflows/release-tag.yml | 122 +++++++++++++++++++++++++--- deploy/docker/Dockerfile.images | 21 +++++ tasks/docker.toml | 10 +++ tasks/scripts/docker-build-image.sh | 7 +- 5 files changed, 147 insertions(+), 15 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 16a8447c9..5afbabd61 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -4,7 +4,7 @@ on: workflow_call: inputs: component: - description: "Component to build (gateway, cluster)" + description: "Component to build (gateway, supervisor, cluster)" required: true type: string timeout-minutes: diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 9ae661114..7271de28b 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -69,6 +69,13 @@ jobs: component: gateway cargo-version: ${{ needs.compute-versions.outputs.cargo_version }} + build-supervisor: + needs: [compute-versions] + uses: ./.github/workflows/docker-build.yml + with: + component: supervisor + cargo-version: ${{ needs.compute-versions.outputs.cargo_version }} + build-cluster: needs: [compute-versions] uses: ./.github/workflows/docker-build.yml @@ -85,7 +92,7 @@ jobs: tag-ghcr-release: name: Tag GHCR Images for Release - needs: [compute-versions, build-gateway, build-cluster, e2e] + needs: [compute-versions, build-gateway, build-supervisor, build-cluster, e2e] runs-on: build-amd64 timeout-minutes: 10 steps: @@ -97,7 +104,7 @@ jobs: set -euo pipefail REGISTRY="ghcr.io/nvidia/openshell" VERSION="${{ needs.compute-versions.outputs.semver }}" - for component in gateway cluster; do + for component in gateway supervisor cluster; do echo "Tagging ${REGISTRY}/${component}:${{ github.sha }} as ${VERSION} and latest..." docker buildx imagetools create \ --prefer-index=false \ @@ -305,11 +312,6 @@ jobs: # Override z3-sys default (stdc++) so Rust links the matching runtime. echo "CXXSTDLIB=c++" >> "$GITHUB_ENV" - - name: Scope workspace to CLI crates - run: | - set -euo pipefail - sed -i 's|members = \["crates/\*"\]|members = ["crates/openshell-cli", "crates/openshell-core", "crates/openshell-bootstrap", "crates/openshell-policy", "crates/openshell-prover", "crates/openshell-providers", "crates/openshell-tui"]|' Cargo.toml - - name: Patch workspace version if: needs.compute-versions.outputs.cargo_version != '' run: | @@ -450,11 +452,6 @@ jobs: cache-directories: .cache/sccache cache-targets: "true" - - name: Scope workspace to gateway crates - run: | - set -euo pipefail - sed -i 's|members = \["crates/\*"\]|members = ["crates/openshell-server", "crates/openshell-core", "crates/openshell-driver-kubernetes", "crates/openshell-policy", "crates/openshell-router"]|' Cargo.toml - - name: Patch workspace version if: needs.compute-versions.outputs.cargo_version != '' run: | @@ -492,6 +489,91 @@ jobs: path: artifacts/*.tar.gz retention-days: 5 + # --------------------------------------------------------------------------- + # Build standalone supervisor binaries (Linux GNU — native on each arch) + # --------------------------------------------------------------------------- + build-supervisor-binary-linux: + name: Build Supervisor Binary (Linux ${{ matrix.arch }}) + needs: [compute-versions] + strategy: + matrix: + include: + - arch: amd64 + runner: build-amd64 + target: x86_64-unknown-linux-gnu + - arch: arm64 + runner: build-arm64 + target: aarch64-unknown-linux-gnu + runs-on: ${{ matrix.runner }} + timeout-minutes: 60 + container: + image: ghcr.io/nvidia/openshell/ci:latest + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + options: --privileged + env: + MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SCCACHE_MEMCACHED_ENDPOINT: ${{ vars.SCCACHE_MEMCACHED_ENDPOINT }} + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.tag || github.ref }} + fetch-depth: 0 + + - name: Mark workspace safe for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + + - name: Fetch tags + run: git fetch --tags --force + + - name: Install tools + run: mise install + + - name: Cache Rust target and registry + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + with: + shared-key: supervisor-binary-gnu-${{ matrix.arch }} + cache-directories: .cache/sccache + cache-targets: "true" + + - name: Patch workspace version + if: needs.compute-versions.outputs.cargo_version != '' + run: | + set -euo pipefail + sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${{ needs.compute-versions.outputs.cargo_version }}"'"/}' Cargo.toml + + - name: Build ${{ matrix.target }} + run: | + set -euo pipefail + mise x -- cargo build --release --target ${{ matrix.target }} -p openshell-sandbox --bin openshell-sandbox + + - name: Verify packaged binary + run: | + set -euo pipefail + OUTPUT="$(target/${{ matrix.target }}/release/openshell-sandbox --version)" + echo "$OUTPUT" + grep -q '^openshell-sandbox ' <<<"$OUTPUT" + + - name: sccache stats + if: always() + run: mise x -- sccache --show-stats + + - name: Package binary + run: | + set -euo pipefail + mkdir -p artifacts + tar -czf artifacts/openshell-sandbox-${{ matrix.target }}.tar.gz \ + -C target/${{ matrix.target }}/release openshell-sandbox + ls -lh artifacts/ + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: supervisor-binary-linux-${{ matrix.arch }} + path: artifacts/*.tar.gz + retention-days: 5 + # --------------------------------------------------------------------------- # Build standalone gateway binary (macOS aarch64 via osxcross) # --------------------------------------------------------------------------- @@ -565,7 +647,7 @@ jobs: # --------------------------------------------------------------------------- release: name: Release - needs: [compute-versions, build-cli-linux, build-cli-macos, build-gateway-binary-linux, build-gateway-binary-macos, build-python-wheels-linux, build-python-wheel-macos, tag-ghcr-release] + needs: [compute-versions, build-cli-linux, build-cli-macos, build-gateway-binary-linux, build-gateway-binary-macos, build-supervisor-binary-linux, build-python-wheels-linux, build-python-wheel-macos, tag-ghcr-release] runs-on: build-amd64 timeout-minutes: 10 outputs: @@ -589,6 +671,13 @@ jobs: path: release/ merge-multiple: true + - name: Download supervisor binary artifacts + uses: actions/download-artifact@v4 + with: + pattern: supervisor-binary-* + path: release/ + merge-multiple: true + - name: Download wheel artifacts uses: actions/download-artifact@v4 with: @@ -619,6 +708,10 @@ jobs: openshell-gateway-aarch64-unknown-linux-gnu.tar.gz \ openshell-gateway-aarch64-apple-darwin.tar.gz > openshell-gateway-checksums-sha256.txt cat openshell-gateway-checksums-sha256.txt + sha256sum \ + openshell-sandbox-x86_64-unknown-linux-gnu.tar.gz \ + openshell-sandbox-aarch64-unknown-linux-gnu.tar.gz > openshell-sandbox-checksums-sha256.txt + cat openshell-sandbox-checksums-sha256.txt - name: Create GitHub Release uses: softprops/action-gh-release@v2 @@ -643,9 +736,12 @@ jobs: release/openshell-gateway-x86_64-unknown-linux-gnu.tar.gz release/openshell-gateway-aarch64-unknown-linux-gnu.tar.gz release/openshell-gateway-aarch64-apple-darwin.tar.gz + release/openshell-sandbox-x86_64-unknown-linux-gnu.tar.gz + release/openshell-sandbox-aarch64-unknown-linux-gnu.tar.gz release/*.whl release/openshell-checksums-sha256.txt release/openshell-gateway-checksums-sha256.txt + release/openshell-sandbox-checksums-sha256.txt publish-fern-docs: name: Publish Fern Docs diff --git a/deploy/docker/Dockerfile.images b/deploy/docker/Dockerfile.images index b7e854677..ad8779b0b 100644 --- a/deploy/docker/Dockerfile.images +++ b/deploy/docker/Dockerfile.images @@ -7,6 +7,7 @@ # # Targets: # gateway Final gateway image +# supervisor Final supervisor image # cluster Final cluster image # gateway-builder Release openshell-server binary # supervisor-builder Release openshell-sandbox binary @@ -201,6 +202,26 @@ EXPOSE 8080 ENTRYPOINT ["openshell-server"] CMD ["--port", "8080"] +# --------------------------------------------------------------------------- +# Final supervisor image +# --------------------------------------------------------------------------- +FROM nvcr.io/nvidia/base/ubuntu:noble-20251013 AS supervisor + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates && \ + apt-get install -y --only-upgrade gpgv && \ + rm -rf /var/lib/apt/lists/* + +RUN useradd --create-home --user-group openshell + +WORKDIR /app + +COPY --from=supervisor-builder /build/out/openshell-sandbox /usr/local/bin/ + +USER openshell + +ENTRYPOINT ["openshell-sandbox"] + # --------------------------------------------------------------------------- # Cluster asset stages # --------------------------------------------------------------------------- diff --git a/tasks/docker.toml b/tasks/docker.toml index b4e370ff3..da5bd24e4 100644 --- a/tasks/docker.toml +++ b/tasks/docker.toml @@ -36,6 +36,16 @@ description = "Alias for build:docker:gateway" depends = ["build:docker:gateway"] hide = true +["build:docker:supervisor"] +description = "Build the supervisor Docker image" +run = "tasks/scripts/docker-build-image.sh supervisor" +hide = true + +["docker:build:supervisor"] +description = "Alias for build:docker:supervisor" +depends = ["build:docker:supervisor"] +hide = true + ["build:docker:cluster"] description = "Build the k3s cluster image (component images pulled at runtime from registry)" run = "tasks/scripts/docker-build-image.sh cluster" diff --git a/tasks/scripts/docker-build-image.sh b/tasks/scripts/docker-build-image.sh index 3675d7549..e429d0339 100755 --- a/tasks/scripts/docker-build-image.sh +++ b/tasks/scripts/docker-build-image.sh @@ -38,7 +38,7 @@ detect_rust_scope() { echo "no-rust" } -TARGET=${1:?"Usage: docker-build-image.sh [extra-args...]"} +TARGET=${1:?"Usage: docker-build-image.sh [extra-args...]"} shift DOCKERFILE="deploy/docker/Dockerfile.images" @@ -56,6 +56,11 @@ case "${TARGET}" in IMAGE_NAME="openshell/gateway" DOCKER_TARGET="gateway" ;; + supervisor) + IS_FINAL_IMAGE=1 + IMAGE_NAME="openshell/supervisor" + DOCKER_TARGET="supervisor" + ;; cluster) IS_FINAL_IMAGE=1 IMAGE_NAME="openshell/cluster" From 7310cf73c947ac4c466a12a9a1246af8c950ccec Mon Sep 17 00:00:00 2001 From: Drew Newberry Date: Thu, 16 Apr 2026 09:12:20 -0700 Subject: [PATCH 4/9] feat(release): add supervisor to dev release workflow Mirror release-tag changes: build supervisor Docker image, standalone Linux binaries (amd64/arm64), GHCR dev tagging, checksums, and release assets. Also remove unnecessary workspace scoping sed from CLI and gateway builds. --- .github/workflows/release-dev.yml | 121 ++++++++++++++++++++++++++---- 1 file changed, 108 insertions(+), 13 deletions(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index afc1563ef..a980098e8 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -54,6 +54,13 @@ jobs: component: gateway cargo-version: ${{ needs.compute-versions.outputs.cargo_version }} + build-supervisor: + needs: [compute-versions] + uses: ./.github/workflows/docker-build.yml + with: + component: supervisor + cargo-version: ${{ needs.compute-versions.outputs.cargo_version }} + build-cluster: needs: [compute-versions] uses: ./.github/workflows/docker-build.yml @@ -70,7 +77,7 @@ jobs: tag-ghcr-dev: name: Tag GHCR Images as Dev - needs: [build-gateway, build-cluster] + needs: [build-gateway, build-supervisor, build-cluster] runs-on: build-amd64 timeout-minutes: 10 steps: @@ -81,7 +88,7 @@ jobs: run: | set -euo pipefail REGISTRY="ghcr.io/nvidia/openshell" - for component in gateway cluster; do + for component in gateway supervisor cluster; do echo "Tagging ${REGISTRY}/${component}:${{ github.sha }} as dev..." docker buildx imagetools create \ --prefer-index=false \ @@ -282,11 +289,6 @@ jobs: # Override z3-sys default (stdc++) so Rust links the matching runtime. echo "CXXSTDLIB=c++" >> "$GITHUB_ENV" - - name: Scope workspace to CLI crates - run: | - set -euo pipefail - sed -i 's|members = \["crates/\*"\]|members = ["crates/openshell-cli", "crates/openshell-core", "crates/openshell-bootstrap", "crates/openshell-policy", "crates/openshell-prover", "crates/openshell-providers", "crates/openshell-tui"]|' Cargo.toml - - name: Patch workspace version if: needs.compute-versions.outputs.cargo_version != '' run: | @@ -425,11 +427,6 @@ jobs: cache-directories: .cache/sccache cache-targets: "true" - - name: Scope workspace to gateway crates - run: | - set -euo pipefail - sed -i 's|members = \["crates/\*"\]|members = ["crates/openshell-server", "crates/openshell-core", "crates/openshell-driver-kubernetes", "crates/openshell-policy", "crates/openshell-router"]|' Cargo.toml - - name: Patch workspace version if: needs.compute-versions.outputs.cargo_version != '' run: | @@ -534,12 +531,96 @@ jobs: path: artifacts/*.tar.gz retention-days: 5 + # --------------------------------------------------------------------------- + # Build standalone supervisor binaries (Linux GNU — native on each arch) + # --------------------------------------------------------------------------- + build-supervisor-binary-linux: + name: Build Supervisor Binary (Linux ${{ matrix.arch }}) + needs: [compute-versions] + strategy: + matrix: + include: + - arch: amd64 + runner: build-amd64 + target: x86_64-unknown-linux-gnu + - arch: arm64 + runner: build-arm64 + target: aarch64-unknown-linux-gnu + runs-on: ${{ matrix.runner }} + timeout-minutes: 60 + container: + image: ghcr.io/nvidia/openshell/ci:latest + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + options: --privileged + env: + MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SCCACHE_MEMCACHED_ENDPOINT: ${{ vars.SCCACHE_MEMCACHED_ENDPOINT }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Mark workspace safe for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + + - name: Fetch tags + run: git fetch --tags --force + + - name: Install tools + run: mise install + + - name: Cache Rust target and registry + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + with: + shared-key: supervisor-binary-gnu-${{ matrix.arch }} + cache-directories: .cache/sccache + cache-targets: "true" + + - name: Patch workspace version + if: needs.compute-versions.outputs.cargo_version != '' + run: | + set -euo pipefail + sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${{ needs.compute-versions.outputs.cargo_version }}"'"/}' Cargo.toml + + - name: Build ${{ matrix.target }} + run: | + set -euo pipefail + mise x -- cargo build --release --target ${{ matrix.target }} -p openshell-sandbox --bin openshell-sandbox + + - name: Verify packaged binary + run: | + set -euo pipefail + OUTPUT="$(target/${{ matrix.target }}/release/openshell-sandbox --version)" + echo "$OUTPUT" + grep -q '^openshell-sandbox ' <<<"$OUTPUT" + + - name: sccache stats + if: always() + run: mise x -- sccache --show-stats + + - name: Package binary + run: | + set -euo pipefail + mkdir -p artifacts + tar -czf artifacts/openshell-sandbox-${{ matrix.target }}.tar.gz \ + -C target/${{ matrix.target }}/release openshell-sandbox + ls -lh artifacts/ + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: supervisor-binary-linux-${{ matrix.arch }} + path: artifacts/*.tar.gz + retention-days: 5 + # --------------------------------------------------------------------------- # Create / update the dev GitHub Release with CLI binaries and wheels # --------------------------------------------------------------------------- release-dev: name: Release Dev - needs: [compute-versions, build-cli-linux, build-cli-macos, build-gateway-binary-linux, build-gateway-binary-macos, build-python-wheels-linux, build-python-wheel-macos] + needs: [compute-versions, build-cli-linux, build-cli-macos, build-gateway-binary-linux, build-gateway-binary-macos, build-supervisor-binary-linux, build-python-wheels-linux, build-python-wheel-macos] runs-on: build-amd64 timeout-minutes: 10 outputs: @@ -561,6 +642,13 @@ jobs: path: release/ merge-multiple: true + - name: Download supervisor binary artifacts + uses: actions/download-artifact@v4 + with: + pattern: supervisor-binary-* + path: release/ + merge-multiple: true + - name: Download wheel artifacts uses: actions/download-artifact@v4 with: @@ -591,6 +679,10 @@ jobs: openshell-gateway-aarch64-unknown-linux-gnu.tar.gz \ openshell-gateway-aarch64-apple-darwin.tar.gz > openshell-gateway-checksums-sha256.txt cat openshell-gateway-checksums-sha256.txt + sha256sum \ + openshell-sandbox-x86_64-unknown-linux-gnu.tar.gz \ + openshell-sandbox-aarch64-unknown-linux-gnu.tar.gz > openshell-sandbox-checksums-sha256.txt + cat openshell-sandbox-checksums-sha256.txt - name: Prune stale wheel assets from dev release uses: actions/github-script@v7 @@ -671,9 +763,12 @@ jobs: release/openshell-gateway-x86_64-unknown-linux-gnu.tar.gz release/openshell-gateway-aarch64-unknown-linux-gnu.tar.gz release/openshell-gateway-aarch64-apple-darwin.tar.gz + release/openshell-sandbox-x86_64-unknown-linux-gnu.tar.gz + release/openshell-sandbox-aarch64-unknown-linux-gnu.tar.gz release/*.whl release/openshell-checksums-sha256.txt release/openshell-gateway-checksums-sha256.txt + release/openshell-sandbox-checksums-sha256.txt trigger-wheel-publish: name: Trigger Wheel Publish From 2765a5af746532284e8d4e5eb8a5ccc10741b11a Mon Sep 17 00:00:00 2001 From: Drew Newberry Date: Thu, 16 Apr 2026 11:11:06 -0700 Subject: [PATCH 5/9] cleanup --- deploy/docker/Dockerfile.images | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deploy/docker/Dockerfile.images b/deploy/docker/Dockerfile.images index ad8779b0b..21610cfc8 100644 --- a/deploy/docker/Dockerfile.images +++ b/deploy/docker/Dockerfile.images @@ -73,6 +73,7 @@ RUN mkdir -p \ crates/openshell-router/src \ crates/openshell-sandbox/src \ crates/openshell-server/src \ + crates/openshell-server/src/bin \ crates/openshell-tui/src \ crates/openshell-vm/src && \ touch crates/openshell-bootstrap/src/lib.rs && \ @@ -89,6 +90,7 @@ RUN mkdir -p \ printf 'fn main() {}\n' > crates/openshell-sandbox/src/main.rs && \ touch crates/openshell-server/src/lib.rs && \ printf 'fn main() {}\n' > crates/openshell-server/src/main.rs && \ + printf 'fn main() {}\n' > crates/openshell-server/src/bin/openshell-gateway.rs && \ touch crates/openshell-tui/src/lib.rs && \ touch crates/openshell-vm/src/lib.rs && \ printf 'fn main() {}\n' > crates/openshell-vm/src/main.rs @@ -123,6 +125,7 @@ COPY crates/openshell-server/ crates/openshell-server/ RUN touch \ crates/openshell-core/build.rs \ crates/openshell-server/src/main.rs \ + crates/openshell-server/src/bin/openshell-gateway.rs \ proto/*.proto && \ if [ -n "${OPENSHELL_CARGO_VERSION:-}" ]; then \ sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${OPENSHELL_CARGO_VERSION}"'"/}' Cargo.toml; \ From 4fa37c7910ea5f6998c93107d8c560e099055cad Mon Sep 17 00:00:00 2001 From: Drew Newberry Date: Thu, 16 Apr 2026 13:51:10 -0700 Subject: [PATCH 6/9] refactor(server): consolidate to single openshell-server binary Remove the separate openshell-gateway binary entrypoint. The openshell-server crate now produces a single binary (openshell-server) from src/main.rs. Simplify cli.rs by removing the program_name parameter. Rename standalone release artifacts from openshell-gateway-* to openshell-server-*. Rename Dockerfile.gateway-macos to Dockerfile.server-macos. Remove unused docker:build:supervisor task alias. --- .github/workflows/release-dev.yml | 34 +++++++++--------- .github/workflows/release-tag.yml | 34 +++++++++--------- architecture/build-containers.md | 8 ++--- crates/openshell-server/Cargo.toml | 4 --- .../src/bin/openshell-gateway.rs | 9 ----- crates/openshell-server/src/cli.rs | 35 +++++++------------ crates/openshell-server/src/main.rs | 2 +- crates/openshell-server/src/tracing_bus.rs | 2 +- deploy/docker/Dockerfile.images | 5 +-- ....gateway-macos => Dockerfile.server-macos} | 27 +++++++------- tasks/docker.toml | 5 --- 11 files changed, 66 insertions(+), 99 deletions(-) delete mode 100644 crates/openshell-server/src/bin/openshell-gateway.rs rename deploy/docker/{Dockerfile.gateway-macos => Dockerfile.server-macos} (77%) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index a980098e8..7acf326d6 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -436,14 +436,14 @@ jobs: - name: Build ${{ matrix.target }} run: | set -euo pipefail - mise x -- cargo build --release --target ${{ matrix.target }} -p openshell-server --bin openshell-gateway + mise x -- cargo build --release --target ${{ matrix.target }} -p openshell-server --bin openshell-server - name: Verify packaged binary run: | set -euo pipefail - OUTPUT="$(target/${{ matrix.target }}/release/openshell-gateway --version)" + OUTPUT="$(target/${{ matrix.target }}/release/openshell-server --version)" echo "$OUTPUT" - grep -q '^openshell-gateway ' <<<"$OUTPUT" + grep -q '^openshell-server ' <<<"$OUTPUT" - name: sccache stats if: always() @@ -453,8 +453,8 @@ jobs: run: | set -euo pipefail mkdir -p artifacts - tar -czf artifacts/openshell-gateway-${{ matrix.target }}.tar.gz \ - -C target/${{ matrix.target }}/release openshell-gateway + tar -czf artifacts/openshell-server-${{ matrix.target }}.tar.gz \ + -C target/${{ matrix.target }}/release openshell-server ls -lh artifacts/ - name: Upload artifact @@ -504,7 +504,7 @@ jobs: run: | set -euo pipefail docker buildx build \ - --file deploy/docker/Dockerfile.gateway-macos \ + --file deploy/docker/Dockerfile.server-macos \ --build-arg OPENSHELL_CARGO_VERSION="${{ needs.compute-versions.outputs.cargo_version }}" \ --build-arg CARGO_TARGET_CACHE_SCOPE="${{ github.sha }}" \ --target binary \ @@ -514,14 +514,14 @@ jobs: - name: Verify packaged binary shape run: | set -euo pipefail - test -x out/openshell-gateway + test -x out/openshell-server - name: Package binary run: | set -euo pipefail mkdir -p artifacts - tar -czf artifacts/openshell-gateway-aarch64-apple-darwin.tar.gz \ - -C out openshell-gateway + tar -czf artifacts/openshell-server-aarch64-apple-darwin.tar.gz \ + -C out openshell-server ls -lh artifacts/ - name: Upload artifact @@ -675,10 +675,10 @@ jobs: *.whl > openshell-checksums-sha256.txt cat openshell-checksums-sha256.txt sha256sum \ - openshell-gateway-x86_64-unknown-linux-gnu.tar.gz \ - openshell-gateway-aarch64-unknown-linux-gnu.tar.gz \ - openshell-gateway-aarch64-apple-darwin.tar.gz > openshell-gateway-checksums-sha256.txt - cat openshell-gateway-checksums-sha256.txt + openshell-server-x86_64-unknown-linux-gnu.tar.gz \ + openshell-server-aarch64-unknown-linux-gnu.tar.gz \ + openshell-server-aarch64-apple-darwin.tar.gz > openshell-server-checksums-sha256.txt + cat openshell-server-checksums-sha256.txt sha256sum \ openshell-sandbox-x86_64-unknown-linux-gnu.tar.gz \ openshell-sandbox-aarch64-unknown-linux-gnu.tar.gz > openshell-sandbox-checksums-sha256.txt @@ -760,14 +760,14 @@ jobs: release/openshell-x86_64-unknown-linux-musl.tar.gz release/openshell-aarch64-unknown-linux-musl.tar.gz release/openshell-aarch64-apple-darwin.tar.gz - release/openshell-gateway-x86_64-unknown-linux-gnu.tar.gz - release/openshell-gateway-aarch64-unknown-linux-gnu.tar.gz - release/openshell-gateway-aarch64-apple-darwin.tar.gz + release/openshell-server-x86_64-unknown-linux-gnu.tar.gz + release/openshell-server-aarch64-unknown-linux-gnu.tar.gz + release/openshell-server-aarch64-apple-darwin.tar.gz release/openshell-sandbox-x86_64-unknown-linux-gnu.tar.gz release/openshell-sandbox-aarch64-unknown-linux-gnu.tar.gz release/*.whl release/openshell-checksums-sha256.txt - release/openshell-gateway-checksums-sha256.txt + release/openshell-server-checksums-sha256.txt release/openshell-sandbox-checksums-sha256.txt trigger-wheel-publish: diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 7271de28b..426e0091b 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -461,14 +461,14 @@ jobs: - name: Build ${{ matrix.target }} run: | set -euo pipefail - mise x -- cargo build --release --target ${{ matrix.target }} -p openshell-server --bin openshell-gateway + mise x -- cargo build --release --target ${{ matrix.target }} -p openshell-server --bin openshell-server - name: Verify packaged binary run: | set -euo pipefail - OUTPUT="$(target/${{ matrix.target }}/release/openshell-gateway --version)" + OUTPUT="$(target/${{ matrix.target }}/release/openshell-server --version)" echo "$OUTPUT" - grep -q '^openshell-gateway ' <<<"$OUTPUT" + grep -q '^openshell-server ' <<<"$OUTPUT" - name: sccache stats if: always() @@ -478,8 +478,8 @@ jobs: run: | set -euo pipefail mkdir -p artifacts - tar -czf artifacts/openshell-gateway-${{ matrix.target }}.tar.gz \ - -C target/${{ matrix.target }}/release openshell-gateway + tar -czf artifacts/openshell-server-${{ matrix.target }}.tar.gz \ + -C target/${{ matrix.target }}/release openshell-server ls -lh artifacts/ - name: Upload artifact @@ -615,7 +615,7 @@ jobs: run: | set -euo pipefail docker buildx build \ - --file deploy/docker/Dockerfile.gateway-macos \ + --file deploy/docker/Dockerfile.server-macos \ --build-arg OPENSHELL_CARGO_VERSION="${{ needs.compute-versions.outputs.cargo_version }}" \ --build-arg CARGO_TARGET_CACHE_SCOPE="${{ github.sha }}" \ --target binary \ @@ -625,14 +625,14 @@ jobs: - name: Verify packaged binary shape run: | set -euo pipefail - test -x out/openshell-gateway + test -x out/openshell-server - name: Package binary run: | set -euo pipefail mkdir -p artifacts - tar -czf artifacts/openshell-gateway-aarch64-apple-darwin.tar.gz \ - -C out openshell-gateway + tar -czf artifacts/openshell-server-aarch64-apple-darwin.tar.gz \ + -C out openshell-server ls -lh artifacts/ - name: Upload artifact @@ -704,10 +704,10 @@ jobs: *.whl > openshell-checksums-sha256.txt cat openshell-checksums-sha256.txt sha256sum \ - openshell-gateway-x86_64-unknown-linux-gnu.tar.gz \ - openshell-gateway-aarch64-unknown-linux-gnu.tar.gz \ - openshell-gateway-aarch64-apple-darwin.tar.gz > openshell-gateway-checksums-sha256.txt - cat openshell-gateway-checksums-sha256.txt + openshell-server-x86_64-unknown-linux-gnu.tar.gz \ + openshell-server-aarch64-unknown-linux-gnu.tar.gz \ + openshell-server-aarch64-apple-darwin.tar.gz > openshell-server-checksums-sha256.txt + cat openshell-server-checksums-sha256.txt sha256sum \ openshell-sandbox-x86_64-unknown-linux-gnu.tar.gz \ openshell-sandbox-aarch64-unknown-linux-gnu.tar.gz > openshell-sandbox-checksums-sha256.txt @@ -733,14 +733,14 @@ jobs: release/openshell-x86_64-unknown-linux-musl.tar.gz release/openshell-aarch64-unknown-linux-musl.tar.gz release/openshell-aarch64-apple-darwin.tar.gz - release/openshell-gateway-x86_64-unknown-linux-gnu.tar.gz - release/openshell-gateway-aarch64-unknown-linux-gnu.tar.gz - release/openshell-gateway-aarch64-apple-darwin.tar.gz + release/openshell-server-x86_64-unknown-linux-gnu.tar.gz + release/openshell-server-aarch64-unknown-linux-gnu.tar.gz + release/openshell-server-aarch64-apple-darwin.tar.gz release/openshell-sandbox-x86_64-unknown-linux-gnu.tar.gz release/openshell-sandbox-aarch64-unknown-linux-gnu.tar.gz release/*.whl release/openshell-checksums-sha256.txt - release/openshell-gateway-checksums-sha256.txt + release/openshell-server-checksums-sha256.txt release/openshell-sandbox-checksums-sha256.txt publish-fern-docs: diff --git a/architecture/build-containers.md b/architecture/build-containers.md index 2db9445f8..266b73387 100644 --- a/architecture/build-containers.md +++ b/architecture/build-containers.md @@ -21,17 +21,17 @@ The cluster image is a single-container Kubernetes distribution that bundles the The supervisor binary (`openshell-sandbox`) is built by the shared `supervisor-builder` stage in `deploy/docker/Dockerfile.images` and placed at `/opt/openshell/bin/openshell-sandbox`. It is exposed to sandbox pods at runtime via a read-only `hostPath` volume mount — it is not baked into sandbox images. -## Standalone Gateway Binary +## Standalone Server Binary -OpenShell also publishes a standalone `openshell-gateway` binary as a GitHub release asset. +OpenShell also publishes a standalone `openshell-server` binary as a GitHub release asset. - **Source crate**: `crates/openshell-server` -- **Artifact name**: `openshell-gateway-.tar.gz` +- **Artifact name**: `openshell-server-.tar.gz` - **Targets**: `x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu`, `aarch64-apple-darwin` - **Release workflows**: `.github/workflows/release-dev.yml`, `.github/workflows/release-tag.yml` - **Installer**: None yet. The binary is a manual-download asset. -The standalone artifact is user-facing only. The deployed container image keeps using the internal `openshell-server` binary name and entrypoint. +Both the standalone artifact and the deployed container image use the `openshell-server` binary. ## Python Wheels diff --git a/crates/openshell-server/Cargo.toml b/crates/openshell-server/Cargo.toml index a13b339d5..b4e8b9e2f 100644 --- a/crates/openshell-server/Cargo.toml +++ b/crates/openshell-server/Cargo.toml @@ -14,10 +14,6 @@ repository.workspace = true name = "openshell-server" path = "src/main.rs" -[[bin]] -name = "openshell-gateway" -path = "src/bin/openshell-gateway.rs" - [dependencies] openshell-core = { path = "../openshell-core" } openshell-driver-kubernetes = { path = "../openshell-driver-kubernetes" } diff --git a/crates/openshell-server/src/bin/openshell-gateway.rs b/crates/openshell-server/src/bin/openshell-gateway.rs deleted file mode 100644 index cff048b0f..000000000 --- a/crates/openshell-server/src/bin/openshell-gateway.rs +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -use miette::Result; - -#[tokio::main] -async fn main() -> Result<()> { - openshell_server::cli::run_cli("openshell-gateway").await -} diff --git a/crates/openshell-server/src/cli.rs b/crates/openshell-server/src/cli.rs index f8e870f7a..344cb67a8 100644 --- a/crates/openshell-server/src/cli.rs +++ b/crates/openshell-server/src/cli.rs @@ -125,17 +125,18 @@ struct Args { disable_gateway_auth: bool, } -pub fn command(program_name: &'static str) -> Command { - Args::command().name(program_name).bin_name(program_name) +pub fn command() -> Command { + Args::command() + .name("openshell-server") + .bin_name("openshell-server") } -pub async fn run_cli(program_name: &'static str) -> Result<()> { +pub async fn run_cli() -> Result<()> { rustls::crypto::ring::default_provider() .install_default() .map_err(|e| miette::miette!("failed to install rustls crypto provider: {e:?}"))?; - let args = - Args::from_arg_matches(&command(program_name).get_matches()).expect("clap validated args"); + let args = Args::from_arg_matches(&command().get_matches()).expect("clap validated args"); run_from_args(args).await } @@ -230,27 +231,17 @@ mod tests { use super::command; #[test] - fn command_uses_requested_binary_name() { - let mut server_help = Vec::new(); - command("openshell-server") - .write_long_help(&mut server_help) - .unwrap(); - let server_help = String::from_utf8(server_help).unwrap(); - assert!(server_help.contains("openshell-server")); - - let mut gateway_help = Vec::new(); - command("openshell-gateway") - .write_long_help(&mut gateway_help) - .unwrap(); - let gateway_help = String::from_utf8(gateway_help).unwrap(); - assert!(gateway_help.contains("openshell-gateway")); - assert!(!gateway_help.contains("openshell-server")); + fn command_uses_server_binary_name() { + let mut help = Vec::new(); + command().write_long_help(&mut help).unwrap(); + let help = String::from_utf8(help).unwrap(); + assert!(help.contains("openshell-server")); } #[test] fn command_exposes_version() { - let gateway_command = command("openshell-gateway"); - let version = gateway_command.get_version().unwrap(); + let cmd = command(); + let version = cmd.get_version().unwrap(); assert_eq!(version.to_string(), openshell_core::VERSION); } } diff --git a/crates/openshell-server/src/main.rs b/crates/openshell-server/src/main.rs index 29c4a26fd..23d6edc64 100644 --- a/crates/openshell-server/src/main.rs +++ b/crates/openshell-server/src/main.rs @@ -7,5 +7,5 @@ use miette::Result; #[tokio::main] async fn main() -> Result<()> { - openshell_server::cli::run_cli("openshell-server").await + openshell_server::cli::run_cli().await } diff --git a/crates/openshell-server/src/tracing_bus.rs b/crates/openshell-server/src/tracing_bus.rs index 6767a450e..cdecc64b4 100644 --- a/crates/openshell-server/src/tracing_bus.rs +++ b/crates/openshell-server/src/tracing_bus.rs @@ -320,7 +320,7 @@ mod tests { #[test] fn platform_event_bus_tail_returns_buffered_events() { - use openshell_core::proto::{PlatformEvent, sandbox_stream_event}; + use openshell_core::proto::{sandbox_stream_event, PlatformEvent}; let bus = PlatformEventBus::new(); let sandbox_id = "sb-6"; diff --git a/deploy/docker/Dockerfile.images b/deploy/docker/Dockerfile.images index 21610cfc8..383f67b9f 100644 --- a/deploy/docker/Dockerfile.images +++ b/deploy/docker/Dockerfile.images @@ -73,7 +73,6 @@ RUN mkdir -p \ crates/openshell-router/src \ crates/openshell-sandbox/src \ crates/openshell-server/src \ - crates/openshell-server/src/bin \ crates/openshell-tui/src \ crates/openshell-vm/src && \ touch crates/openshell-bootstrap/src/lib.rs && \ @@ -90,7 +89,6 @@ RUN mkdir -p \ printf 'fn main() {}\n' > crates/openshell-sandbox/src/main.rs && \ touch crates/openshell-server/src/lib.rs && \ printf 'fn main() {}\n' > crates/openshell-server/src/main.rs && \ - printf 'fn main() {}\n' > crates/openshell-server/src/bin/openshell-gateway.rs && \ touch crates/openshell-tui/src/lib.rs && \ touch crates/openshell-vm/src/lib.rs && \ printf 'fn main() {}\n' > crates/openshell-vm/src/main.rs @@ -125,9 +123,8 @@ COPY crates/openshell-server/ crates/openshell-server/ RUN touch \ crates/openshell-core/build.rs \ crates/openshell-server/src/main.rs \ - crates/openshell-server/src/bin/openshell-gateway.rs \ proto/*.proto && \ - if [ -n "${OPENSHELL_CARGO_VERSION:-}" ]; then \ + if [ -n "${OPENSHELL_CARGO_VERSION:-}" ]; then sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${OPENSHELL_CARGO_VERSION}"'"/}' Cargo.toml; \ fi diff --git a/deploy/docker/Dockerfile.gateway-macos b/deploy/docker/Dockerfile.server-macos similarity index 77% rename from deploy/docker/Dockerfile.gateway-macos rename to deploy/docker/Dockerfile.server-macos index b6e24684d..4b41b3dd6 100644 --- a/deploy/docker/Dockerfile.gateway-macos +++ b/deploy/docker/Dockerfile.server-macos @@ -3,7 +3,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -# Cross-compile the standalone openshell-gateway binary for macOS aarch64 +# Cross-compile the standalone openshell-server binary for macOS aarch64 # (Apple Silicon) using the osxcross toolchain. ARG OSXCROSS_IMAGE=crazymax/osxcross:latest @@ -65,20 +65,18 @@ RUN mkdir -p crates/openshell-core/src \ crates/openshell-driver-kubernetes/src \ crates/openshell-policy/src \ crates/openshell-router/src \ - crates/openshell-server/src/bin && \ touch crates/openshell-core/src/lib.rs && \ touch crates/openshell-driver-kubernetes/src/lib.rs && \ printf 'fn main() {}\n' > crates/openshell-driver-kubernetes/src/main.rs && \ touch crates/openshell-policy/src/lib.rs && \ touch crates/openshell-router/src/lib.rs && \ touch crates/openshell-server/src/lib.rs && \ - printf 'fn main() {}\n' > crates/openshell-server/src/main.rs && \ - printf 'fn main() {}\n' > crates/openshell-server/src/bin/openshell-gateway.rs + printf 'fn main() {}\n' > crates/openshell-server/src/main.rs -RUN --mount=type=cache,id=cargo-registry-gateway-macos,sharing=locked,target=/root/.cargo/registry \ - --mount=type=cache,id=cargo-git-gateway-macos,sharing=locked,target=/root/.cargo/git \ - --mount=type=cache,id=cargo-target-gateway-macos-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \ - cargo build --release --target aarch64-apple-darwin -p openshell-server --bin openshell-gateway 2>/dev/null || true +RUN --mount=type=cache,id=cargo-registry-server-macos,sharing=locked,target=/root/.cargo/registry \ + --mount=type=cache,id=cargo-git-server-macos,sharing=locked,target=/root/.cargo/git \ + --mount=type=cache,id=cargo-target-server-macos-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \ + cargo build --release --target aarch64-apple-darwin -p openshell-server 2>/dev/null || true COPY crates/ crates/ @@ -89,19 +87,18 @@ RUN touch crates/openshell-core/src/lib.rs \ crates/openshell-router/src/lib.rs \ crates/openshell-server/src/lib.rs \ crates/openshell-server/src/main.rs \ - crates/openshell-server/src/bin/openshell-gateway.rs \ crates/openshell-core/build.rs \ proto/*.proto ARG OPENSHELL_CARGO_VERSION -RUN --mount=type=cache,id=cargo-registry-gateway-macos,sharing=locked,target=/root/.cargo/registry \ - --mount=type=cache,id=cargo-git-gateway-macos,sharing=locked,target=/root/.cargo/git \ - --mount=type=cache,id=cargo-target-gateway-macos-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \ +RUN --mount=type=cache,id=cargo-registry-server-macos,sharing=locked,target=/root/.cargo/registry \ + --mount=type=cache,id=cargo-git-server-macos,sharing=locked,target=/root/.cargo/git \ + --mount=type=cache,id=cargo-target-server-macos-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \ if [ -n "${OPENSHELL_CARGO_VERSION:-}" ]; then \ sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${OPENSHELL_CARGO_VERSION}"'"/}' Cargo.toml; \ fi && \ - cargo build --release --target aarch64-apple-darwin -p openshell-server --bin openshell-gateway && \ - cp target/aarch64-apple-darwin/release/openshell-gateway /openshell-gateway + cargo build --release --target aarch64-apple-darwin -p openshell-server && \ + cp target/aarch64-apple-darwin/release/openshell-server /openshell-server FROM scratch AS binary -COPY --from=builder /openshell-gateway /openshell-gateway +COPY --from=builder /openshell-server /openshell-server diff --git a/tasks/docker.toml b/tasks/docker.toml index da5bd24e4..4eabbaee4 100644 --- a/tasks/docker.toml +++ b/tasks/docker.toml @@ -41,11 +41,6 @@ description = "Build the supervisor Docker image" run = "tasks/scripts/docker-build-image.sh supervisor" hide = true -["docker:build:supervisor"] -description = "Alias for build:docker:supervisor" -depends = ["build:docker:supervisor"] -hide = true - ["build:docker:cluster"] description = "Build the k3s cluster image (component images pulled at runtime from registry)" run = "tasks/scripts/docker-build-image.sh cluster" From 539dc0ba55374ddcdfda23a784368aebe0dcdd4b Mon Sep 17 00:00:00 2001 From: Drew Newberry Date: Thu, 16 Apr 2026 14:09:39 -0700 Subject: [PATCH 7/9] Revert "refactor(server): consolidate to single openshell-server binary" This reverts commit 4fa37c7910ea5f6998c93107d8c560e099055cad. --- .github/workflows/release-dev.yml | 34 +++++++++--------- .github/workflows/release-tag.yml | 34 +++++++++--------- architecture/build-containers.md | 8 ++--- crates/openshell-server/Cargo.toml | 4 +++ .../src/bin/openshell-gateway.rs | 9 +++++ crates/openshell-server/src/cli.rs | 35 ++++++++++++------- crates/openshell-server/src/main.rs | 2 +- crates/openshell-server/src/tracing_bus.rs | 2 +- ....server-macos => Dockerfile.gateway-macos} | 27 +++++++------- deploy/docker/Dockerfile.images | 5 ++- tasks/docker.toml | 5 +++ 11 files changed, 99 insertions(+), 66 deletions(-) create mode 100644 crates/openshell-server/src/bin/openshell-gateway.rs rename deploy/docker/{Dockerfile.server-macos => Dockerfile.gateway-macos} (77%) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 7acf326d6..a980098e8 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -436,14 +436,14 @@ jobs: - name: Build ${{ matrix.target }} run: | set -euo pipefail - mise x -- cargo build --release --target ${{ matrix.target }} -p openshell-server --bin openshell-server + mise x -- cargo build --release --target ${{ matrix.target }} -p openshell-server --bin openshell-gateway - name: Verify packaged binary run: | set -euo pipefail - OUTPUT="$(target/${{ matrix.target }}/release/openshell-server --version)" + OUTPUT="$(target/${{ matrix.target }}/release/openshell-gateway --version)" echo "$OUTPUT" - grep -q '^openshell-server ' <<<"$OUTPUT" + grep -q '^openshell-gateway ' <<<"$OUTPUT" - name: sccache stats if: always() @@ -453,8 +453,8 @@ jobs: run: | set -euo pipefail mkdir -p artifacts - tar -czf artifacts/openshell-server-${{ matrix.target }}.tar.gz \ - -C target/${{ matrix.target }}/release openshell-server + tar -czf artifacts/openshell-gateway-${{ matrix.target }}.tar.gz \ + -C target/${{ matrix.target }}/release openshell-gateway ls -lh artifacts/ - name: Upload artifact @@ -504,7 +504,7 @@ jobs: run: | set -euo pipefail docker buildx build \ - --file deploy/docker/Dockerfile.server-macos \ + --file deploy/docker/Dockerfile.gateway-macos \ --build-arg OPENSHELL_CARGO_VERSION="${{ needs.compute-versions.outputs.cargo_version }}" \ --build-arg CARGO_TARGET_CACHE_SCOPE="${{ github.sha }}" \ --target binary \ @@ -514,14 +514,14 @@ jobs: - name: Verify packaged binary shape run: | set -euo pipefail - test -x out/openshell-server + test -x out/openshell-gateway - name: Package binary run: | set -euo pipefail mkdir -p artifacts - tar -czf artifacts/openshell-server-aarch64-apple-darwin.tar.gz \ - -C out openshell-server + tar -czf artifacts/openshell-gateway-aarch64-apple-darwin.tar.gz \ + -C out openshell-gateway ls -lh artifacts/ - name: Upload artifact @@ -675,10 +675,10 @@ jobs: *.whl > openshell-checksums-sha256.txt cat openshell-checksums-sha256.txt sha256sum \ - openshell-server-x86_64-unknown-linux-gnu.tar.gz \ - openshell-server-aarch64-unknown-linux-gnu.tar.gz \ - openshell-server-aarch64-apple-darwin.tar.gz > openshell-server-checksums-sha256.txt - cat openshell-server-checksums-sha256.txt + openshell-gateway-x86_64-unknown-linux-gnu.tar.gz \ + openshell-gateway-aarch64-unknown-linux-gnu.tar.gz \ + openshell-gateway-aarch64-apple-darwin.tar.gz > openshell-gateway-checksums-sha256.txt + cat openshell-gateway-checksums-sha256.txt sha256sum \ openshell-sandbox-x86_64-unknown-linux-gnu.tar.gz \ openshell-sandbox-aarch64-unknown-linux-gnu.tar.gz > openshell-sandbox-checksums-sha256.txt @@ -760,14 +760,14 @@ jobs: release/openshell-x86_64-unknown-linux-musl.tar.gz release/openshell-aarch64-unknown-linux-musl.tar.gz release/openshell-aarch64-apple-darwin.tar.gz - release/openshell-server-x86_64-unknown-linux-gnu.tar.gz - release/openshell-server-aarch64-unknown-linux-gnu.tar.gz - release/openshell-server-aarch64-apple-darwin.tar.gz + release/openshell-gateway-x86_64-unknown-linux-gnu.tar.gz + release/openshell-gateway-aarch64-unknown-linux-gnu.tar.gz + release/openshell-gateway-aarch64-apple-darwin.tar.gz release/openshell-sandbox-x86_64-unknown-linux-gnu.tar.gz release/openshell-sandbox-aarch64-unknown-linux-gnu.tar.gz release/*.whl release/openshell-checksums-sha256.txt - release/openshell-server-checksums-sha256.txt + release/openshell-gateway-checksums-sha256.txt release/openshell-sandbox-checksums-sha256.txt trigger-wheel-publish: diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 426e0091b..7271de28b 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -461,14 +461,14 @@ jobs: - name: Build ${{ matrix.target }} run: | set -euo pipefail - mise x -- cargo build --release --target ${{ matrix.target }} -p openshell-server --bin openshell-server + mise x -- cargo build --release --target ${{ matrix.target }} -p openshell-server --bin openshell-gateway - name: Verify packaged binary run: | set -euo pipefail - OUTPUT="$(target/${{ matrix.target }}/release/openshell-server --version)" + OUTPUT="$(target/${{ matrix.target }}/release/openshell-gateway --version)" echo "$OUTPUT" - grep -q '^openshell-server ' <<<"$OUTPUT" + grep -q '^openshell-gateway ' <<<"$OUTPUT" - name: sccache stats if: always() @@ -478,8 +478,8 @@ jobs: run: | set -euo pipefail mkdir -p artifacts - tar -czf artifacts/openshell-server-${{ matrix.target }}.tar.gz \ - -C target/${{ matrix.target }}/release openshell-server + tar -czf artifacts/openshell-gateway-${{ matrix.target }}.tar.gz \ + -C target/${{ matrix.target }}/release openshell-gateway ls -lh artifacts/ - name: Upload artifact @@ -615,7 +615,7 @@ jobs: run: | set -euo pipefail docker buildx build \ - --file deploy/docker/Dockerfile.server-macos \ + --file deploy/docker/Dockerfile.gateway-macos \ --build-arg OPENSHELL_CARGO_VERSION="${{ needs.compute-versions.outputs.cargo_version }}" \ --build-arg CARGO_TARGET_CACHE_SCOPE="${{ github.sha }}" \ --target binary \ @@ -625,14 +625,14 @@ jobs: - name: Verify packaged binary shape run: | set -euo pipefail - test -x out/openshell-server + test -x out/openshell-gateway - name: Package binary run: | set -euo pipefail mkdir -p artifacts - tar -czf artifacts/openshell-server-aarch64-apple-darwin.tar.gz \ - -C out openshell-server + tar -czf artifacts/openshell-gateway-aarch64-apple-darwin.tar.gz \ + -C out openshell-gateway ls -lh artifacts/ - name: Upload artifact @@ -704,10 +704,10 @@ jobs: *.whl > openshell-checksums-sha256.txt cat openshell-checksums-sha256.txt sha256sum \ - openshell-server-x86_64-unknown-linux-gnu.tar.gz \ - openshell-server-aarch64-unknown-linux-gnu.tar.gz \ - openshell-server-aarch64-apple-darwin.tar.gz > openshell-server-checksums-sha256.txt - cat openshell-server-checksums-sha256.txt + openshell-gateway-x86_64-unknown-linux-gnu.tar.gz \ + openshell-gateway-aarch64-unknown-linux-gnu.tar.gz \ + openshell-gateway-aarch64-apple-darwin.tar.gz > openshell-gateway-checksums-sha256.txt + cat openshell-gateway-checksums-sha256.txt sha256sum \ openshell-sandbox-x86_64-unknown-linux-gnu.tar.gz \ openshell-sandbox-aarch64-unknown-linux-gnu.tar.gz > openshell-sandbox-checksums-sha256.txt @@ -733,14 +733,14 @@ jobs: release/openshell-x86_64-unknown-linux-musl.tar.gz release/openshell-aarch64-unknown-linux-musl.tar.gz release/openshell-aarch64-apple-darwin.tar.gz - release/openshell-server-x86_64-unknown-linux-gnu.tar.gz - release/openshell-server-aarch64-unknown-linux-gnu.tar.gz - release/openshell-server-aarch64-apple-darwin.tar.gz + release/openshell-gateway-x86_64-unknown-linux-gnu.tar.gz + release/openshell-gateway-aarch64-unknown-linux-gnu.tar.gz + release/openshell-gateway-aarch64-apple-darwin.tar.gz release/openshell-sandbox-x86_64-unknown-linux-gnu.tar.gz release/openshell-sandbox-aarch64-unknown-linux-gnu.tar.gz release/*.whl release/openshell-checksums-sha256.txt - release/openshell-server-checksums-sha256.txt + release/openshell-gateway-checksums-sha256.txt release/openshell-sandbox-checksums-sha256.txt publish-fern-docs: diff --git a/architecture/build-containers.md b/architecture/build-containers.md index 266b73387..2db9445f8 100644 --- a/architecture/build-containers.md +++ b/architecture/build-containers.md @@ -21,17 +21,17 @@ The cluster image is a single-container Kubernetes distribution that bundles the The supervisor binary (`openshell-sandbox`) is built by the shared `supervisor-builder` stage in `deploy/docker/Dockerfile.images` and placed at `/opt/openshell/bin/openshell-sandbox`. It is exposed to sandbox pods at runtime via a read-only `hostPath` volume mount — it is not baked into sandbox images. -## Standalone Server Binary +## Standalone Gateway Binary -OpenShell also publishes a standalone `openshell-server` binary as a GitHub release asset. +OpenShell also publishes a standalone `openshell-gateway` binary as a GitHub release asset. - **Source crate**: `crates/openshell-server` -- **Artifact name**: `openshell-server-.tar.gz` +- **Artifact name**: `openshell-gateway-.tar.gz` - **Targets**: `x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu`, `aarch64-apple-darwin` - **Release workflows**: `.github/workflows/release-dev.yml`, `.github/workflows/release-tag.yml` - **Installer**: None yet. The binary is a manual-download asset. -Both the standalone artifact and the deployed container image use the `openshell-server` binary. +The standalone artifact is user-facing only. The deployed container image keeps using the internal `openshell-server` binary name and entrypoint. ## Python Wheels diff --git a/crates/openshell-server/Cargo.toml b/crates/openshell-server/Cargo.toml index b4e8b9e2f..a13b339d5 100644 --- a/crates/openshell-server/Cargo.toml +++ b/crates/openshell-server/Cargo.toml @@ -14,6 +14,10 @@ repository.workspace = true name = "openshell-server" path = "src/main.rs" +[[bin]] +name = "openshell-gateway" +path = "src/bin/openshell-gateway.rs" + [dependencies] openshell-core = { path = "../openshell-core" } openshell-driver-kubernetes = { path = "../openshell-driver-kubernetes" } diff --git a/crates/openshell-server/src/bin/openshell-gateway.rs b/crates/openshell-server/src/bin/openshell-gateway.rs new file mode 100644 index 000000000..cff048b0f --- /dev/null +++ b/crates/openshell-server/src/bin/openshell-gateway.rs @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +use miette::Result; + +#[tokio::main] +async fn main() -> Result<()> { + openshell_server::cli::run_cli("openshell-gateway").await +} diff --git a/crates/openshell-server/src/cli.rs b/crates/openshell-server/src/cli.rs index 344cb67a8..f8e870f7a 100644 --- a/crates/openshell-server/src/cli.rs +++ b/crates/openshell-server/src/cli.rs @@ -125,18 +125,17 @@ struct Args { disable_gateway_auth: bool, } -pub fn command() -> Command { - Args::command() - .name("openshell-server") - .bin_name("openshell-server") +pub fn command(program_name: &'static str) -> Command { + Args::command().name(program_name).bin_name(program_name) } -pub async fn run_cli() -> Result<()> { +pub async fn run_cli(program_name: &'static str) -> Result<()> { rustls::crypto::ring::default_provider() .install_default() .map_err(|e| miette::miette!("failed to install rustls crypto provider: {e:?}"))?; - let args = Args::from_arg_matches(&command().get_matches()).expect("clap validated args"); + let args = + Args::from_arg_matches(&command(program_name).get_matches()).expect("clap validated args"); run_from_args(args).await } @@ -231,17 +230,27 @@ mod tests { use super::command; #[test] - fn command_uses_server_binary_name() { - let mut help = Vec::new(); - command().write_long_help(&mut help).unwrap(); - let help = String::from_utf8(help).unwrap(); - assert!(help.contains("openshell-server")); + fn command_uses_requested_binary_name() { + let mut server_help = Vec::new(); + command("openshell-server") + .write_long_help(&mut server_help) + .unwrap(); + let server_help = String::from_utf8(server_help).unwrap(); + assert!(server_help.contains("openshell-server")); + + let mut gateway_help = Vec::new(); + command("openshell-gateway") + .write_long_help(&mut gateway_help) + .unwrap(); + let gateway_help = String::from_utf8(gateway_help).unwrap(); + assert!(gateway_help.contains("openshell-gateway")); + assert!(!gateway_help.contains("openshell-server")); } #[test] fn command_exposes_version() { - let cmd = command(); - let version = cmd.get_version().unwrap(); + let gateway_command = command("openshell-gateway"); + let version = gateway_command.get_version().unwrap(); assert_eq!(version.to_string(), openshell_core::VERSION); } } diff --git a/crates/openshell-server/src/main.rs b/crates/openshell-server/src/main.rs index 23d6edc64..29c4a26fd 100644 --- a/crates/openshell-server/src/main.rs +++ b/crates/openshell-server/src/main.rs @@ -7,5 +7,5 @@ use miette::Result; #[tokio::main] async fn main() -> Result<()> { - openshell_server::cli::run_cli().await + openshell_server::cli::run_cli("openshell-server").await } diff --git a/crates/openshell-server/src/tracing_bus.rs b/crates/openshell-server/src/tracing_bus.rs index cdecc64b4..6767a450e 100644 --- a/crates/openshell-server/src/tracing_bus.rs +++ b/crates/openshell-server/src/tracing_bus.rs @@ -320,7 +320,7 @@ mod tests { #[test] fn platform_event_bus_tail_returns_buffered_events() { - use openshell_core::proto::{sandbox_stream_event, PlatformEvent}; + use openshell_core::proto::{PlatformEvent, sandbox_stream_event}; let bus = PlatformEventBus::new(); let sandbox_id = "sb-6"; diff --git a/deploy/docker/Dockerfile.server-macos b/deploy/docker/Dockerfile.gateway-macos similarity index 77% rename from deploy/docker/Dockerfile.server-macos rename to deploy/docker/Dockerfile.gateway-macos index 4b41b3dd6..b6e24684d 100644 --- a/deploy/docker/Dockerfile.server-macos +++ b/deploy/docker/Dockerfile.gateway-macos @@ -3,7 +3,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -# Cross-compile the standalone openshell-server binary for macOS aarch64 +# Cross-compile the standalone openshell-gateway binary for macOS aarch64 # (Apple Silicon) using the osxcross toolchain. ARG OSXCROSS_IMAGE=crazymax/osxcross:latest @@ -65,18 +65,20 @@ RUN mkdir -p crates/openshell-core/src \ crates/openshell-driver-kubernetes/src \ crates/openshell-policy/src \ crates/openshell-router/src \ + crates/openshell-server/src/bin && \ touch crates/openshell-core/src/lib.rs && \ touch crates/openshell-driver-kubernetes/src/lib.rs && \ printf 'fn main() {}\n' > crates/openshell-driver-kubernetes/src/main.rs && \ touch crates/openshell-policy/src/lib.rs && \ touch crates/openshell-router/src/lib.rs && \ touch crates/openshell-server/src/lib.rs && \ - printf 'fn main() {}\n' > crates/openshell-server/src/main.rs + printf 'fn main() {}\n' > crates/openshell-server/src/main.rs && \ + printf 'fn main() {}\n' > crates/openshell-server/src/bin/openshell-gateway.rs -RUN --mount=type=cache,id=cargo-registry-server-macos,sharing=locked,target=/root/.cargo/registry \ - --mount=type=cache,id=cargo-git-server-macos,sharing=locked,target=/root/.cargo/git \ - --mount=type=cache,id=cargo-target-server-macos-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \ - cargo build --release --target aarch64-apple-darwin -p openshell-server 2>/dev/null || true +RUN --mount=type=cache,id=cargo-registry-gateway-macos,sharing=locked,target=/root/.cargo/registry \ + --mount=type=cache,id=cargo-git-gateway-macos,sharing=locked,target=/root/.cargo/git \ + --mount=type=cache,id=cargo-target-gateway-macos-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \ + cargo build --release --target aarch64-apple-darwin -p openshell-server --bin openshell-gateway 2>/dev/null || true COPY crates/ crates/ @@ -87,18 +89,19 @@ RUN touch crates/openshell-core/src/lib.rs \ crates/openshell-router/src/lib.rs \ crates/openshell-server/src/lib.rs \ crates/openshell-server/src/main.rs \ + crates/openshell-server/src/bin/openshell-gateway.rs \ crates/openshell-core/build.rs \ proto/*.proto ARG OPENSHELL_CARGO_VERSION -RUN --mount=type=cache,id=cargo-registry-server-macos,sharing=locked,target=/root/.cargo/registry \ - --mount=type=cache,id=cargo-git-server-macos,sharing=locked,target=/root/.cargo/git \ - --mount=type=cache,id=cargo-target-server-macos-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \ +RUN --mount=type=cache,id=cargo-registry-gateway-macos,sharing=locked,target=/root/.cargo/registry \ + --mount=type=cache,id=cargo-git-gateway-macos,sharing=locked,target=/root/.cargo/git \ + --mount=type=cache,id=cargo-target-gateway-macos-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \ if [ -n "${OPENSHELL_CARGO_VERSION:-}" ]; then \ sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${OPENSHELL_CARGO_VERSION}"'"/}' Cargo.toml; \ fi && \ - cargo build --release --target aarch64-apple-darwin -p openshell-server && \ - cp target/aarch64-apple-darwin/release/openshell-server /openshell-server + cargo build --release --target aarch64-apple-darwin -p openshell-server --bin openshell-gateway && \ + cp target/aarch64-apple-darwin/release/openshell-gateway /openshell-gateway FROM scratch AS binary -COPY --from=builder /openshell-server /openshell-server +COPY --from=builder /openshell-gateway /openshell-gateway diff --git a/deploy/docker/Dockerfile.images b/deploy/docker/Dockerfile.images index 383f67b9f..21610cfc8 100644 --- a/deploy/docker/Dockerfile.images +++ b/deploy/docker/Dockerfile.images @@ -73,6 +73,7 @@ RUN mkdir -p \ crates/openshell-router/src \ crates/openshell-sandbox/src \ crates/openshell-server/src \ + crates/openshell-server/src/bin \ crates/openshell-tui/src \ crates/openshell-vm/src && \ touch crates/openshell-bootstrap/src/lib.rs && \ @@ -89,6 +90,7 @@ RUN mkdir -p \ printf 'fn main() {}\n' > crates/openshell-sandbox/src/main.rs && \ touch crates/openshell-server/src/lib.rs && \ printf 'fn main() {}\n' > crates/openshell-server/src/main.rs && \ + printf 'fn main() {}\n' > crates/openshell-server/src/bin/openshell-gateway.rs && \ touch crates/openshell-tui/src/lib.rs && \ touch crates/openshell-vm/src/lib.rs && \ printf 'fn main() {}\n' > crates/openshell-vm/src/main.rs @@ -123,8 +125,9 @@ COPY crates/openshell-server/ crates/openshell-server/ RUN touch \ crates/openshell-core/build.rs \ crates/openshell-server/src/main.rs \ + crates/openshell-server/src/bin/openshell-gateway.rs \ proto/*.proto && \ - if [ -n "${OPENSHELL_CARGO_VERSION:-}" ]; then + if [ -n "${OPENSHELL_CARGO_VERSION:-}" ]; then \ sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${OPENSHELL_CARGO_VERSION}"'"/}' Cargo.toml; \ fi diff --git a/tasks/docker.toml b/tasks/docker.toml index 4eabbaee4..da5bd24e4 100644 --- a/tasks/docker.toml +++ b/tasks/docker.toml @@ -41,6 +41,11 @@ description = "Build the supervisor Docker image" run = "tasks/scripts/docker-build-image.sh supervisor" hide = true +["docker:build:supervisor"] +description = "Alias for build:docker:supervisor" +depends = ["build:docker:supervisor"] +hide = true + ["build:docker:cluster"] description = "Build the k3s cluster image (component images pulled at runtime from registry)" run = "tasks/scripts/docker-build-image.sh cluster" From 616336cf1440fa0a36d103c51012b55af92d80f2 Mon Sep 17 00:00:00 2001 From: Drew Newberry Date: Thu, 16 Apr 2026 14:13:44 -0700 Subject: [PATCH 8/9] refactor(server): consolidate to single openshell-gateway binary Remove the separate openshell-server binary entrypoint. The openshell-server crate now produces a single binary (openshell-gateway) from src/main.rs. Simplify cli.rs by removing the program_name parameter. Update Dockerfile.images and Dockerfile.gateway-macos to remove bin/ skeleton stubs. Update container ENTRYPOINT to openshell-gateway. Remove unused docker:build:supervisor task alias. --- .github/workflows/release-dev.yml | 2 +- .github/workflows/release-tag.yml | 2 +- architecture/build-containers.md | 4 +-- crates/openshell-server/Cargo.toml | 6 +--- .../src/bin/openshell-gateway.rs | 9 ----- crates/openshell-server/src/cli.rs | 35 +++++++------------ crates/openshell-server/src/main.rs | 4 +-- deploy/docker/Dockerfile.gateway-macos | 9 ++--- deploy/docker/Dockerfile.images | 11 +++--- tasks/docker.toml | 5 --- 10 files changed, 27 insertions(+), 60 deletions(-) delete mode 100644 crates/openshell-server/src/bin/openshell-gateway.rs diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index a980098e8..fc42794cc 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -436,7 +436,7 @@ jobs: - name: Build ${{ matrix.target }} run: | set -euo pipefail - mise x -- cargo build --release --target ${{ matrix.target }} -p openshell-server --bin openshell-gateway + mise x -- cargo build --release --target ${{ matrix.target }} -p openshell-server - name: Verify packaged binary run: | diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 7271de28b..9b6a94065 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -461,7 +461,7 @@ jobs: - name: Build ${{ matrix.target }} run: | set -euo pipefail - mise x -- cargo build --release --target ${{ matrix.target }} -p openshell-server --bin openshell-gateway + mise x -- cargo build --release --target ${{ matrix.target }} -p openshell-server - name: Verify packaged binary run: | diff --git a/architecture/build-containers.md b/architecture/build-containers.md index 2db9445f8..196663e7a 100644 --- a/architecture/build-containers.md +++ b/architecture/build-containers.md @@ -9,7 +9,7 @@ The gateway runs the control plane API server. It is deployed as a StatefulSet i - **Docker target**: `gateway` in `deploy/docker/Dockerfile.images` - **Registry**: `ghcr.io/nvidia/openshell/gateway:latest` - **Pulled when**: Cluster startup (the Helm chart triggers the pull) -- **Entrypoint**: `openshell-server --port 8080` (gRPC + HTTP, mTLS) +- **Entrypoint**: `openshell-gateway --port 8080` (gRPC + HTTP, mTLS) ## Cluster (`openshell/cluster`) @@ -31,7 +31,7 @@ OpenShell also publishes a standalone `openshell-gateway` binary as a GitHub rel - **Release workflows**: `.github/workflows/release-dev.yml`, `.github/workflows/release-tag.yml` - **Installer**: None yet. The binary is a manual-download asset. -The standalone artifact is user-facing only. The deployed container image keeps using the internal `openshell-server` binary name and entrypoint. +Both the standalone artifact and the deployed container image use the `openshell-gateway` binary. ## Python Wheels diff --git a/crates/openshell-server/Cargo.toml b/crates/openshell-server/Cargo.toml index a13b339d5..33e354247 100644 --- a/crates/openshell-server/Cargo.toml +++ b/crates/openshell-server/Cargo.toml @@ -10,13 +10,9 @@ rust-version.workspace = true license.workspace = true repository.workspace = true -[[bin]] -name = "openshell-server" -path = "src/main.rs" - [[bin]] name = "openshell-gateway" -path = "src/bin/openshell-gateway.rs" +path = "src/main.rs" [dependencies] openshell-core = { path = "../openshell-core" } diff --git a/crates/openshell-server/src/bin/openshell-gateway.rs b/crates/openshell-server/src/bin/openshell-gateway.rs deleted file mode 100644 index cff048b0f..000000000 --- a/crates/openshell-server/src/bin/openshell-gateway.rs +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -use miette::Result; - -#[tokio::main] -async fn main() -> Result<()> { - openshell_server::cli::run_cli("openshell-gateway").await -} diff --git a/crates/openshell-server/src/cli.rs b/crates/openshell-server/src/cli.rs index f8e870f7a..9509fe84b 100644 --- a/crates/openshell-server/src/cli.rs +++ b/crates/openshell-server/src/cli.rs @@ -125,17 +125,18 @@ struct Args { disable_gateway_auth: bool, } -pub fn command(program_name: &'static str) -> Command { - Args::command().name(program_name).bin_name(program_name) +pub fn command() -> Command { + Args::command() + .name("openshell-gateway") + .bin_name("openshell-gateway") } -pub async fn run_cli(program_name: &'static str) -> Result<()> { +pub async fn run_cli() -> Result<()> { rustls::crypto::ring::default_provider() .install_default() .map_err(|e| miette::miette!("failed to install rustls crypto provider: {e:?}"))?; - let args = - Args::from_arg_matches(&command(program_name).get_matches()).expect("clap validated args"); + let args = Args::from_arg_matches(&command().get_matches()).expect("clap validated args"); run_from_args(args).await } @@ -230,27 +231,17 @@ mod tests { use super::command; #[test] - fn command_uses_requested_binary_name() { - let mut server_help = Vec::new(); - command("openshell-server") - .write_long_help(&mut server_help) - .unwrap(); - let server_help = String::from_utf8(server_help).unwrap(); - assert!(server_help.contains("openshell-server")); - - let mut gateway_help = Vec::new(); - command("openshell-gateway") - .write_long_help(&mut gateway_help) - .unwrap(); - let gateway_help = String::from_utf8(gateway_help).unwrap(); - assert!(gateway_help.contains("openshell-gateway")); - assert!(!gateway_help.contains("openshell-server")); + fn command_uses_gateway_binary_name() { + let mut help = Vec::new(); + command().write_long_help(&mut help).unwrap(); + let help = String::from_utf8(help).unwrap(); + assert!(help.contains("openshell-gateway")); } #[test] fn command_exposes_version() { - let gateway_command = command("openshell-gateway"); - let version = gateway_command.get_version().unwrap(); + let cmd = command(); + let version = cmd.get_version().unwrap(); assert_eq!(version.to_string(), openshell_core::VERSION); } } diff --git a/crates/openshell-server/src/main.rs b/crates/openshell-server/src/main.rs index 29c4a26fd..0f33c685f 100644 --- a/crates/openshell-server/src/main.rs +++ b/crates/openshell-server/src/main.rs @@ -1,11 +1,11 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -//! `OpenShell` Server binary entrypoint. +//! `OpenShell` Gateway binary entrypoint. use miette::Result; #[tokio::main] async fn main() -> Result<()> { - openshell_server::cli::run_cli("openshell-server").await + openshell_server::cli::run_cli().await } diff --git a/deploy/docker/Dockerfile.gateway-macos b/deploy/docker/Dockerfile.gateway-macos index b6e24684d..2917c7027 100644 --- a/deploy/docker/Dockerfile.gateway-macos +++ b/deploy/docker/Dockerfile.gateway-macos @@ -65,20 +65,18 @@ RUN mkdir -p crates/openshell-core/src \ crates/openshell-driver-kubernetes/src \ crates/openshell-policy/src \ crates/openshell-router/src \ - crates/openshell-server/src/bin && \ touch crates/openshell-core/src/lib.rs && \ touch crates/openshell-driver-kubernetes/src/lib.rs && \ printf 'fn main() {}\n' > crates/openshell-driver-kubernetes/src/main.rs && \ touch crates/openshell-policy/src/lib.rs && \ touch crates/openshell-router/src/lib.rs && \ touch crates/openshell-server/src/lib.rs && \ - printf 'fn main() {}\n' > crates/openshell-server/src/main.rs && \ - printf 'fn main() {}\n' > crates/openshell-server/src/bin/openshell-gateway.rs + printf 'fn main() {}\n' > crates/openshell-server/src/main.rs RUN --mount=type=cache,id=cargo-registry-gateway-macos,sharing=locked,target=/root/.cargo/registry \ --mount=type=cache,id=cargo-git-gateway-macos,sharing=locked,target=/root/.cargo/git \ --mount=type=cache,id=cargo-target-gateway-macos-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \ - cargo build --release --target aarch64-apple-darwin -p openshell-server --bin openshell-gateway 2>/dev/null || true + cargo build --release --target aarch64-apple-darwin -p openshell-server 2>/dev/null || true COPY crates/ crates/ @@ -89,7 +87,6 @@ RUN touch crates/openshell-core/src/lib.rs \ crates/openshell-router/src/lib.rs \ crates/openshell-server/src/lib.rs \ crates/openshell-server/src/main.rs \ - crates/openshell-server/src/bin/openshell-gateway.rs \ crates/openshell-core/build.rs \ proto/*.proto @@ -100,7 +97,7 @@ RUN --mount=type=cache,id=cargo-registry-gateway-macos,sharing=locked,target=/ro if [ -n "${OPENSHELL_CARGO_VERSION:-}" ]; then \ sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${OPENSHELL_CARGO_VERSION}"'"/}' Cargo.toml; \ fi && \ - cargo build --release --target aarch64-apple-darwin -p openshell-server --bin openshell-gateway && \ + cargo build --release --target aarch64-apple-darwin -p openshell-server && \ cp target/aarch64-apple-darwin/release/openshell-gateway /openshell-gateway FROM scratch AS binary diff --git a/deploy/docker/Dockerfile.images b/deploy/docker/Dockerfile.images index 21610cfc8..d29fa3d7f 100644 --- a/deploy/docker/Dockerfile.images +++ b/deploy/docker/Dockerfile.images @@ -9,7 +9,7 @@ # gateway Final gateway image # supervisor Final supervisor image # cluster Final cluster image -# gateway-builder Release openshell-server binary +# gateway-builder Release openshell-gateway binary # supervisor-builder Release openshell-sandbox binary # supervisor-output Minimal stage exporting only the supervisor binary @@ -73,7 +73,6 @@ RUN mkdir -p \ crates/openshell-router/src \ crates/openshell-sandbox/src \ crates/openshell-server/src \ - crates/openshell-server/src/bin \ crates/openshell-tui/src \ crates/openshell-vm/src && \ touch crates/openshell-bootstrap/src/lib.rs && \ @@ -90,7 +89,6 @@ RUN mkdir -p \ printf 'fn main() {}\n' > crates/openshell-sandbox/src/main.rs && \ touch crates/openshell-server/src/lib.rs && \ printf 'fn main() {}\n' > crates/openshell-server/src/main.rs && \ - printf 'fn main() {}\n' > crates/openshell-server/src/bin/openshell-gateway.rs && \ touch crates/openshell-tui/src/lib.rs && \ touch crates/openshell-vm/src/lib.rs && \ printf 'fn main() {}\n' > crates/openshell-vm/src/main.rs @@ -125,7 +123,6 @@ COPY crates/openshell-server/ crates/openshell-server/ RUN touch \ crates/openshell-core/build.rs \ crates/openshell-server/src/main.rs \ - crates/openshell-server/src/bin/openshell-gateway.rs \ proto/*.proto && \ if [ -n "${OPENSHELL_CARGO_VERSION:-}" ]; then \ sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${OPENSHELL_CARGO_VERSION}"'"/}' Cargo.toml; \ @@ -142,7 +139,7 @@ RUN --mount=type=cache,id=cargo-registry-${TARGETARCH},sharing=locked,target=/us . cross-build.sh && \ cargo_cross_build --release -p openshell-server ${EXTRA_CARGO_FEATURES:+--features "$EXTRA_CARGO_FEATURES"} && \ mkdir -p /build/out && \ - cp "$(cross_output_dir release)/openshell-server" /build/out/ + cp "$(cross_output_dir release)/openshell-gateway" /build/out/ FROM rust-deps AS supervisor-workspace ARG OPENSHELL_CARGO_VERSION @@ -194,7 +191,7 @@ RUN useradd --create-home --user-group openshell WORKDIR /app -COPY --from=gateway-builder /build/out/openshell-server /usr/local/bin/ +COPY --from=gateway-builder /build/out/openshell-gateway /usr/local/bin/ RUN mkdir -p /build/crates/openshell-server COPY --chmod=755 crates/openshell-server/migrations /build/crates/openshell-server/migrations @@ -202,7 +199,7 @@ COPY --chmod=755 crates/openshell-server/migrations /build/crates/openshell-serv USER openshell EXPOSE 8080 -ENTRYPOINT ["openshell-server"] +ENTRYPOINT ["openshell-gateway"] CMD ["--port", "8080"] # --------------------------------------------------------------------------- diff --git a/tasks/docker.toml b/tasks/docker.toml index da5bd24e4..4eabbaee4 100644 --- a/tasks/docker.toml +++ b/tasks/docker.toml @@ -41,11 +41,6 @@ description = "Build the supervisor Docker image" run = "tasks/scripts/docker-build-image.sh supervisor" hide = true -["docker:build:supervisor"] -description = "Alias for build:docker:supervisor" -depends = ["build:docker:supervisor"] -hide = true - ["build:docker:cluster"] description = "Build the k3s cluster image (component images pulled at runtime from registry)" run = "tasks/scripts/docker-build-image.sh cluster" From ee994165dfb3b6e6e169556b843e012b604bb972 Mon Sep 17 00:00:00 2001 From: Drew Newberry Date: Thu, 16 Apr 2026 15:02:02 -0700 Subject: [PATCH 9/9] fix(ci): restore docker:build:supervisor alias and fix Dockerfile.gateway-macos syntax The docker-build.yml reusable workflow invokes mise tasks via docker:build:$component, so the supervisor alias is required. Also fix a missing && separator in the Dockerfile.gateway-macos mkdir/touch skeleton stage that caused the server src dir to not be created. --- .github/workflows/docker-build.yml | 2 +- deploy/docker/Dockerfile.gateway-macos | 1 + tasks/docker.toml | 25 +++++-------------------- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 5afbabd61..69a8d1d17 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -93,4 +93,4 @@ jobs: # Enable dev-settings feature for test settings (dummy_bool, dummy_int) # used by e2e tests. EXTRA_CARGO_FEATURES: openshell-core/dev-settings - run: mise run --no-prepare docker:build:${{ inputs.component }} + run: mise run --no-prepare build:docker:${{ inputs.component }} diff --git a/deploy/docker/Dockerfile.gateway-macos b/deploy/docker/Dockerfile.gateway-macos index 2917c7027..b0ac282fc 100644 --- a/deploy/docker/Dockerfile.gateway-macos +++ b/deploy/docker/Dockerfile.gateway-macos @@ -65,6 +65,7 @@ RUN mkdir -p crates/openshell-core/src \ crates/openshell-driver-kubernetes/src \ crates/openshell-policy/src \ crates/openshell-router/src \ + crates/openshell-server/src && \ touch crates/openshell-core/src/lib.rs && \ touch crates/openshell-driver-kubernetes/src/lib.rs && \ printf 'fn main() {}\n' > crates/openshell-driver-kubernetes/src/main.rs && \ diff --git a/tasks/docker.toml b/tasks/docker.toml index 4eabbaee4..f05c8aab1 100644 --- a/tasks/docker.toml +++ b/tasks/docker.toml @@ -11,31 +11,16 @@ depends = [ ] hide = true -["docker:build"] -description = "Alias for build:docker" -depends = ["build:docker"] -hide = true - ["build:docker:ci"] description = "Build the CI Docker image" run = "tasks/scripts/docker-build-ci.sh" hide = true -["docker:build:ci"] -description = "Alias for build:docker:ci" -depends = ["build:docker:ci"] -hide = true - ["build:docker:gateway"] description = "Build the gateway Docker image" run = "tasks/scripts/docker-build-image.sh gateway" hide = true -["docker:build:gateway"] -description = "Alias for build:docker:gateway" -depends = ["build:docker:gateway"] -hide = true - ["build:docker:supervisor"] description = "Build the supervisor Docker image" run = "tasks/scripts/docker-build-image.sh supervisor" @@ -46,6 +31,11 @@ description = "Build the k3s cluster image (component images pulled at runtime f run = "tasks/scripts/docker-build-image.sh cluster" hide = true +["docker:build:gateway"] +description = "Alias for build:docker:gateway" +depends = ["build:docker:gateway"] +hide = true + ["docker:build:cluster"] description = "Alias for build:docker:cluster" depends = ["build:docker:cluster"] @@ -56,11 +46,6 @@ description = "Build multi-arch cluster image and push to a registry" run = "tasks/scripts/docker-publish-multiarch.sh" hide = true -["docker:build:cluster:multiarch"] -description = "Alias for build:docker:cluster:multiarch" -depends = ["build:docker:cluster:multiarch"] -hide = true - ["docker:cleanup"] description = "Remove stale images, volumes, and build cache not used by the current cluster" run = "scripts/docker-cleanup.sh --force"