From 35df806d7497bb71f87275c16bd7aa2a366ee380 Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 16 Jun 2026 09:45:30 -0700 Subject: [PATCH 01/16] fix(gateway): lower glibc floor to 2.28 Signed-off-by: Piotr Mlocek --- .github/workflows/release-dev.yml | 8 ++++---- .github/workflows/release-tag.yml | 8 ++++---- .github/workflows/rust-native-build.yml | 8 ++++---- architecture/build.md | 12 ++++++------ docs/about/installation.mdx | 2 +- docs/reference/support-matrix.mdx | 2 +- install.sh | 2 +- tasks/scripts/stage-prebuilt-binaries.sh | 4 ++-- tasks/scripts/test-install-sh.sh | 17 +++++++++-------- 9 files changed, 32 insertions(+), 31 deletions(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index d1e029144..9d4dc8336 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -376,7 +376,7 @@ jobs: retention-days: 5 # --------------------------------------------------------------------------- - # Build standalone gateway binaries (Linux GNU — glibc 2.31 floor) + # Build standalone gateway binaries (Linux GNU — glibc 2.28 floor) # --------------------------------------------------------------------------- build-gateway-binary-linux: name: Build Gateway Binary (Linux ${{ matrix.arch }}) @@ -387,11 +387,11 @@ jobs: - arch: amd64 runner: linux-amd64-cpu8 target: x86_64-unknown-linux-gnu - zig_target: x86_64-unknown-linux-gnu.2.31 + zig_target: x86_64-unknown-linux-gnu.2.28 - arch: arm64 runner: linux-arm64-cpu8 target: aarch64-unknown-linux-gnu - zig_target: aarch64-unknown-linux-gnu.2.31 + zig_target: aarch64-unknown-linux-gnu.2.28 runs-on: ${{ matrix.runner }} timeout-minutes: 60 container: @@ -452,7 +452,7 @@ jobs: fi - name: Verify glibc symbol floor - run: tasks/scripts/verify-glibc-symbols.sh 2.31 artifacts/bin/openshell-gateway + run: tasks/scripts/verify-glibc-symbols.sh 2.28 artifacts/bin/openshell-gateway - name: sccache stats if: always() diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 01e9a6bfa..1abc308f2 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -409,7 +409,7 @@ jobs: retention-days: 5 # --------------------------------------------------------------------------- - # Build standalone gateway binaries (Linux GNU — glibc 2.31 floor) + # Build standalone gateway binaries (Linux GNU — glibc 2.28 floor) # --------------------------------------------------------------------------- build-gateway-binary-linux: name: Build Gateway Binary (Linux ${{ matrix.arch }}) @@ -420,11 +420,11 @@ jobs: - arch: amd64 runner: linux-amd64-cpu8 target: x86_64-unknown-linux-gnu - zig_target: x86_64-unknown-linux-gnu.2.31 + zig_target: x86_64-unknown-linux-gnu.2.28 - arch: arm64 runner: linux-arm64-cpu8 target: aarch64-unknown-linux-gnu - zig_target: aarch64-unknown-linux-gnu.2.31 + zig_target: aarch64-unknown-linux-gnu.2.28 runs-on: ${{ matrix.runner }} timeout-minutes: 60 container: @@ -486,7 +486,7 @@ jobs: fi - name: Verify glibc symbol floor - run: tasks/scripts/verify-glibc-symbols.sh 2.31 artifacts/bin/openshell-gateway + run: tasks/scripts/verify-glibc-symbols.sh 2.28 artifacts/bin/openshell-gateway - name: sccache stats if: always() diff --git a/.github/workflows/rust-native-build.yml b/.github/workflows/rust-native-build.yml index 7d8614fb5..575ace611 100644 --- a/.github/workflows/rust-native-build.yml +++ b/.github/workflows/rust-native-build.yml @@ -7,7 +7,7 @@ name: Rust Image Binary Build (openshell-gateway / openshell-sandbox) # consumes them as prebuilt artifacts. Gateway images use GNU-linked binaries # for the NVIDIA distroless C/C++ runtime; supervisor images use musl/static # binaries so the final image can remain scratch. Gateway GNU binaries are -# built with an explicit glibc 2.31 floor so image, package, and tarball +# built with an explicit glibc 2.28 floor so image, package, and tarball # artifacts share the same host portability contract. on: @@ -134,7 +134,7 @@ jobs: zig_target=x86_64-linux-musl else target=x86_64-unknown-linux-gnu - zig_target=x86_64-unknown-linux-gnu.2.31 + zig_target=x86_64-unknown-linux-gnu.2.28 fi ;; arm64) @@ -143,7 +143,7 @@ jobs: zig_target=aarch64-linux-musl else target=aarch64-unknown-linux-gnu - zig_target=aarch64-unknown-linux-gnu.2.31 + zig_target=aarch64-unknown-linux-gnu.2.28 fi ;; *) @@ -255,7 +255,7 @@ jobs: run: | set -euo pipefail BIN="target/${{ steps.target.outputs.target }}/release/${{ steps.target.outputs.binary }}" - tasks/scripts/verify-glibc-symbols.sh 2.31 "$BIN" + tasks/scripts/verify-glibc-symbols.sh 2.28 "$BIN" - name: Stage binary for prebuilt layout run: | diff --git a/architecture/build.md b/architecture/build.md index 74952ad36..6a2b312f4 100644 --- a/architecture/build.md +++ b/architecture/build.md @@ -44,9 +44,9 @@ disable telemetry in a default (telemetry-enabled) build. OpenShell uses different Linux libc environments for different host artifacts. The standalone `openshell` CLI is built as a static musl binary so it can run on a wide range of Linux distributions without depending on the host's glibc. Host -runtime binaries that use the GNU/Linux runtime environment, including -`openshell-gateway` and `openshell-driver-vm`, are GNU-linked and built with a -glibc 2.31 floor. +runtime binaries that use the GNU/Linux runtime environment are GNU-linked. +`openshell-gateway` is built with a glibc 2.28 floor. `openshell-driver-vm` +continues to use a glibc 2.31 floor. ## Container Builds @@ -58,7 +58,7 @@ Dockerfile compiles Rust — both copy a staged binary out of `deploy/docker/.build/prebuilt-binaries//` into the final image. Binary staging is driven by `tasks/scripts/stage-prebuilt-binaries.sh`. Gateway -binaries use `cargo zigbuild` with GNU targets pinned to glibc 2.31, including +binaries use `cargo zigbuild` with GNU targets pinned to glibc 2.28, including native-architecture builds, so the gateway image, standalone tarballs, and Linux packages share the same host portability floor. Supervisor binaries remain static musl and use `cargo zigbuild` when available, including native CPU @@ -74,8 +74,8 @@ Runtime layout: - **Gateway**: `gcr.io/distroless/cc-debian13:nonroot` base, GNU-linked binary at `/usr/local/bin/openshell-gateway`, runs as UID/GID `1000:1000`. Linux GNU - gateway and VM driver binaries must not reference `GLIBC_*` symbols newer than - `GLIBC_2.31`; release workflows verify this before publishing artifacts. + gateway binaries must not reference `GLIBC_*` symbols newer than + `GLIBC_2.28`; release workflows verify this before publishing artifacts. - **Supervisor**: `scratch` base, static musl binary at `/openshell-sandbox`. Static linkage is required because the image is mounted/extracted into sandbox environments (Docker extraction, Podman image volumes, Kubernetes diff --git a/docs/about/installation.mdx b/docs/about/installation.mdx index f0ad72455..256015947 100644 --- a/docs/about/installation.mdx +++ b/docs/about/installation.mdx @@ -55,7 +55,7 @@ On Fedora and RHEL, the install script uses RPM packages. The RPM installs the ` On Debian and Ubuntu, the install script uses a Debian package. The Debian package installs the `openshell` CLI, the `openshell-gateway` daemon, VM sandbox support, and a systemd user service. -Linux packages require glibc 2.31 or newer. The installer checks libc before downloading packages and exits with an error on older glibc versions, Alpine, musl-based distributions, or unknown libc environments. +Linux packages require glibc 2.28 or newer. The installer checks libc before downloading packages and exits with an error on older glibc versions, Alpine, musl-based distributions, or unknown libc environments. The Linux user service listens on `https://127.0.0.1:17670`, starts from built-in defaults, and generates a local mTLS bundle before the gateway starts. Create `~/.config/openshell/gateway.toml` only when you need to override those defaults. diff --git a/docs/reference/support-matrix.mdx b/docs/reference/support-matrix.mdx index 6fe54921a..bb390b06e 100644 --- a/docs/reference/support-matrix.mdx +++ b/docs/reference/support-matrix.mdx @@ -33,7 +33,7 @@ OpenShell publishes standalone `openshell-gateway` release assets for manual dow These artifacts are attached to GitHub releases. Kubernetes deployments should use the Helm chart and the published gateway image. -On Linux, `openshell-gateway` requires glibc 2.31 or newer. Compatible systems include, for example, Ubuntu 20.04+, RHEL 9+, Amazon Linux 2023+, and Fedora 32+. +On Linux, `openshell-gateway` requires glibc 2.28 or newer. Compatible systems include, for example, Ubuntu 20.04+, RHEL 8+, Rocky Linux 8+, Amazon Linux 2023+, and Fedora 32+. ## Compute Drivers diff --git a/install.sh b/install.sh index 6a8bc3029..6cda59f8b 100755 --- a/install.sh +++ b/install.sh @@ -19,7 +19,7 @@ LOCAL_GATEWAY_PORT="17670" HOMEBREW_TAP="nvidia/openshell" HOMEBREW_FORMULA_NAME="openshell" BREAKING_RELEASE_VERSION="0.0.37" -LINUX_PACKAGE_GLIBC_MIN_VERSION="2.31" +LINUX_PACKAGE_GLIBC_MIN_VERSION="2.28" UPGRADE_NOTICE_ACK="${OPENSHELL_ACK_BREAKING_UPGRADE:-}" info() { diff --git a/tasks/scripts/stage-prebuilt-binaries.sh b/tasks/scripts/stage-prebuilt-binaries.sh index a5436f896..18274a0a2 100755 --- a/tasks/scripts/stage-prebuilt-binaries.sh +++ b/tasks/scripts/stage-prebuilt-binaries.sh @@ -165,9 +165,9 @@ build_component_for_arch() { if [[ "$component" == "gateway" ]]; then if has_cargo_zigbuild; then cargo_subcommand=(cargo zigbuild) - build_target="${target}.2.31" + build_target="${target}.2.28" else - echo "Error: cargo-zigbuild + zig are required to build ${binary} with the glibc 2.31 floor." >&2 + echo "Error: cargo-zigbuild + zig are required to build ${binary} with the glibc 2.28 floor." >&2 exit 1 fi elif [[ "$target_libc" == "musl" ]] && has_cargo_zigbuild; then diff --git a/tasks/scripts/test-install-sh.sh b/tasks/scripts/test-install-sh.sh index 5bf98071a..a1259cf0b 100755 --- a/tasks/scripts/test-install-sh.sh +++ b/tasks/scripts/test-install-sh.sh @@ -44,9 +44,9 @@ assert_glibc_preflight_fails() { fi } -setup_glibc_228() { +setup_glibc_227() { export OPENSHELL_TEST_GETCONF_UNAVAILABLE=1 - export OPENSHELL_TEST_LDD_OUTPUT="ldd (GNU libc) 2.28" + export OPENSHELL_TEST_LDD_OUTPUT="ldd (GNU libc) 2.27" } setup_missing_glibc() { @@ -64,6 +64,7 @@ setup_ldd_musl() { export OPENSHELL_TEST_LDD_OUTPUT="musl libc (x86_64)" } +assert_glibc_preflight_passes "glibc 2.28 passes" "glibc 2.28" assert_glibc_preflight_passes "glibc 2.31 passes" "glibc 2.31" assert_glibc_preflight_passes "glibc 2.35 passes" "ldd (GNU libc) 2.35" @@ -80,23 +81,23 @@ if ! (export OPENSHELL_TEST_LDD_OUTPUT="not ldd" OPENSHELL_TEST_GETCONF_OUTPUT=" fi assert_glibc_preflight_fails \ - "glibc 2.28 fails" \ - "OpenShell Linux packages require glibc >= 2.31; detected glibc 2.28." \ - setup_glibc_228 + "glibc 2.27 fails" \ + "OpenShell Linux packages require glibc >= 2.28; detected glibc 2.27." \ + setup_glibc_227 assert_glibc_preflight_fails \ "missing glibc detection fails" \ - "OpenShell Linux packages require glibc >= 2.31; could not detect glibc." \ + "OpenShell Linux packages require glibc >= 2.28; could not detect glibc." \ setup_missing_glibc assert_glibc_preflight_fails \ "musl detection fails" \ - "OpenShell Linux packages require glibc >= 2.31; detected musl or unsupported libc." \ + "OpenShell Linux packages require glibc >= 2.28; detected musl or unsupported libc." \ setup_getconf_musl assert_glibc_preflight_fails \ "ldd musl fallback fails" \ - "OpenShell Linux packages require glibc >= 2.31; detected musl or unsupported libc." \ + "OpenShell Linux packages require glibc >= 2.28; detected musl or unsupported libc." \ setup_ldd_musl echo "install.sh libc preflight tests passed" From 6dfd3b9d20738eeb369b8857d7a2205a05ba11e2 Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 16 Jun 2026 09:53:45 -0700 Subject: [PATCH 02/16] fix(driver-vm): lower glibc floor to 2.28 Signed-off-by: Piotr Mlocek --- .github/workflows/driver-vm-linux.yml | 8 ++++---- architecture/build.md | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/.github/workflows/driver-vm-linux.yml b/.github/workflows/driver-vm-linux.yml index f4abb301f..4ee3487ad 100644 --- a/.github/workflows/driver-vm-linux.yml +++ b/.github/workflows/driver-vm-linux.yml @@ -81,13 +81,13 @@ jobs: - arch: arm64 runner: linux-arm64-cpu8 target: aarch64-unknown-linux-gnu - zig_target: aarch64-unknown-linux-gnu.2.31 + zig_target: aarch64-unknown-linux-gnu.2.28 platform: linux-aarch64 guest_arch: aarch64 - arch: amd64 runner: linux-amd64-cpu8 target: x86_64-unknown-linux-gnu - zig_target: x86_64-unknown-linux-gnu.2.31 + zig_target: x86_64-unknown-linux-gnu.2.28 platform: linux-x86_64 guest_arch: x86_64 runs-on: ${{ matrix.runner }} @@ -165,7 +165,7 @@ jobs: set -euo pipefail sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${{ inputs['cargo-version'] }}"'"/}' Cargo.toml - - name: Build openshell-driver-vm with glibc 2.31 floor + - name: Build openshell-driver-vm with glibc 2.28 floor run: | set -euo pipefail mise x -- rustup target add ${{ matrix.target }} @@ -182,7 +182,7 @@ jobs: grep -q '^openshell-driver-vm ' <<<"$OUTPUT" - name: Verify glibc symbol floor - run: tasks/scripts/verify-glibc-symbols.sh 2.31 artifacts/bin/openshell-driver-vm + run: tasks/scripts/verify-glibc-symbols.sh 2.28 artifacts/bin/openshell-driver-vm - name: sccache stats if: always() diff --git a/architecture/build.md b/architecture/build.md index 6a2b312f4..fe87d9684 100644 --- a/architecture/build.md +++ b/architecture/build.md @@ -45,8 +45,7 @@ OpenShell uses different Linux libc environments for different host artifacts. The standalone `openshell` CLI is built as a static musl binary so it can run on a wide range of Linux distributions without depending on the host's glibc. Host runtime binaries that use the GNU/Linux runtime environment are GNU-linked. -`openshell-gateway` is built with a glibc 2.28 floor. `openshell-driver-vm` -continues to use a glibc 2.31 floor. +`openshell-gateway` and `openshell-driver-vm` are built with a glibc 2.28 floor. ## Container Builds @@ -60,10 +59,12 @@ Dockerfile compiles Rust — both copy a staged binary out of Binary staging is driven by `tasks/scripts/stage-prebuilt-binaries.sh`. Gateway binaries use `cargo zigbuild` with GNU targets pinned to glibc 2.28, including native-architecture builds, so the gateway image, standalone tarballs, and Linux -packages share the same host portability floor. Supervisor binaries remain -static musl and use `cargo zigbuild` when available, including native CPU -architectures, so C dependencies are compiled for the musl target instead of the -host GNU libc target. Local Docker image tasks infer the target architecture from +packages share the same host portability floor. Linux VM driver release +artifacts use the same glibc floor so package-managed VM support does not raise +the package runtime requirement. Supervisor binaries remain static musl and use +`cargo zigbuild` when available, including native CPU architectures, so C +dependencies are compiled for the musl target instead of the host GNU libc +target. Local Docker image tasks infer the target architecture from `DOCKER_PLATFORM` when set, otherwise from the container engine host metadata with the kernel architecture as the fallback. CI invokes the same staging step via the `rust-native-build.yml` workflow (per-architecture, per-component) and @@ -76,6 +77,11 @@ Runtime layout: `/usr/local/bin/openshell-gateway`, runs as UID/GID `1000:1000`. Linux GNU gateway binaries must not reference `GLIBC_*` symbols newer than `GLIBC_2.28`; release workflows verify this before publishing artifacts. +- **VM driver**: host GNU-linked binary installed at + `/usr/libexec/openshell/openshell-driver-vm` in Linux packages and published + as a release artifact. Linux GNU VM driver binaries must not reference + `GLIBC_*` symbols newer than `GLIBC_2.28`; release workflows verify this + before publishing artifacts. - **Supervisor**: `scratch` base, static musl binary at `/openshell-sandbox`. Static linkage is required because the image is mounted/extracted into sandbox environments (Docker extraction, Podman image volumes, Kubernetes From 19ef8f1cf98113ba590034d1268374591a72c65b Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 16 Jun 2026 10:15:36 -0700 Subject: [PATCH 03/16] fix(linux): use zig default for glibc 2.28 builds Signed-off-by: Piotr Mlocek --- .github/workflows/driver-vm-linux.yml | 4 ++-- .github/workflows/release-dev.yml | 4 ++-- .github/workflows/release-tag.yml | 4 ++-- .github/workflows/rust-native-build.yml | 9 ++++---- architecture/build.md | 27 ++++++++++++------------ tasks/scripts/stage-prebuilt-binaries.sh | 5 ++++- 6 files changed, 29 insertions(+), 24 deletions(-) diff --git a/.github/workflows/driver-vm-linux.yml b/.github/workflows/driver-vm-linux.yml index 4ee3487ad..1c734bc01 100644 --- a/.github/workflows/driver-vm-linux.yml +++ b/.github/workflows/driver-vm-linux.yml @@ -81,13 +81,13 @@ jobs: - arch: arm64 runner: linux-arm64-cpu8 target: aarch64-unknown-linux-gnu - zig_target: aarch64-unknown-linux-gnu.2.28 + zig_target: aarch64-unknown-linux-gnu platform: linux-aarch64 guest_arch: aarch64 - arch: amd64 runner: linux-amd64-cpu8 target: x86_64-unknown-linux-gnu - zig_target: x86_64-unknown-linux-gnu.2.28 + zig_target: x86_64-unknown-linux-gnu platform: linux-x86_64 guest_arch: x86_64 runs-on: ${{ matrix.runner }} diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 9d4dc8336..6a99a4367 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -387,11 +387,11 @@ jobs: - arch: amd64 runner: linux-amd64-cpu8 target: x86_64-unknown-linux-gnu - zig_target: x86_64-unknown-linux-gnu.2.28 + zig_target: x86_64-unknown-linux-gnu - arch: arm64 runner: linux-arm64-cpu8 target: aarch64-unknown-linux-gnu - zig_target: aarch64-unknown-linux-gnu.2.28 + zig_target: aarch64-unknown-linux-gnu runs-on: ${{ matrix.runner }} timeout-minutes: 60 container: diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 1abc308f2..adfe4acbd 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -420,11 +420,11 @@ jobs: - arch: amd64 runner: linux-amd64-cpu8 target: x86_64-unknown-linux-gnu - zig_target: x86_64-unknown-linux-gnu.2.28 + zig_target: x86_64-unknown-linux-gnu - arch: arm64 runner: linux-arm64-cpu8 target: aarch64-unknown-linux-gnu - zig_target: aarch64-unknown-linux-gnu.2.28 + zig_target: aarch64-unknown-linux-gnu runs-on: ${{ matrix.runner }} timeout-minutes: 60 container: diff --git a/.github/workflows/rust-native-build.yml b/.github/workflows/rust-native-build.yml index 575ace611..749642a59 100644 --- a/.github/workflows/rust-native-build.yml +++ b/.github/workflows/rust-native-build.yml @@ -7,8 +7,9 @@ name: Rust Image Binary Build (openshell-gateway / openshell-sandbox) # consumes them as prebuilt artifacts. Gateway images use GNU-linked binaries # for the NVIDIA distroless C/C++ runtime; supervisor images use musl/static # binaries so the final image can remain scratch. Gateway GNU binaries are -# built with an explicit glibc 2.28 floor so image, package, and tarball -# artifacts share the same host portability contract. +# built with cargo-zigbuild and the repo-pinned Zig default glibc 2.28 floor +# so image, package, and tarball artifacts share the same host portability +# contract. on: workflow_call: @@ -134,7 +135,7 @@ jobs: zig_target=x86_64-linux-musl else target=x86_64-unknown-linux-gnu - zig_target=x86_64-unknown-linux-gnu.2.28 + zig_target=x86_64-unknown-linux-gnu fi ;; arm64) @@ -143,7 +144,7 @@ jobs: zig_target=aarch64-linux-musl else target=aarch64-unknown-linux-gnu - zig_target=aarch64-unknown-linux-gnu.2.28 + zig_target=aarch64-unknown-linux-gnu fi ;; *) diff --git a/architecture/build.md b/architecture/build.md index fe87d9684..f87ab3f82 100644 --- a/architecture/build.md +++ b/architecture/build.md @@ -57,19 +57,20 @@ Dockerfile compiles Rust — both copy a staged binary out of `deploy/docker/.build/prebuilt-binaries//` into the final image. Binary staging is driven by `tasks/scripts/stage-prebuilt-binaries.sh`. Gateway -binaries use `cargo zigbuild` with GNU targets pinned to glibc 2.28, including -native-architecture builds, so the gateway image, standalone tarballs, and Linux -packages share the same host portability floor. Linux VM driver release -artifacts use the same glibc floor so package-managed VM support does not raise -the package runtime requirement. Supervisor binaries remain static musl and use -`cargo zigbuild` when available, including native CPU architectures, so C -dependencies are compiled for the musl target instead of the host GNU libc -target. Local Docker image tasks infer the target architecture from -`DOCKER_PLATFORM` when set, otherwise from the container engine host metadata -with the kernel architecture as the fallback. CI invokes the same staging step -via the `rust-native-build.yml` workflow (per-architecture, per-component) and -uploads the result as an artifact that the image build job downloads back into -the staging directory before running Buildx. +binaries use `cargo zigbuild` with bare GNU targets. The repo-pinned Zig 0.14.1 +default for GNU targets is glibc 2.28, so the gateway image, standalone +tarballs, and Linux packages share the same host portability floor. Linux VM +driver release artifacts use the same glibc floor so package-managed VM support +does not raise the package runtime requirement. Release workflows verify the +maximum referenced `GLIBC_*` symbol version before publishing artifacts. +Supervisor binaries remain static musl and use `cargo zigbuild` when available, +including native CPU architectures, so C dependencies are compiled for the musl +target instead of the host GNU libc target. Local Docker image tasks infer the +target architecture from `DOCKER_PLATFORM` when set, otherwise from the +container engine host metadata with the kernel architecture as the fallback. CI +invokes the same staging step via the `rust-native-build.yml` workflow +(per-architecture, per-component) and uploads the result as an artifact that the +image build job downloads back into the staging directory before running Buildx. Runtime layout: diff --git a/tasks/scripts/stage-prebuilt-binaries.sh b/tasks/scripts/stage-prebuilt-binaries.sh index 18274a0a2..bd2cccbc2 100755 --- a/tasks/scripts/stage-prebuilt-binaries.sh +++ b/tasks/scripts/stage-prebuilt-binaries.sh @@ -165,7 +165,10 @@ build_component_for_arch() { if [[ "$component" == "gateway" ]]; then if has_cargo_zigbuild; then cargo_subcommand=(cargo zigbuild) - build_target="${target}.2.28" + # The repo-pinned Zig 0.14.1 defaults GNU targets to glibc 2.28. The + # explicit .2.28 suffix is invalid for Zig's C/C++ wrapper path, so keep + # the Rust target bare and rely on verify-glibc-symbols.sh as the guard. + build_target="${target}" else echo "Error: cargo-zigbuild + zig are required to build ${binary} with the glibc 2.28 floor." >&2 exit 1 From 1b04fd75aca30c74fa31b23c012ff086d5dd43c3 Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 16 Jun 2026 15:12:22 -0700 Subject: [PATCH 04/16] fix(release): unbundle gateway z3 runtime Signed-off-by: Piotr Mlocek --- .github/workflows/docker-build.yml | 2 +- .github/workflows/driver-vm-linux.yml | 4 ++-- .github/workflows/release-dev.yml | 12 ++++++------ .github/workflows/release-tag.yml | 18 ++++++++++-------- .github/workflows/rust-native-build.yml | 17 +++++++++-------- architecture/build.md | 21 ++++++++++++++------- deploy/deb/control.in | 1 + deploy/docker/Dockerfile.gateway | 14 ++++++++++++-- docs/about/installation.mdx | 4 ++-- docs/reference/support-matrix.mdx | 1 + openshell.spec | 1 + python/openshell/release_formula_test.py | 1 + snapcraft.yaml | 2 ++ tasks/scripts/release.py | 1 + tasks/scripts/stage-prebuilt-binaries.sh | 8 +------- 15 files changed, 64 insertions(+), 43 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index d9ff2d971..a716fee83 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -162,7 +162,7 @@ jobs: cargo-version: ${{ inputs['cargo-version'] }} image-tag: ${{ needs.resolve.outputs.image_tag_base }} checkout-ref: ${{ inputs['checkout-ref'] }} - features: ${{ inputs.component == 'gateway' && 'openshell-core/dev-settings bundled-z3' || 'openshell-core/dev-settings' }} + features: openshell-core/dev-settings artifact-name: ${{ needs.resolve.outputs.artifact_prefix }}-linux-${{ matrix.arch }} secrets: inherit diff --git a/.github/workflows/driver-vm-linux.yml b/.github/workflows/driver-vm-linux.yml index 1c734bc01..4ee3487ad 100644 --- a/.github/workflows/driver-vm-linux.yml +++ b/.github/workflows/driver-vm-linux.yml @@ -81,13 +81,13 @@ jobs: - arch: arm64 runner: linux-arm64-cpu8 target: aarch64-unknown-linux-gnu - zig_target: aarch64-unknown-linux-gnu + zig_target: aarch64-unknown-linux-gnu.2.28 platform: linux-aarch64 guest_arch: aarch64 - arch: amd64 runner: linux-amd64-cpu8 target: x86_64-unknown-linux-gnu - zig_target: x86_64-unknown-linux-gnu + zig_target: x86_64-unknown-linux-gnu.2.28 platform: linux-x86_64 guest_arch: x86_64 runs-on: ${{ matrix.runner }} diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 6a99a4367..f9148a106 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -387,11 +387,11 @@ jobs: - arch: amd64 runner: linux-amd64-cpu8 target: x86_64-unknown-linux-gnu - zig_target: x86_64-unknown-linux-gnu + zig_target: x86_64-unknown-linux-gnu.2.28 - arch: arm64 runner: linux-arm64-cpu8 target: aarch64-unknown-linux-gnu - zig_target: aarch64-unknown-linux-gnu + zig_target: aarch64-unknown-linux-gnu.2.28 runs-on: ${{ matrix.runner }} timeout-minutes: 60 container: @@ -435,7 +435,7 @@ jobs: run: | set -euo pipefail mise x -- rustup target add ${{ matrix.target }} - mise x -- cargo zigbuild --release --target ${{ matrix.zig_target }} -p openshell-server --bin openshell-gateway --features bundled-z3 + mise x -- cargo zigbuild --release --target ${{ matrix.zig_target }} -p openshell-server --bin openshell-gateway mkdir -p artifacts/bin install -m 0755 target/${{ matrix.target }}/release/openshell-gateway artifacts/bin/openshell-gateway @@ -446,8 +446,8 @@ jobs: echo "$OUTPUT" grep -q '^openshell-gateway ' <<<"$OUTPUT" ldd artifacts/bin/openshell-gateway || true - if ldd artifacts/bin/openshell-gateway | grep -q 'libz3'; then - echo "gateway binary must not depend on shared libz3; build with bundled-z3" >&2 + if ! ldd artifacts/bin/openshell-gateway | grep -q 'libz3'; then + echo "gateway binary must link shared libz3; do not build with bundled-z3" >&2 exit 1 fi @@ -705,7 +705,7 @@ jobs: set -euo pipefail apt-get update apt-get install -y --no-install-recommends ./package-input/*.deb - openshell-gateway --version + LD_BIND_NOW=1 openshell-gateway --version /usr/libexec/openshell/openshell-driver-vm --version # --------------------------------------------------------------------------- diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index adfe4acbd..d90c482b3 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -420,11 +420,11 @@ jobs: - arch: amd64 runner: linux-amd64-cpu8 target: x86_64-unknown-linux-gnu - zig_target: x86_64-unknown-linux-gnu + zig_target: x86_64-unknown-linux-gnu.2.28 - arch: arm64 runner: linux-arm64-cpu8 target: aarch64-unknown-linux-gnu - zig_target: aarch64-unknown-linux-gnu + zig_target: aarch64-unknown-linux-gnu.2.28 runs-on: ${{ matrix.runner }} timeout-minutes: 60 container: @@ -469,7 +469,7 @@ jobs: run: | set -euo pipefail mise x -- rustup target add ${{ matrix.target }} - mise x -- cargo zigbuild --release --target ${{ matrix.zig_target }} -p openshell-server --bin openshell-gateway --features bundled-z3 + mise x -- cargo zigbuild --release --target ${{ matrix.zig_target }} -p openshell-server --bin openshell-gateway mkdir -p artifacts/bin install -m 0755 target/${{ matrix.target }}/release/openshell-gateway artifacts/bin/openshell-gateway @@ -480,8 +480,8 @@ jobs: echo "$OUTPUT" grep -q '^openshell-gateway ' <<<"$OUTPUT" ldd artifacts/bin/openshell-gateway || true - if ldd artifacts/bin/openshell-gateway | grep -q 'libz3'; then - echo "gateway binary must not depend on shared libz3; build with bundled-z3" >&2 + if ! ldd artifacts/bin/openshell-gateway | grep -q 'libz3'; then + echo "gateway binary must link shared libz3; do not build with bundled-z3" >&2 exit 1 fi @@ -782,10 +782,12 @@ jobs: if: matrix.kind == 'binary' run: | set -euo pipefail + apt-get update + apt-get install -y --no-install-recommends libz3-4 mkdir -p smoke-bin tar -xzf smoke-input/openshell-gateway-${{ matrix.target }}.tar.gz -C smoke-bin tar -xzf smoke-input/openshell-driver-vm-${{ matrix.target }}.tar.gz -C smoke-bin - smoke-bin/openshell-gateway --version + LD_BIND_NOW=1 smoke-bin/openshell-gateway --version smoke-bin/openshell-driver-vm --version - name: Download Debian package artifact @@ -801,7 +803,7 @@ jobs: set -euo pipefail apt-get update apt-get install -y --no-install-recommends ./package-input/*.deb - openshell-gateway --version + LD_BIND_NOW=1 openshell-gateway --version /usr/libexec/openshell/openshell-driver-vm --version - name: Download RPM package artifacts @@ -816,7 +818,7 @@ jobs: run: | set -euo pipefail dnf install -y ./package-input/openshell-[0-9]*.rpm ./package-input/openshell-gateway-*.rpm - openshell-gateway --version + LD_BIND_NOW=1 openshell-gateway --version # --------------------------------------------------------------------------- # Create a tagged GitHub Release with CLI, gateway, driver, and wheels diff --git a/.github/workflows/rust-native-build.yml b/.github/workflows/rust-native-build.yml index 749642a59..7aabd0232 100644 --- a/.github/workflows/rust-native-build.yml +++ b/.github/workflows/rust-native-build.yml @@ -7,9 +7,8 @@ name: Rust Image Binary Build (openshell-gateway / openshell-sandbox) # consumes them as prebuilt artifacts. Gateway images use GNU-linked binaries # for the NVIDIA distroless C/C++ runtime; supervisor images use musl/static # binaries so the final image can remain scratch. Gateway GNU binaries are -# built with cargo-zigbuild and the repo-pinned Zig default glibc 2.28 floor -# so image, package, and tarball artifacts share the same host portability -# contract. +# built with an explicit glibc 2.28 floor so image, package, and tarball +# artifacts share the same host portability contract. on: workflow_call: @@ -135,7 +134,7 @@ jobs: zig_target=x86_64-linux-musl else target=x86_64-unknown-linux-gnu - zig_target=x86_64-unknown-linux-gnu + zig_target=x86_64-unknown-linux-gnu.2.28 fi ;; arm64) @@ -144,7 +143,7 @@ jobs: zig_target=aarch64-linux-musl else target=aarch64-unknown-linux-gnu - zig_target=aarch64-unknown-linux-gnu + zig_target=aarch64-unknown-linux-gnu.2.28 fi ;; *) @@ -246,9 +245,11 @@ jobs: # Record linkage so image runtime drift is visible in logs. ldd --version ldd "$BIN" || true - if [[ "${{ inputs.component }}" == "gateway" ]] && ldd "$BIN" | grep -q 'libz3'; then - echo "gateway binary must not depend on shared libz3; enable bundled-z3 for image artifacts" >&2 - exit 1 + if [[ "${{ inputs.component }}" == "gateway" ]]; then + if ! ldd "$BIN" | grep -q 'libz3'; then + echo "gateway binary must link shared libz3; do not enable bundled-z3 for image artifacts" >&2 + exit 1 + fi fi - name: Verify glibc symbol floor diff --git a/architecture/build.md b/architecture/build.md index f87ab3f82..bae39a293 100644 --- a/architecture/build.md +++ b/architecture/build.md @@ -46,6 +46,9 @@ The standalone `openshell` CLI is built as a static musl binary so it can run on a wide range of Linux distributions without depending on the host's glibc. Host runtime binaries that use the GNU/Linux runtime environment are GNU-linked. `openshell-gateway` and `openshell-driver-vm` are built with a glibc 2.28 floor. +The gateway links against system `libz3` at runtime; Debian and RPM packages +declare that dependency, and the gateway container image copies the matching +runtime library into the distroless image. ## Container Builds @@ -57,12 +60,14 @@ Dockerfile compiles Rust — both copy a staged binary out of `deploy/docker/.build/prebuilt-binaries//` into the final image. Binary staging is driven by `tasks/scripts/stage-prebuilt-binaries.sh`. Gateway -binaries use `cargo zigbuild` with bare GNU targets. The repo-pinned Zig 0.14.1 -default for GNU targets is glibc 2.28, so the gateway image, standalone -tarballs, and Linux packages share the same host portability floor. Linux VM -driver release artifacts use the same glibc floor so package-managed VM support -does not raise the package runtime requirement. Release workflows verify the -maximum referenced `GLIBC_*` symbol version before publishing artifacts. +binaries use `cargo zigbuild` with GNU targets pinned to glibc 2.28, including +native-architecture builds, so the gateway image, standalone tarballs, and Linux +packages share the same host portability floor. The gateway build uses system Z3 +instead of the `bundled-z3` feature so cargo-zigbuild does not compile the Z3 +C/C++ sources. Linux VM driver release artifacts use the same glibc floor so +package-managed VM support does not raise the package runtime requirement. +Release workflows verify the maximum referenced `GLIBC_*` symbol version before +publishing artifacts. Supervisor binaries remain static musl and use `cargo zigbuild` when available, including native CPU architectures, so C dependencies are compiled for the musl target instead of the host GNU libc target. Local Docker image tasks infer the @@ -77,7 +82,9 @@ Runtime layout: - **Gateway**: `gcr.io/distroless/cc-debian13:nonroot` base, GNU-linked binary at `/usr/local/bin/openshell-gateway`, runs as UID/GID `1000:1000`. Linux GNU gateway binaries must not reference `GLIBC_*` symbols newer than - `GLIBC_2.28`; release workflows verify this before publishing artifacts. + `GLIBC_2.28`; release workflows verify this before publishing artifacts. The + image includes `libz3.so.4` copied from Debian so the dynamic gateway binary + does not depend on bundled Z3. - **VM driver**: host GNU-linked binary installed at `/usr/libexec/openshell/openshell-driver-vm` in Linux packages and published as a release artifact. Linux GNU VM driver binaries must not reference diff --git a/deploy/deb/control.in b/deploy/deb/control.in index 9f77f8775..2578eda08 100644 --- a/deploy/deb/control.in +++ b/deploy/deb/control.in @@ -5,6 +5,7 @@ Maintainer: NVIDIA OpenShell Maintainers Section: utils Priority: optional Pre-Depends: init-system-helpers (>= 1.54~) +Depends: libz3-4 Homepage: https://github.com/NVIDIA/OpenShell Description: Safe, sandboxed runtimes for autonomous AI agents OpenShell provides host-side command-line and gateway components for diff --git a/deploy/docker/Dockerfile.gateway b/deploy/docker/Dockerfile.gateway index 9dd7ed8b9..d8d462189 100644 --- a/deploy/docker/Dockerfile.gateway +++ b/deploy/docker/Dockerfile.gateway @@ -16,17 +16,27 @@ # # The runtime is distroless Debian 13, which provides glibc and the dynamic # loader needed by the GNU-linked gateway binary while keeping the attack -# surface small. The default digest currently carries Debian glibc -# 2.41-12+deb13u3. +# surface small. libz3 is copied from a matching Debian runtime stage because +# the gateway intentionally links system Z3 instead of bundling it into the +# binary. The default digest currently carries Debian glibc 2.41-12+deb13u3. ARG GATEWAY_BASE_IMAGE=gcr.io/distroless/cc-debian13:nonroot@sha256:e1fd250ce83d94603e9887ec991156a6c26905a6b0001039b7a43699018c0733 +FROM debian:trixie-slim AS z3-runtime + +RUN apt-get update \ + && apt-get install -y --no-install-recommends libz3-4 \ + && mkdir -p /out \ + && cp --parents /usr/lib/*-linux-gnu/libz3.so.4* /out/ \ + && rm -rf /var/lib/apt/lists/* + FROM ${GATEWAY_BASE_IMAGE} AS gateway ARG TARGETARCH WORKDIR /app +COPY --from=z3-runtime /out/ / COPY deploy/docker/.build/prebuilt-binaries/${TARGETARCH}/openshell-gateway /usr/local/bin/openshell-gateway USER 1000:1000 diff --git a/docs/about/installation.mdx b/docs/about/installation.mdx index 256015947..4f8400624 100644 --- a/docs/about/installation.mdx +++ b/docs/about/installation.mdx @@ -51,9 +51,9 @@ brew services restart openshell ## Linux -On Fedora and RHEL, the install script uses RPM packages. The RPM installs the `openshell` CLI, the `openshell-gateway` daemon, and a systemd user service. +On Fedora and RHEL, the install script uses RPM packages. The RPM installs the `openshell` CLI, the `openshell-gateway` daemon, the Z3 runtime library dependency, and a systemd user service. -On Debian and Ubuntu, the install script uses a Debian package. The Debian package installs the `openshell` CLI, the `openshell-gateway` daemon, VM sandbox support, and a systemd user service. +On Debian and Ubuntu, the install script uses a Debian package. The Debian package installs the `openshell` CLI, the `openshell-gateway` daemon, VM sandbox support, the Z3 runtime library dependency, and a systemd user service. Linux packages require glibc 2.28 or newer. The installer checks libc before downloading packages and exits with an error on older glibc versions, Alpine, musl-based distributions, or unknown libc environments. diff --git a/docs/reference/support-matrix.mdx b/docs/reference/support-matrix.mdx index bb390b06e..ad34baf05 100644 --- a/docs/reference/support-matrix.mdx +++ b/docs/reference/support-matrix.mdx @@ -54,6 +54,7 @@ Install the software for the compute driver you use: |---|---|---| | Docker Desktop or Docker Engine | 28.04 | Required for Docker-backed gateways, local image builds, and Docker development workflows. | | Podman | 5.x | Required for Podman-backed gateways. | +| Z3 runtime library | `libz3.so.4` | Installed by Linux gateway packages; required when running standalone GNU/Linux gateway tarballs directly. | | Kubernetes | 1.29 | Required for Helm deployments and Kubernetes sandbox scheduling. | | Helm | 3.x | Required to install `deploy/helm/openshell`. | | kubectl | Compatible with your cluster | Required for Kubernetes operational inspection and secret creation. | diff --git a/openshell.spec b/openshell.spec index 3200659d7..b23524292 100644 --- a/openshell.spec +++ b/openshell.spec @@ -68,6 +68,7 @@ LLM inference routing. Summary: OpenShell gateway server with Podman sandbox driver Requires: podman Requires: openssl +Requires: z3-libs Requires: %{name} = %{version}-%{release} %description gateway diff --git a/python/openshell/release_formula_test.py b/python/openshell/release_formula_test.py index b3ab871ae..8d9a420d7 100644 --- a/python/openshell/release_formula_test.py +++ b/python/openshell/release_formula_test.py @@ -51,6 +51,7 @@ def test_generate_homebrew_formula_uses_tagged_macos_driver_asset_without_defaul "v0.0.10/openshell-driver-vm-aarch64-apple-darwin.tar.gz" ) in formula assert 'sha256 "' + "b" * 64 + '"' in formula + assert 'depends_on "z3"' in formula assert "OPENSHELL_DRIVERS: " not in formula assert 'OPENSHELL_GATEWAY_CONFIG: "#{var}/openshell/gateway.toml"' not in formula assert "init-gateway-config.sh" not in formula diff --git a/snapcraft.yaml b/snapcraft.yaml index 01d90366e..e03043fad 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -103,6 +103,8 @@ parts: openshell: plugin: nil source: ./snap/prebuilt + stage-packages: + - libz3-4 override-pull: | craftctl default craftctl set version="$(cat "$CRAFT_PART_SRC/version")" diff --git a/tasks/scripts/release.py b/tasks/scripts/release.py index f00bd19d3..9c8e13223 100644 --- a/tasks/scripts/release.py +++ b/tasks/scripts/release.py @@ -250,6 +250,7 @@ class Openshell < Formula depends_on macos: :big_sur depends_on arch: :arm64 + depends_on "z3" resource "openshell-gateway" do url "{_asset_url(release_tag, HOMEBREW_GATEWAY_ASSET)}" diff --git a/tasks/scripts/stage-prebuilt-binaries.sh b/tasks/scripts/stage-prebuilt-binaries.sh index bd2cccbc2..ecd560dc0 100755 --- a/tasks/scripts/stage-prebuilt-binaries.sh +++ b/tasks/scripts/stage-prebuilt-binaries.sh @@ -153,9 +153,6 @@ build_component_for_arch() { target="$(target_triple "$arch" "$target_libc")" stage="${ROOT}/deploy/docker/.build/prebuilt-binaries/${arch}" features="${EXTRA_CARGO_FEATURES:-openshell-core/dev-settings}" - if [[ "$component" == "gateway" && " ${features} " != *" bundled-z3 "* ]]; then - features="${features} bundled-z3" - fi current_host_os="$(host_os)" current_host_arch="$(host_arch)" @@ -165,10 +162,7 @@ build_component_for_arch() { if [[ "$component" == "gateway" ]]; then if has_cargo_zigbuild; then cargo_subcommand=(cargo zigbuild) - # The repo-pinned Zig 0.14.1 defaults GNU targets to glibc 2.28. The - # explicit .2.28 suffix is invalid for Zig's C/C++ wrapper path, so keep - # the Rust target bare and rely on verify-glibc-symbols.sh as the guard. - build_target="${target}" + build_target="${target}.2.28" else echo "Error: cargo-zigbuild + zig are required to build ${binary} with the glibc 2.28 floor." >&2 exit 1 From 6a31dbb752f54c50f9da8d3ee1724ab3feffdadd Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 16 Jun 2026 16:12:16 -0700 Subject: [PATCH 05/16] ci(release): add temporary package smoke dispatch Signed-off-by: Piotr Mlocek --- .github/workflows/release-dev.yml | 81 +++++++++++++++++++++++++++++- .github/workflows/snap-package.yml | 10 +++- 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index f9148a106..6679f513e 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -4,6 +4,12 @@ on: push: branches: [main] workflow_dispatch: + inputs: + package-smoke-only: + description: "Temporary PR probe: build Linux packages and run install smoke tests without publishing release artifacts" + required: false + type: boolean + default: false permissions: contents: write @@ -54,6 +60,7 @@ jobs: echo "rpm_release=$(uv run python tasks/scripts/release.py get-version --rpm-release)" >> "$GITHUB_OUTPUT" build-gateway: + if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [compute-versions] uses: ./.github/workflows/docker-build.yml with: @@ -61,6 +68,7 @@ jobs: cargo-version: ${{ needs.compute-versions.outputs.cargo_version }} build-supervisor: + if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [compute-versions] uses: ./.github/workflows/docker-build.yml with: @@ -68,6 +76,7 @@ jobs: cargo-version: ${{ needs.compute-versions.outputs.cargo_version }} e2e: + if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [build-gateway, build-supervisor] uses: ./.github/workflows/e2e-test.yml with: @@ -76,6 +85,7 @@ jobs: tag-ghcr-dev: name: Tag GHCR Images as Dev + if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [build-gateway, build-supervisor, release-dev] runs-on: linux-amd64-cpu8 timeout-minutes: 10 @@ -97,6 +107,7 @@ jobs: build-python-wheels-linux: name: Build Python Wheels (Linux ${{ matrix.arch }}) + if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [compute-versions] strategy: matrix: @@ -155,6 +166,7 @@ jobs: build-python-wheel-macos: name: Build Python Wheel (macOS) + if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [compute-versions] runs-on: linux-amd64-cpu8 timeout-minutes: 120 @@ -318,6 +330,7 @@ jobs: # --------------------------------------------------------------------------- build-cli-macos: name: Build CLI (macOS) + if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [compute-versions] runs-on: linux-amd64-cpu8 timeout-minutes: 60 @@ -478,6 +491,7 @@ jobs: # --------------------------------------------------------------------------- build-gateway-binary-macos: name: Build Gateway Binary (macOS) + if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [compute-versions] runs-on: linux-amd64-cpu8 timeout-minutes: 60 @@ -635,6 +649,7 @@ jobs: build-driver-vm-macos: name: Build Driver VM macOS + if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [compute-versions] uses: ./.github/workflows/driver-vm-macos.yml with: @@ -660,6 +675,7 @@ jobs: checkout-ref: ${{ github.sha }} upload-channel: latest/edge github-environment: latest/edge + publish: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} secrets: publish-credentials: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} @@ -676,7 +692,7 @@ jobs: smoke-linux-dev-artifacts: name: Smoke Linux Dev Artifacts (${{ matrix.name }}) - needs: [build-gateway-binary-linux, build-driver-vm-linux, build-deb] + needs: [build-gateway-binary-linux, build-driver-vm-linux, build-deb, build-rpm] timeout-minutes: 20 strategy: fail-fast: false @@ -685,22 +701,40 @@ jobs: - name: ubuntu-22.04-deb-amd64 runner: linux-amd64-cpu8 image: ubuntu:22.04 + kind: deb artifact_arch: amd64 + rpm_arch: x86_64 - name: ubuntu-22.04-deb-arm64 runner: linux-arm64-cpu8 image: ubuntu:22.04 + kind: deb artifact_arch: arm64 + rpm_arch: aarch64 + - name: fedora-rpm-amd64 + runner: linux-amd64-cpu8 + image: fedora:latest + kind: rpm + artifact_arch: amd64 + rpm_arch: x86_64 + - name: fedora-rpm-arm64 + runner: linux-arm64-cpu8 + image: fedora:latest + kind: rpm + artifact_arch: arm64 + rpm_arch: aarch64 runs-on: ${{ matrix.runner }} container: image: ${{ matrix.image }} steps: - name: Download Debian package artifact + if: matrix.kind == 'deb' uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: deb-linux-${{ matrix.artifact_arch }} path: package-input/ - name: Smoke Debian package on Ubuntu 22.04 + if: matrix.kind == 'deb' run: | set -euo pipefail apt-get update @@ -708,11 +742,55 @@ jobs: LD_BIND_NOW=1 openshell-gateway --version /usr/libexec/openshell/openshell-driver-vm --version + - name: Download RPM package artifacts + if: matrix.kind == 'rpm' + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: rpm-linux-${{ matrix.rpm_arch }} + path: package-input/ + + - name: Smoke RPM packages on Fedora + if: matrix.kind == 'rpm' + run: | + set -euo pipefail + dnf install -y ./package-input/openshell-[0-9]*.rpm ./package-input/openshell-gateway-*.rpm + LD_BIND_NOW=1 openshell-gateway --version + + smoke-snap-dev-artifact: + name: Smoke Snap Dev Artifact (amd64) + if: ${{ github.event_name == 'workflow_dispatch' && inputs['package-smoke-only'] == true }} + needs: [build-snap] + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Install snapd + run: | + set -euo pipefail + sudo apt-get update + sudo apt-get install -y snapd + sudo systemctl enable --now snapd.socket + sudo systemctl start snapd + sudo snap wait system seed.loaded + + - name: Download snap artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: snap-linux-amd64 + path: snap-input/ + + - name: Smoke snap install + run: | + set -euo pipefail + sudo snap install ./snap-input/*.snap --dangerous + openshell --version + sudo snap run openshell.gateway --version + # --------------------------------------------------------------------------- # Create / update the dev GitHub Release with CLI, gateway, driver, and wheels # --------------------------------------------------------------------------- release-dev: name: Release Dev + if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} 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, e2e, build-driver-vm-linux, build-driver-vm-macos, build-deb, build-rpm, build-snap, smoke-linux-dev-artifacts] runs-on: linux-amd64-cpu8 timeout-minutes: 10 @@ -971,6 +1049,7 @@ jobs: trigger-wheel-publish: name: Trigger Wheel Publish + if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [compute-versions, release-dev] runs-on: [self-hosted, nv] timeout-minutes: 10 diff --git a/.github/workflows/snap-package.yml b/.github/workflows/snap-package.yml index 3de044ef1..961453891 100644 --- a/.github/workflows/snap-package.yml +++ b/.github/workflows/snap-package.yml @@ -17,10 +17,15 @@ on: required: true type: string description: "GitHub deployment environment for approval gates (e.g., latest/edge, latest/stable)" + publish: + required: false + type: boolean + default: true + description: "Upload the built snap to the Snap Store" secrets: publish-credentials: - required: true + required: false description: "Snap Store credentials (SNAPCRAFT_STORE_CREDENTIALS)" permissions: @@ -42,7 +47,7 @@ jobs: runner: linux-arm64-cpu8 runs-on: ${{ matrix.runner }} timeout-minutes: 60 - environment: ${{ inputs.github-environment }} + environment: ${{ inputs.publish && inputs.github-environment || 'package-smoke' }} steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 with: @@ -167,6 +172,7 @@ jobs: retention-days: 5 - name: Upload snap to Snap Store + if: inputs.publish env: SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.publish-credentials }} run: | From 4509eb52de1e15fbffdfb4d9c445c54f9945016e Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 16 Jun 2026 16:31:16 -0700 Subject: [PATCH 06/16] fix(rpm): require z3 runtime for package checks Signed-off-by: Piotr Mlocek --- .github/workflows/rpm-package.yml | 2 +- openshell.spec | 1 + python/openshell/release_formula_test.py | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rpm-package.yml b/.github/workflows/rpm-package.yml index ce64dedb6..157ccfdf4 100644 --- a/.github/workflows/rpm-package.yml +++ b/.github/workflows/rpm-package.yml @@ -56,7 +56,7 @@ jobs: dnf install -y \ packit rpm-build \ rust cargo gcc gcc-c++ make cmake pkg-config \ - clang-devel z3-devel systemd-rpm-macros \ + clang-devel z3-libs z3-devel systemd-rpm-macros \ pandoc python3-devel git-core \ cargo-rpm-macros diff --git a/openshell.spec b/openshell.spec index b23524292..127da63da 100644 --- a/openshell.spec +++ b/openshell.spec @@ -44,6 +44,7 @@ BuildRequires: make BuildRequires: cmake BuildRequires: pkg-config BuildRequires: clang-devel +BuildRequires: z3-libs BuildRequires: z3-devel BuildRequires: systemd-rpm-macros diff --git a/python/openshell/release_formula_test.py b/python/openshell/release_formula_test.py index 8d9a420d7..5c0fb753a 100644 --- a/python/openshell/release_formula_test.py +++ b/python/openshell/release_formula_test.py @@ -118,6 +118,8 @@ def test_rpm_spec_uses_gateway_defaults_without_config_helper() -> None: repo_root = Path(__file__).resolve().parents[2] spec = (repo_root / "openshell.spec").read_text(encoding="utf-8") + assert "BuildRequires: z3-libs" in spec + assert "Requires: z3-libs" in spec assert "init-gateway-config.sh" not in spec assert "init-pki.sh" not in spec assert "Environment=OPENSHELL_LOCAL_TLS_DIR=%%h/.local/state/openshell/tls" in spec From 94cf5b6299ae8c1d2e6cca79f11089cfe18fbc4d Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 16 Jun 2026 17:17:51 -0700 Subject: [PATCH 07/16] fix(release): bundle gateway z3 runtime Signed-off-by: Piotr Mlocek --- .github/workflows/release-dev.yml | 6 +++--- .github/workflows/release-tag.yml | 7 +++---- .github/workflows/rpm-package.yml | 2 +- .github/workflows/rust-native-build.yml | 7 +++++-- architecture/build.md | 15 +++++++-------- deploy/deb/control.in | 1 - deploy/docker/Dockerfile.gateway | 13 +------------ docs/reference/support-matrix.mdx | 1 - openshell.spec | 3 --- python/openshell/release_formula_test.py | 11 ++++++++--- snapcraft.yaml | 2 -- tasks/scripts/release.py | 2 -- 12 files changed, 28 insertions(+), 42 deletions(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 6679f513e..adac77f83 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -448,7 +448,7 @@ jobs: run: | set -euo pipefail mise x -- rustup target add ${{ matrix.target }} - mise x -- cargo zigbuild --release --target ${{ matrix.zig_target }} -p openshell-server --bin openshell-gateway + mise x -- cargo zigbuild --release --target ${{ matrix.zig_target }} -p openshell-server --bin openshell-gateway --features bundled-z3 mkdir -p artifacts/bin install -m 0755 target/${{ matrix.target }}/release/openshell-gateway artifacts/bin/openshell-gateway @@ -459,8 +459,8 @@ jobs: echo "$OUTPUT" grep -q '^openshell-gateway ' <<<"$OUTPUT" ldd artifacts/bin/openshell-gateway || true - if ! ldd artifacts/bin/openshell-gateway | grep -q 'libz3'; then - echo "gateway binary must link shared libz3; do not build with bundled-z3" >&2 + if ldd artifacts/bin/openshell-gateway | grep -q 'libz3'; then + echo "gateway binary must not require shared libz3; keep z3 bundled for portable release artifacts" >&2 exit 1 fi diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index d90c482b3..fbf1d73e5 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -469,7 +469,7 @@ jobs: run: | set -euo pipefail mise x -- rustup target add ${{ matrix.target }} - mise x -- cargo zigbuild --release --target ${{ matrix.zig_target }} -p openshell-server --bin openshell-gateway + mise x -- cargo zigbuild --release --target ${{ matrix.zig_target }} -p openshell-server --bin openshell-gateway --features bundled-z3 mkdir -p artifacts/bin install -m 0755 target/${{ matrix.target }}/release/openshell-gateway artifacts/bin/openshell-gateway @@ -480,8 +480,8 @@ jobs: echo "$OUTPUT" grep -q '^openshell-gateway ' <<<"$OUTPUT" ldd artifacts/bin/openshell-gateway || true - if ! ldd artifacts/bin/openshell-gateway | grep -q 'libz3'; then - echo "gateway binary must link shared libz3; do not build with bundled-z3" >&2 + if ldd artifacts/bin/openshell-gateway | grep -q 'libz3'; then + echo "gateway binary must not require shared libz3; keep z3 bundled for portable release artifacts" >&2 exit 1 fi @@ -783,7 +783,6 @@ jobs: run: | set -euo pipefail apt-get update - apt-get install -y --no-install-recommends libz3-4 mkdir -p smoke-bin tar -xzf smoke-input/openshell-gateway-${{ matrix.target }}.tar.gz -C smoke-bin tar -xzf smoke-input/openshell-driver-vm-${{ matrix.target }}.tar.gz -C smoke-bin diff --git a/.github/workflows/rpm-package.yml b/.github/workflows/rpm-package.yml index 157ccfdf4..06cc957e0 100644 --- a/.github/workflows/rpm-package.yml +++ b/.github/workflows/rpm-package.yml @@ -56,7 +56,7 @@ jobs: dnf install -y \ packit rpm-build \ rust cargo gcc gcc-c++ make cmake pkg-config \ - clang-devel z3-libs z3-devel systemd-rpm-macros \ + clang-devel systemd-rpm-macros \ pandoc python3-devel git-core \ cargo-rpm-macros diff --git a/.github/workflows/rust-native-build.yml b/.github/workflows/rust-native-build.yml index 7aabd0232..69eda2a80 100644 --- a/.github/workflows/rust-native-build.yml +++ b/.github/workflows/rust-native-build.yml @@ -227,6 +227,9 @@ jobs: -p "${{ steps.target.outputs.crate }}" --bin "${{ steps.target.outputs.binary }}" ) + if [[ "${{ inputs.component }}" == "gateway" ]]; then + args+=(--features bundled-z3) + fi if [[ -n "$FEATURES" ]]; then args+=(--features "$FEATURES") fi @@ -246,8 +249,8 @@ jobs: ldd --version ldd "$BIN" || true if [[ "${{ inputs.component }}" == "gateway" ]]; then - if ! ldd "$BIN" | grep -q 'libz3'; then - echo "gateway binary must link shared libz3; do not enable bundled-z3 for image artifacts" >&2 + if ldd "$BIN" | grep -q 'libz3'; then + echo "gateway binary must not require shared libz3; keep z3 bundled for image artifacts" >&2 exit 1 fi fi diff --git a/architecture/build.md b/architecture/build.md index bae39a293..5fccce116 100644 --- a/architecture/build.md +++ b/architecture/build.md @@ -46,9 +46,9 @@ The standalone `openshell` CLI is built as a static musl binary so it can run on a wide range of Linux distributions without depending on the host's glibc. Host runtime binaries that use the GNU/Linux runtime environment are GNU-linked. `openshell-gateway` and `openshell-driver-vm` are built with a glibc 2.28 floor. -The gateway links against system `libz3` at runtime; Debian and RPM packages -declare that dependency, and the gateway container image copies the matching -runtime library into the distroless image. +The gateway bundles z3 into the release binary so Linux packages, standalone +tarballs, and gateway images do not depend on distro-specific z3 shared-library +SONAMEs. ## Container Builds @@ -62,9 +62,9 @@ Dockerfile compiles Rust — both copy a staged binary out of Binary staging is driven by `tasks/scripts/stage-prebuilt-binaries.sh`. Gateway binaries use `cargo zigbuild` with GNU targets pinned to glibc 2.28, including native-architecture builds, so the gateway image, standalone tarballs, and Linux -packages share the same host portability floor. The gateway build uses system Z3 -instead of the `bundled-z3` feature so cargo-zigbuild does not compile the Z3 -C/C++ sources. Linux VM driver release artifacts use the same glibc floor so +packages share the same host portability floor. The gateway build enables +`bundled-z3` and release workflows verify that no dynamic `libz3` dependency is +introduced. Linux VM driver release artifacts use the same glibc floor so package-managed VM support does not raise the package runtime requirement. Release workflows verify the maximum referenced `GLIBC_*` symbol version before publishing artifacts. @@ -83,8 +83,7 @@ Runtime layout: `/usr/local/bin/openshell-gateway`, runs as UID/GID `1000:1000`. Linux GNU gateway binaries must not reference `GLIBC_*` symbols newer than `GLIBC_2.28`; release workflows verify this before publishing artifacts. The - image includes `libz3.so.4` copied from Debian so the dynamic gateway binary - does not depend on bundled Z3. + gateway bundles z3, so the image does not need a distro-provided z3 runtime. - **VM driver**: host GNU-linked binary installed at `/usr/libexec/openshell/openshell-driver-vm` in Linux packages and published as a release artifact. Linux GNU VM driver binaries must not reference diff --git a/deploy/deb/control.in b/deploy/deb/control.in index 2578eda08..9f77f8775 100644 --- a/deploy/deb/control.in +++ b/deploy/deb/control.in @@ -5,7 +5,6 @@ Maintainer: NVIDIA OpenShell Maintainers Section: utils Priority: optional Pre-Depends: init-system-helpers (>= 1.54~) -Depends: libz3-4 Homepage: https://github.com/NVIDIA/OpenShell Description: Safe, sandboxed runtimes for autonomous AI agents OpenShell provides host-side command-line and gateway components for diff --git a/deploy/docker/Dockerfile.gateway b/deploy/docker/Dockerfile.gateway index d8d462189..3b999ead4 100644 --- a/deploy/docker/Dockerfile.gateway +++ b/deploy/docker/Dockerfile.gateway @@ -16,27 +16,16 @@ # # The runtime is distroless Debian 13, which provides glibc and the dynamic # loader needed by the GNU-linked gateway binary while keeping the attack -# surface small. libz3 is copied from a matching Debian runtime stage because -# the gateway intentionally links system Z3 instead of bundling it into the -# binary. The default digest currently carries Debian glibc 2.41-12+deb13u3. +# surface small. The default digest currently carries Debian glibc 2.41-12+deb13u3. ARG GATEWAY_BASE_IMAGE=gcr.io/distroless/cc-debian13:nonroot@sha256:e1fd250ce83d94603e9887ec991156a6c26905a6b0001039b7a43699018c0733 -FROM debian:trixie-slim AS z3-runtime - -RUN apt-get update \ - && apt-get install -y --no-install-recommends libz3-4 \ - && mkdir -p /out \ - && cp --parents /usr/lib/*-linux-gnu/libz3.so.4* /out/ \ - && rm -rf /var/lib/apt/lists/* - FROM ${GATEWAY_BASE_IMAGE} AS gateway ARG TARGETARCH WORKDIR /app -COPY --from=z3-runtime /out/ / COPY deploy/docker/.build/prebuilt-binaries/${TARGETARCH}/openshell-gateway /usr/local/bin/openshell-gateway USER 1000:1000 diff --git a/docs/reference/support-matrix.mdx b/docs/reference/support-matrix.mdx index ad34baf05..bb390b06e 100644 --- a/docs/reference/support-matrix.mdx +++ b/docs/reference/support-matrix.mdx @@ -54,7 +54,6 @@ Install the software for the compute driver you use: |---|---|---| | Docker Desktop or Docker Engine | 28.04 | Required for Docker-backed gateways, local image builds, and Docker development workflows. | | Podman | 5.x | Required for Podman-backed gateways. | -| Z3 runtime library | `libz3.so.4` | Installed by Linux gateway packages; required when running standalone GNU/Linux gateway tarballs directly. | | Kubernetes | 1.29 | Required for Helm deployments and Kubernetes sandbox scheduling. | | Helm | 3.x | Required to install `deploy/helm/openshell`. | | kubectl | Compatible with your cluster | Required for Kubernetes operational inspection and secret creation. | diff --git a/openshell.spec b/openshell.spec index 127da63da..2e1cbe10a 100644 --- a/openshell.spec +++ b/openshell.spec @@ -44,8 +44,6 @@ BuildRequires: make BuildRequires: cmake BuildRequires: pkg-config BuildRequires: clang-devel -BuildRequires: z3-libs -BuildRequires: z3-devel BuildRequires: systemd-rpm-macros # Man page generation @@ -69,7 +67,6 @@ LLM inference routing. Summary: OpenShell gateway server with Podman sandbox driver Requires: podman Requires: openssl -Requires: z3-libs Requires: %{name} = %{version}-%{release} %description gateway diff --git a/python/openshell/release_formula_test.py b/python/openshell/release_formula_test.py index 5c0fb753a..91dc48114 100644 --- a/python/openshell/release_formula_test.py +++ b/python/openshell/release_formula_test.py @@ -51,7 +51,7 @@ def test_generate_homebrew_formula_uses_tagged_macos_driver_asset_without_defaul "v0.0.10/openshell-driver-vm-aarch64-apple-darwin.tar.gz" ) in formula assert 'sha256 "' + "b" * 64 + '"' in formula - assert 'depends_on "z3"' in formula + assert 'depends_on "z3"' not in formula assert "OPENSHELL_DRIVERS: " not in formula assert 'OPENSHELL_GATEWAY_CONFIG: "#{var}/openshell/gateway.toml"' not in formula assert "init-gateway-config.sh" not in formula @@ -118,8 +118,8 @@ def test_rpm_spec_uses_gateway_defaults_without_config_helper() -> None: repo_root = Path(__file__).resolve().parents[2] spec = (repo_root / "openshell.spec").read_text(encoding="utf-8") - assert "BuildRequires: z3-libs" in spec - assert "Requires: z3-libs" in spec + assert "z3-libs" not in spec + assert "z3-devel" not in spec assert "init-gateway-config.sh" not in spec assert "init-pki.sh" not in spec assert "Environment=OPENSHELL_LOCAL_TLS_DIR=%%h/.local/state/openshell/tls" in spec @@ -142,6 +142,11 @@ def test_deb_user_service_uses_gateway_defaults_without_config_helper() -> None: unit = (repo_root / "deploy/deb/openshell-gateway.service").read_text( encoding="utf-8" ) + control = (repo_root / "deploy/deb/control.in").read_text(encoding="utf-8") + snapcraft = (repo_root / "snapcraft.yaml").read_text(encoding="utf-8") + + assert "libz3-4" not in control + assert "libz3-4" not in snapcraft assert "EnvironmentFile=-%E/openshell/gateway.env" in unit assert "Environment=OPENSHELL_LOCAL_TLS_DIR=%h/.local/state/openshell/tls" in unit diff --git a/snapcraft.yaml b/snapcraft.yaml index e03043fad..01d90366e 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -103,8 +103,6 @@ parts: openshell: plugin: nil source: ./snap/prebuilt - stage-packages: - - libz3-4 override-pull: | craftctl default craftctl set version="$(cat "$CRAFT_PART_SRC/version")" diff --git a/tasks/scripts/release.py b/tasks/scripts/release.py index 9c8e13223..eb3ce73f4 100644 --- a/tasks/scripts/release.py +++ b/tasks/scripts/release.py @@ -250,8 +250,6 @@ class Openshell < Formula depends_on macos: :big_sur depends_on arch: :arm64 - depends_on "z3" - resource "openshell-gateway" do url "{_asset_url(release_tag, HOMEBREW_GATEWAY_ASSET)}" sha256 "{gateway_sha256}" From 4710341643ad8cc8f96d621b9accb795fbf5f556 Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 16 Jun 2026 17:27:59 -0700 Subject: [PATCH 08/16] ci(release): wrap zig C++ builds for glibc target Signed-off-by: Piotr Mlocek --- .github/workflows/release-dev.yml | 3 + .github/workflows/release-tag.yml | 3 + .github/workflows/rust-native-build.yml | 4 ++ tasks/scripts/setup-zig-cc-wrapper.sh | 74 +++++++++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100755 tasks/scripts/setup-zig-cc-wrapper.sh diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index adac77f83..e18001d3c 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -442,6 +442,9 @@ jobs: set -euo pipefail sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${{ needs.compute-versions.outputs.cargo_version }}"'"/}' Cargo.toml + - name: Set up Zig C/C++ wrappers + run: tasks/scripts/setup-zig-cc-wrapper.sh ${{ matrix.zig_target }} ${{ matrix.zig_target }} /tmp/zig-gnu + - name: Build ${{ matrix.zig_target }} env: OPENSHELL_IMAGE_TAG: ${{ github.sha }} diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index fbf1d73e5..9f32e0715 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -463,6 +463,9 @@ jobs: set -euo pipefail sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${{ needs.compute-versions.outputs.cargo_version }}"'"/}' Cargo.toml + - name: Set up Zig C/C++ wrappers + run: tasks/scripts/setup-zig-cc-wrapper.sh ${{ matrix.zig_target }} ${{ matrix.zig_target }} /tmp/zig-gnu + - name: Build ${{ matrix.zig_target }} env: OPENSHELL_IMAGE_TAG: ${{ needs.compute-versions.outputs.source_sha }} diff --git a/.github/workflows/rust-native-build.yml b/.github/workflows/rust-native-build.yml index 69eda2a80..4a5aa9e42 100644 --- a/.github/workflows/rust-native-build.yml +++ b/.github/workflows/rust-native-build.yml @@ -206,6 +206,10 @@ jobs: echo "CARGO_TARGET_${TARGET_ENV_UPPER}_LINKER=/tmp/zig-musl/cc" >> "$GITHUB_ENV" echo "CARGO_TARGET_${TARGET_ENV_UPPER}_RUSTFLAGS=-Clink-self-contained=no" >> "$GITHUB_ENV" + - name: Set up zig glibc wrappers + if: inputs.component == 'gateway' + run: tasks/scripts/setup-zig-cc-wrapper.sh "${{ steps.target.outputs.zig_target }}" "${{ steps.target.outputs.zig_target }}" /tmp/zig-gnu + - name: Build ${{ steps.target.outputs.binary }} (${{ steps.target.outputs.zig_target || steps.target.outputs.target }}) env: # Preserve the release-codegen setting used by the old Dockerfile diff --git a/tasks/scripts/setup-zig-cc-wrapper.sh b/tasks/scripts/setup-zig-cc-wrapper.sh new file mode 100755 index 000000000..6d1c4e683 --- /dev/null +++ b/tasks/scripts/setup-zig-cc-wrapper.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +usage() { + echo "Usage: setup-zig-cc-wrapper.sh " >&2 +} + +if [[ $# -ne 3 ]]; then + usage + exit 2 +fi + +cargo_target=$1 +zig_target=$2 +wrapper_dir=$3 + +# cargo-zigbuild accepts Rust target triples with glibc suffixes, for example +# x86_64-unknown-linux-gnu.2.28. Zig's C/C++ driver expects the vendorless form. +zig_cc_target=${zig_target/-unknown-linux-/-linux-} + +if [[ -n ${ZIG:-} ]]; then + zig=$ZIG +elif command -v mise >/dev/null 2>&1; then + zig=$(mise which zig) +else + zig=$(command -v zig) +fi + +mkdir -p "$wrapper_dir" + +for tool in cc c++; do + cat >"$wrapper_dir/$tool" <>"$GITHUB_ENV" +else + echo "export CC_${target_env}=$wrapper_dir/cc" + echo "export CXX_${target_env}=$wrapper_dir/c++" +fi From 25a43e772933e19576db2f33b19657e2d687c130 Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 16 Jun 2026 17:45:26 -0700 Subject: [PATCH 09/16] ci(release): override cmake toolchain for z3 Signed-off-by: Piotr Mlocek --- tasks/scripts/setup-zig-cc-wrapper.sh | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tasks/scripts/setup-zig-cc-wrapper.sh b/tasks/scripts/setup-zig-cc-wrapper.sh index 6d1c4e683..74291b85f 100755 --- a/tasks/scripts/setup-zig-cc-wrapper.sh +++ b/tasks/scripts/setup-zig-cc-wrapper.sh @@ -61,14 +61,42 @@ EOF chmod +x "$wrapper_dir/$tool" done +processor=${cargo_target%%-*} +toolchain_file="$wrapper_dir/toolchain.cmake" +cat >"$toolchain_file" <>"$GITHUB_ENV" else echo "export CC_${target_env}=$wrapper_dir/cc" echo "export CXX_${target_env}=$wrapper_dir/c++" + echo "export CMAKE_TOOLCHAIN_FILE_${target_env}=$toolchain_file" + if [[ $bare_target_env != "$target_env" ]]; then + echo "export CC_${bare_target_env}=$wrapper_dir/cc" + echo "export CXX_${bare_target_env}=$wrapper_dir/c++" + echo "export CMAKE_TOOLCHAIN_FILE_${bare_target_env}=$toolchain_file" + fi fi From be44b30fe4597ebea6028b8117c40adc37478594 Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 16 Jun 2026 17:53:40 -0700 Subject: [PATCH 10/16] ci(release): clear stale z3 cmake cache Signed-off-by: Piotr Mlocek --- tasks/scripts/setup-zig-cc-wrapper.sh | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/tasks/scripts/setup-zig-cc-wrapper.sh b/tasks/scripts/setup-zig-cc-wrapper.sh index 74291b85f..991bfc41d 100755 --- a/tasks/scripts/setup-zig-cc-wrapper.sh +++ b/tasks/scripts/setup-zig-cc-wrapper.sh @@ -17,6 +17,11 @@ cargo_target=$1 zig_target=$2 wrapper_dir=$3 +bare_cargo_target=$cargo_target +if [[ $bare_cargo_target =~ ^(.+)\.[0-9]+\.[0-9]+$ ]]; then + bare_cargo_target=${BASH_REMATCH[1]} +fi + # cargo-zigbuild accepts Rust target triples with glibc suffixes, for example # x86_64-unknown-linux-gnu.2.28. Zig's C/C++ driver expects the vendorless form. zig_cc_target=${zig_target/-unknown-linux-/-linux-} @@ -71,10 +76,14 @@ set(CMAKE_CXX_COMPILER "$wrapper_dir/c++") set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) EOF -bare_cargo_target=$cargo_target -if [[ $bare_cargo_target =~ ^(.+)\.[0-9]+\.[0-9]+$ ]]; then - bare_cargo_target=${BASH_REMATCH[1]} -fi +for profile in release debug; do + z3_build_root="target/$bare_cargo_target/$profile/build" + if [[ -d $z3_build_root ]]; then + while IFS= read -r stale_build_dir; do + rm -rf "$stale_build_dir" + done < <(find "$z3_build_root" -mindepth 3 -maxdepth 3 -type d -path "*/z3-sys-*/out/build") + fi +done target_env=${cargo_target//[-.]/_} bare_target_env=${bare_cargo_target//[-.]/_} From 2b8a96021fa244d03ca03907505dfe32f232a806 Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 16 Jun 2026 18:16:29 -0700 Subject: [PATCH 11/16] ci(release): preserve valid z3 cmake cache Signed-off-by: Piotr Mlocek --- tasks/scripts/setup-zig-cc-wrapper.sh | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tasks/scripts/setup-zig-cc-wrapper.sh b/tasks/scripts/setup-zig-cc-wrapper.sh index 991bfc41d..ea78f5d28 100755 --- a/tasks/scripts/setup-zig-cc-wrapper.sh +++ b/tasks/scripts/setup-zig-cc-wrapper.sh @@ -76,11 +76,22 @@ set(CMAKE_CXX_COMPILER "$wrapper_dir/c++") set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) EOF +is_stale_z3_build_dir() { + local build_dir=$1 + + grep -R -q -E \ + 'cargo-zigbuild|zigc(c|xx)-.*unknown-linux-gnu\.[0-9]+\.[0-9]+' \ + "$build_dir/CMakeCache.txt" "$build_dir/CMakeFiles" 2>/dev/null +} + for profile in release debug; do z3_build_root="target/$bare_cargo_target/$profile/build" if [[ -d $z3_build_root ]]; then - while IFS= read -r stale_build_dir; do - rm -rf "$stale_build_dir" + while IFS= read -r z3_build_dir; do + if is_stale_z3_build_dir "$z3_build_dir"; then + echo "Removing stale z3-sys CMake cache: $z3_build_dir" >&2 + rm -rf "$z3_build_dir" + fi done < <(find "$z3_build_root" -mindepth 3 -maxdepth 3 -type d -path "*/z3-sys-*/out/build") fi done From 21abe56bed682eac7924bce8ccf19cc061f613d0 Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 16 Jun 2026 18:25:32 -0700 Subject: [PATCH 12/16] ci(release): key gateway cache on zig wrapper Signed-off-by: Piotr Mlocek --- .github/workflows/release-dev.yml | 1 + .github/workflows/release-tag.yml | 1 + .github/workflows/rust-native-build.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index e18001d3c..3e7e9570c 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -433,6 +433,7 @@ jobs: uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 with: shared-key: gateway-binary-gnu-${{ matrix.arch }} + key: zig-wrapper-${{ hashFiles('tasks/scripts/setup-zig-cc-wrapper.sh') }} cache-directories: .cache/sccache cache-targets: "true" diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 9f32e0715..7c1622745 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -454,6 +454,7 @@ jobs: uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 with: shared-key: gateway-binary-gnu-${{ matrix.arch }} + key: zig-wrapper-${{ hashFiles('tasks/scripts/setup-zig-cc-wrapper.sh') }} cache-directories: .cache/sccache cache-targets: "true" diff --git a/.github/workflows/rust-native-build.yml b/.github/workflows/rust-native-build.yml index 4a5aa9e42..01754d1d6 100644 --- a/.github/workflows/rust-native-build.yml +++ b/.github/workflows/rust-native-build.yml @@ -163,6 +163,7 @@ jobs: uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 with: shared-key: rust-native-${{ inputs.component }}-${{ inputs.arch }} + key: zig-wrapper-${{ hashFiles('tasks/scripts/setup-zig-cc-wrapper.sh') }} cache-directories: .cache/sccache cache-targets: "true" From 5d836b406624bf78dba49bbc66fdded2324a800a Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 16 Jun 2026 19:00:16 -0700 Subject: [PATCH 13/16] ci(release): include zig wrapper in shared cache key Signed-off-by: Piotr Mlocek --- .github/workflows/release-dev.yml | 3 +-- .github/workflows/release-tag.yml | 3 +-- .github/workflows/rust-native-build.yml | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 3e7e9570c..28dca79fe 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -432,8 +432,7 @@ jobs: - name: Cache Rust target and registry uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 with: - shared-key: gateway-binary-gnu-${{ matrix.arch }} - key: zig-wrapper-${{ hashFiles('tasks/scripts/setup-zig-cc-wrapper.sh') }} + shared-key: gateway-binary-gnu-${{ matrix.arch }}-zig-wrapper-${{ hashFiles('tasks/scripts/setup-zig-cc-wrapper.sh') }} cache-directories: .cache/sccache cache-targets: "true" diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 7c1622745..b62c07467 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -453,8 +453,7 @@ jobs: - name: Cache Rust target and registry uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 with: - shared-key: gateway-binary-gnu-${{ matrix.arch }} - key: zig-wrapper-${{ hashFiles('tasks/scripts/setup-zig-cc-wrapper.sh') }} + shared-key: gateway-binary-gnu-${{ matrix.arch }}-zig-wrapper-${{ hashFiles('tasks/scripts/setup-zig-cc-wrapper.sh') }} cache-directories: .cache/sccache cache-targets: "true" diff --git a/.github/workflows/rust-native-build.yml b/.github/workflows/rust-native-build.yml index 01754d1d6..6824b6222 100644 --- a/.github/workflows/rust-native-build.yml +++ b/.github/workflows/rust-native-build.yml @@ -162,8 +162,7 @@ jobs: - name: Cache Rust target and registry uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 with: - shared-key: rust-native-${{ inputs.component }}-${{ inputs.arch }} - key: zig-wrapper-${{ hashFiles('tasks/scripts/setup-zig-cc-wrapper.sh') }} + shared-key: rust-native-${{ inputs.component }}-${{ inputs.arch }}-zig-wrapper-${{ hashFiles('tasks/scripts/setup-zig-cc-wrapper.sh') }} cache-directories: .cache/sccache cache-targets: "true" From f7a9871f2da556e460e4f2b355c6e1010e049f91 Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 16 Jun 2026 21:23:21 -0700 Subject: [PATCH 14/16] ci(release): remove package smoke workflow probe Signed-off-by: Piotr Mlocek --- .github/workflows/release-dev.yml | 47 ------------------------------- 1 file changed, 47 deletions(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 28dca79fe..dfc1308da 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -4,12 +4,6 @@ on: push: branches: [main] workflow_dispatch: - inputs: - package-smoke-only: - description: "Temporary PR probe: build Linux packages and run install smoke tests without publishing release artifacts" - required: false - type: boolean - default: false permissions: contents: write @@ -60,7 +54,6 @@ jobs: echo "rpm_release=$(uv run python tasks/scripts/release.py get-version --rpm-release)" >> "$GITHUB_OUTPUT" build-gateway: - if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [compute-versions] uses: ./.github/workflows/docker-build.yml with: @@ -68,7 +61,6 @@ jobs: cargo-version: ${{ needs.compute-versions.outputs.cargo_version }} build-supervisor: - if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [compute-versions] uses: ./.github/workflows/docker-build.yml with: @@ -76,7 +68,6 @@ jobs: cargo-version: ${{ needs.compute-versions.outputs.cargo_version }} e2e: - if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [build-gateway, build-supervisor] uses: ./.github/workflows/e2e-test.yml with: @@ -85,7 +76,6 @@ jobs: tag-ghcr-dev: name: Tag GHCR Images as Dev - if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [build-gateway, build-supervisor, release-dev] runs-on: linux-amd64-cpu8 timeout-minutes: 10 @@ -107,7 +97,6 @@ jobs: build-python-wheels-linux: name: Build Python Wheels (Linux ${{ matrix.arch }}) - if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [compute-versions] strategy: matrix: @@ -166,7 +155,6 @@ jobs: build-python-wheel-macos: name: Build Python Wheel (macOS) - if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [compute-versions] runs-on: linux-amd64-cpu8 timeout-minutes: 120 @@ -330,7 +318,6 @@ jobs: # --------------------------------------------------------------------------- build-cli-macos: name: Build CLI (macOS) - if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [compute-versions] runs-on: linux-amd64-cpu8 timeout-minutes: 60 @@ -494,7 +481,6 @@ jobs: # --------------------------------------------------------------------------- build-gateway-binary-macos: name: Build Gateway Binary (macOS) - if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [compute-versions] runs-on: linux-amd64-cpu8 timeout-minutes: 60 @@ -652,7 +638,6 @@ jobs: build-driver-vm-macos: name: Build Driver VM macOS - if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [compute-versions] uses: ./.github/workflows/driver-vm-macos.yml with: @@ -678,7 +663,6 @@ jobs: checkout-ref: ${{ github.sha }} upload-channel: latest/edge github-environment: latest/edge - publish: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} secrets: publish-credentials: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} @@ -759,41 +743,11 @@ jobs: dnf install -y ./package-input/openshell-[0-9]*.rpm ./package-input/openshell-gateway-*.rpm LD_BIND_NOW=1 openshell-gateway --version - smoke-snap-dev-artifact: - name: Smoke Snap Dev Artifact (amd64) - if: ${{ github.event_name == 'workflow_dispatch' && inputs['package-smoke-only'] == true }} - needs: [build-snap] - runs-on: ubuntu-latest - timeout-minutes: 20 - steps: - - name: Install snapd - run: | - set -euo pipefail - sudo apt-get update - sudo apt-get install -y snapd - sudo systemctl enable --now snapd.socket - sudo systemctl start snapd - sudo snap wait system seed.loaded - - - name: Download snap artifact - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: snap-linux-amd64 - path: snap-input/ - - - name: Smoke snap install - run: | - set -euo pipefail - sudo snap install ./snap-input/*.snap --dangerous - openshell --version - sudo snap run openshell.gateway --version - # --------------------------------------------------------------------------- # Create / update the dev GitHub Release with CLI, gateway, driver, and wheels # --------------------------------------------------------------------------- release-dev: name: Release Dev - if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} 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, e2e, build-driver-vm-linux, build-driver-vm-macos, build-deb, build-rpm, build-snap, smoke-linux-dev-artifacts] runs-on: linux-amd64-cpu8 timeout-minutes: 10 @@ -1052,7 +1006,6 @@ jobs: trigger-wheel-publish: name: Trigger Wheel Publish - if: ${{ github.event_name != 'workflow_dispatch' || inputs['package-smoke-only'] != true }} needs: [compute-versions, release-dev] runs-on: [self-hosted, nv] timeout-minutes: 10 From 09e66a907d95906585329281ee4b57ff9963382f Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Wed, 17 Jun 2026 10:16:44 -0700 Subject: [PATCH 15/16] clean up unnecessary changes --- .github/workflows/release-dev.yml | 2 +- .github/workflows/rpm-package.yml | 2 +- .github/workflows/rust-native-build.yml | 12 ++++-------- .github/workflows/snap-package.yml | 10 ++-------- architecture/build.md | 3 +-- deploy/docker/Dockerfile.gateway | 3 ++- docs/about/installation.mdx | 4 ++-- openshell.spec | 1 + python/openshell/release_formula_test.py | 8 -------- tasks/scripts/release.py | 1 + tasks/scripts/stage-prebuilt-binaries.sh | 3 +++ 11 files changed, 18 insertions(+), 31 deletions(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index dfc1308da..0fcc5a966 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -450,7 +450,7 @@ jobs: grep -q '^openshell-gateway ' <<<"$OUTPUT" ldd artifacts/bin/openshell-gateway || true if ldd artifacts/bin/openshell-gateway | grep -q 'libz3'; then - echo "gateway binary must not require shared libz3; keep z3 bundled for portable release artifacts" >&2 + echo "gateway binary must not depend on shared libz3; build with bundled-z3" >&2 exit 1 fi diff --git a/.github/workflows/rpm-package.yml b/.github/workflows/rpm-package.yml index 06cc957e0..ce64dedb6 100644 --- a/.github/workflows/rpm-package.yml +++ b/.github/workflows/rpm-package.yml @@ -56,7 +56,7 @@ jobs: dnf install -y \ packit rpm-build \ rust cargo gcc gcc-c++ make cmake pkg-config \ - clang-devel systemd-rpm-macros \ + clang-devel z3-devel systemd-rpm-macros \ pandoc python3-devel git-core \ cargo-rpm-macros diff --git a/.github/workflows/rust-native-build.yml b/.github/workflows/rust-native-build.yml index 6824b6222..b85afa4e6 100644 --- a/.github/workflows/rust-native-build.yml +++ b/.github/workflows/rust-native-build.yml @@ -224,6 +224,7 @@ jobs: if [[ "${{ inputs.component }}" == "gateway" ]]; then cargo_cmd=(cargo zigbuild) build_target="${{ steps.target.outputs.zig_target }}" + args+=(--features bundled-z3) fi args=( --release @@ -231,9 +232,6 @@ jobs: -p "${{ steps.target.outputs.crate }}" --bin "${{ steps.target.outputs.binary }}" ) - if [[ "${{ inputs.component }}" == "gateway" ]]; then - args+=(--features bundled-z3) - fi if [[ -n "$FEATURES" ]]; then args+=(--features "$FEATURES") fi @@ -252,11 +250,9 @@ jobs: # Record linkage so image runtime drift is visible in logs. ldd --version ldd "$BIN" || true - if [[ "${{ inputs.component }}" == "gateway" ]]; then - if ldd "$BIN" | grep -q 'libz3'; then - echo "gateway binary must not require shared libz3; keep z3 bundled for image artifacts" >&2 - exit 1 - fi + if [[ "${{ inputs.component }}" == "gateway" ]] && ldd "$BIN" | grep -q 'libz3'; then + echo "gateway binary must not depend on shared libz3; enable bundled-z3 for image artifacts" >&2 + exit 1 fi - name: Verify glibc symbol floor diff --git a/.github/workflows/snap-package.yml b/.github/workflows/snap-package.yml index 961453891..3de044ef1 100644 --- a/.github/workflows/snap-package.yml +++ b/.github/workflows/snap-package.yml @@ -17,15 +17,10 @@ on: required: true type: string description: "GitHub deployment environment for approval gates (e.g., latest/edge, latest/stable)" - publish: - required: false - type: boolean - default: true - description: "Upload the built snap to the Snap Store" secrets: publish-credentials: - required: false + required: true description: "Snap Store credentials (SNAPCRAFT_STORE_CREDENTIALS)" permissions: @@ -47,7 +42,7 @@ jobs: runner: linux-arm64-cpu8 runs-on: ${{ matrix.runner }} timeout-minutes: 60 - environment: ${{ inputs.publish && inputs.github-environment || 'package-smoke' }} + environment: ${{ inputs.github-environment }} steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 with: @@ -172,7 +167,6 @@ jobs: retention-days: 5 - name: Upload snap to Snap Store - if: inputs.publish env: SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.publish-credentials }} run: | diff --git a/architecture/build.md b/architecture/build.md index 5fccce116..50bd1922d 100644 --- a/architecture/build.md +++ b/architecture/build.md @@ -63,8 +63,7 @@ Binary staging is driven by `tasks/scripts/stage-prebuilt-binaries.sh`. Gateway binaries use `cargo zigbuild` with GNU targets pinned to glibc 2.28, including native-architecture builds, so the gateway image, standalone tarballs, and Linux packages share the same host portability floor. The gateway build enables -`bundled-z3` and release workflows verify that no dynamic `libz3` dependency is -introduced. Linux VM driver release artifacts use the same glibc floor so +`bundled-z3`. Linux VM driver release artifacts use the same glibc floor so package-managed VM support does not raise the package runtime requirement. Release workflows verify the maximum referenced `GLIBC_*` symbol version before publishing artifacts. diff --git a/deploy/docker/Dockerfile.gateway b/deploy/docker/Dockerfile.gateway index 3b999ead4..9dd7ed8b9 100644 --- a/deploy/docker/Dockerfile.gateway +++ b/deploy/docker/Dockerfile.gateway @@ -16,7 +16,8 @@ # # The runtime is distroless Debian 13, which provides glibc and the dynamic # loader needed by the GNU-linked gateway binary while keeping the attack -# surface small. The default digest currently carries Debian glibc 2.41-12+deb13u3. +# surface small. The default digest currently carries Debian glibc +# 2.41-12+deb13u3. ARG GATEWAY_BASE_IMAGE=gcr.io/distroless/cc-debian13:nonroot@sha256:e1fd250ce83d94603e9887ec991156a6c26905a6b0001039b7a43699018c0733 diff --git a/docs/about/installation.mdx b/docs/about/installation.mdx index 4f8400624..256015947 100644 --- a/docs/about/installation.mdx +++ b/docs/about/installation.mdx @@ -51,9 +51,9 @@ brew services restart openshell ## Linux -On Fedora and RHEL, the install script uses RPM packages. The RPM installs the `openshell` CLI, the `openshell-gateway` daemon, the Z3 runtime library dependency, and a systemd user service. +On Fedora and RHEL, the install script uses RPM packages. The RPM installs the `openshell` CLI, the `openshell-gateway` daemon, and a systemd user service. -On Debian and Ubuntu, the install script uses a Debian package. The Debian package installs the `openshell` CLI, the `openshell-gateway` daemon, VM sandbox support, the Z3 runtime library dependency, and a systemd user service. +On Debian and Ubuntu, the install script uses a Debian package. The Debian package installs the `openshell` CLI, the `openshell-gateway` daemon, VM sandbox support, and a systemd user service. Linux packages require glibc 2.28 or newer. The installer checks libc before downloading packages and exits with an error on older glibc versions, Alpine, musl-based distributions, or unknown libc environments. diff --git a/openshell.spec b/openshell.spec index 2e1cbe10a..3200659d7 100644 --- a/openshell.spec +++ b/openshell.spec @@ -44,6 +44,7 @@ BuildRequires: make BuildRequires: cmake BuildRequires: pkg-config BuildRequires: clang-devel +BuildRequires: z3-devel BuildRequires: systemd-rpm-macros # Man page generation diff --git a/python/openshell/release_formula_test.py b/python/openshell/release_formula_test.py index 91dc48114..b3ab871ae 100644 --- a/python/openshell/release_formula_test.py +++ b/python/openshell/release_formula_test.py @@ -51,7 +51,6 @@ def test_generate_homebrew_formula_uses_tagged_macos_driver_asset_without_defaul "v0.0.10/openshell-driver-vm-aarch64-apple-darwin.tar.gz" ) in formula assert 'sha256 "' + "b" * 64 + '"' in formula - assert 'depends_on "z3"' not in formula assert "OPENSHELL_DRIVERS: " not in formula assert 'OPENSHELL_GATEWAY_CONFIG: "#{var}/openshell/gateway.toml"' not in formula assert "init-gateway-config.sh" not in formula @@ -118,8 +117,6 @@ def test_rpm_spec_uses_gateway_defaults_without_config_helper() -> None: repo_root = Path(__file__).resolve().parents[2] spec = (repo_root / "openshell.spec").read_text(encoding="utf-8") - assert "z3-libs" not in spec - assert "z3-devel" not in spec assert "init-gateway-config.sh" not in spec assert "init-pki.sh" not in spec assert "Environment=OPENSHELL_LOCAL_TLS_DIR=%%h/.local/state/openshell/tls" in spec @@ -142,11 +139,6 @@ def test_deb_user_service_uses_gateway_defaults_without_config_helper() -> None: unit = (repo_root / "deploy/deb/openshell-gateway.service").read_text( encoding="utf-8" ) - control = (repo_root / "deploy/deb/control.in").read_text(encoding="utf-8") - snapcraft = (repo_root / "snapcraft.yaml").read_text(encoding="utf-8") - - assert "libz3-4" not in control - assert "libz3-4" not in snapcraft assert "EnvironmentFile=-%E/openshell/gateway.env" in unit assert "Environment=OPENSHELL_LOCAL_TLS_DIR=%h/.local/state/openshell/tls" in unit diff --git a/tasks/scripts/release.py b/tasks/scripts/release.py index eb3ce73f4..f00bd19d3 100644 --- a/tasks/scripts/release.py +++ b/tasks/scripts/release.py @@ -250,6 +250,7 @@ class Openshell < Formula depends_on macos: :big_sur depends_on arch: :arm64 + resource "openshell-gateway" do url "{_asset_url(release_tag, HOMEBREW_GATEWAY_ASSET)}" sha256 "{gateway_sha256}" diff --git a/tasks/scripts/stage-prebuilt-binaries.sh b/tasks/scripts/stage-prebuilt-binaries.sh index ecd560dc0..18274a0a2 100755 --- a/tasks/scripts/stage-prebuilt-binaries.sh +++ b/tasks/scripts/stage-prebuilt-binaries.sh @@ -153,6 +153,9 @@ build_component_for_arch() { target="$(target_triple "$arch" "$target_libc")" stage="${ROOT}/deploy/docker/.build/prebuilt-binaries/${arch}" features="${EXTRA_CARGO_FEATURES:-openshell-core/dev-settings}" + if [[ "$component" == "gateway" && " ${features} " != *" bundled-z3 "* ]]; then + features="${features} bundled-z3" + fi current_host_os="$(host_os)" current_host_arch="$(host_arch)" From 1365c2f976e5012e076a5d35ac08adf8e6eeccf8 Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Wed, 17 Jun 2026 10:37:23 -0700 Subject: [PATCH 16/16] fix bundled-z3 not making into args --- .github/workflows/rust-native-build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust-native-build.yml b/.github/workflows/rust-native-build.yml index b85afa4e6..6a0454431 100644 --- a/.github/workflows/rust-native-build.yml +++ b/.github/workflows/rust-native-build.yml @@ -219,14 +219,17 @@ jobs: run: | set -euo pipefail mise x -- rustup target add "${{ steps.target.outputs.target }}" + cargo_cmd=(cargo build) build_target="${{ steps.target.outputs.target }}" + args=() + if [[ "${{ inputs.component }}" == "gateway" ]]; then cargo_cmd=(cargo zigbuild) build_target="${{ steps.target.outputs.zig_target }}" args+=(--features bundled-z3) fi - args=( + args+=( --release --target "$build_target" -p "${{ steps.target.outputs.crate }}"