diff --git a/deploy/common/init-gateway-config.sh b/deploy/common/init-gateway-config.sh new file mode 100755 index 000000000..957b2e93c --- /dev/null +++ b/deploy/common/init-gateway-config.sh @@ -0,0 +1,139 @@ +#!/bin/sh +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -eu + +usage() { + echo "Usage: init-gateway-config.sh [package args...]" >&2 + exit 2 +} + +profile="${1:-}" +CONFIG_FILE="${2:-}" +if [ -z "$profile" ] || [ -z "$CONFIG_FILE" ]; then + usage +fi + +if [ -f "$CONFIG_FILE" ]; then + exit 0 +fi + +toml_escape() { + printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g' +} + +toml_string() { + printf '"%s"' "$(toml_escape "$1")" +} + +emit_string_field() { + key="$1" + value="$2" + if [ -n "$value" ]; then + printf '%s = %s\n' "$key" "$(toml_string "$value")" + fi +} + +write_desktop_config() { + pki_dir="${1:-}" + driver_dir="${2:-}" + vm_state_dir="${3:-}" + docker_supervisor_image="${4:-}" + docker_tls_dir="${5:-}" + if [ -z "$pki_dir" ] || [ -z "$driver_dir" ] || [ -z "$vm_state_dir" ]; then + usage + fi + + mkdir -p "$(dirname "$CONFIG_FILE")" "$vm_state_dir" + + tmp="${CONFIG_FILE}.tmp" + { + cat < "$tmp" + + chmod 600 "$tmp" + mv "$tmp" "$CONFIG_FILE" +} + +write_snap_config() { + supervisor_bin="${1:-}" + if [ -z "$supervisor_bin" ]; then + usage + fi + + mkdir -p "$(dirname "$CONFIG_FILE")" + + tmp="${CONFIG_FILE}.tmp" + { + cat < "$tmp" + + chmod 600 "$tmp" + mv "$tmp" "$CONFIG_FILE" +} + +case "$profile" in + deb) + write_desktop_config "${3:-}" "${4:-}" "${5:-}" "" "" + ;; + homebrew) + write_desktop_config "${3:-}" "${4:-}" "${5:-}" "${6:-}" "${7:-}" + ;; + snap) + write_snap_config "${3:-}" + ;; + *) + usage + ;; +esac diff --git a/deploy/deb/init-gateway-config.sh b/deploy/deb/init-gateway-config.sh deleted file mode 100755 index 55b07f7e5..000000000 --- a/deploy/deb/init-gateway-config.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/sh -# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -set -eu - -CONFIG_FILE="${1:?Usage: init-gateway-config.sh }" -PKI_DIR="${2:?Usage: init-gateway-config.sh }" -DRIVER_DIR="${3:?Usage: init-gateway-config.sh }" -VM_STATE_DIR="${4:?Usage: init-gateway-config.sh }" - -if [ -f "$CONFIG_FILE" ]; then - exit 0 -fi - -mkdir -p "$(dirname "$CONFIG_FILE")" "$VM_STATE_DIR" - -port="${OPENSHELL_SERVER_PORT:-17670}" -scheme="https" -if [ "${OPENSHELL_DISABLE_TLS:-false}" = "true" ]; then - scheme="http" -fi - -tmp="${CONFIG_FILE}.tmp" -{ - cat < "$tmp" - -chmod 600 "$tmp" -mv "$tmp" "$CONFIG_FILE" diff --git a/deploy/deb/openshell-gateway.service b/deploy/deb/openshell-gateway.service index 1b57f3e48..98043910d 100644 --- a/deploy/deb/openshell-gateway.service +++ b/deploy/deb/openshell-gateway.service @@ -6,18 +6,13 @@ After=default.target [Service] Type=simple StateDirectory=openshell/gateway -# %S resolves to $XDG_STATE_HOME for user services. -Environment=OPENSHELL_BIND_ADDRESS=127.0.0.1 -Environment=OPENSHELL_SERVER_PORT=17670 -Environment=OPENSHELL_TLS_CERT=%S/openshell/tls/server/tls.crt -Environment=OPENSHELL_TLS_KEY=%S/openshell/tls/server/tls.key -Environment=OPENSHELL_TLS_CLIENT_CA=%S/openshell/tls/ca.crt -Environment=OPENSHELL_DB_URL=sqlite:%S/openshell/gateway/openshell.db -Environment=OPENSHELL_GATEWAY_CONFIG=%S/openshell/gateway/config.toml +# Legacy OPENSHELL_* overrides are still honored, but packaged defaults live +# in %S/openshell/gateway/config.toml. %S resolves to $XDG_STATE_HOME for user +# services. EnvironmentFile=-%h/.config/openshell/gateway.env ExecStartPre=/usr/bin/openshell-gateway generate-certs --output-dir %S/openshell/tls --server-san host.openshell.internal -ExecStartPre=/usr/libexec/openshell/init-gateway-config.sh %S/openshell/gateway/config.toml %S/openshell/tls /usr/libexec/openshell %S/openshell/vm-driver -ExecStart=/usr/bin/openshell-gateway +ExecStartPre=/usr/libexec/openshell/init-gateway-config.sh deb %S/openshell/gateway/config.toml %S/openshell/tls /usr/libexec/openshell %S/openshell/vm-driver +ExecStart=/bin/sh -c 'exec /usr/bin/openshell-gateway --config "$${OPENSHELL_GATEWAY_CONFIG:-%S/openshell/gateway/config.toml}" --db-url "$${OPENSHELL_DB_URL:-sqlite:%S/openshell/gateway/openshell.db}"' Restart=on-failure RestartSec=5s PrivateTmp=true diff --git a/deploy/man/openshell-gateway.8.md b/deploy/man/openshell-gateway.8.md index ee2ad8ed2..ab2fae602 100644 --- a/deploy/man/openshell-gateway.8.md +++ b/deploy/man/openshell-gateway.8.md @@ -22,12 +22,13 @@ network and filesystem policies to sandboxes, routes inference requests, and provides the SSH tunnel endpoint for CLI-to-sandbox connections. -When installed via RPM, the gateway runs as a systemd user service -with the Podman compute driver. Sandboxes are rootless Podman -containers on the host. +When installed via a Linux package, the gateway runs as a systemd user +service. The packaged service creates a gateway TOML file on first +start and launches the gateway with **--config**. -The gateway exposes a single port (default 8080) with multiplexed -gRPC and HTTP, secured by mutual TLS (mTLS) by default. +The gateway exposes a single port with multiplexed gRPC and HTTP, +secured by mutual TLS (mTLS) by default unless the TOML config disables +TLS. # OPTIONS @@ -100,7 +101,7 @@ configured in the TOML file passed with **--config**. # SYSTEMD INTEGRATION -The RPM installs a systemd user unit at +The package installs a systemd user unit at */usr/lib/systemd/user/openshell-gateway.service*. Manage the gateway with standard systemd commands: @@ -114,13 +115,13 @@ View logs: journalctl --user -u openshell-gateway journalctl --user -u openshell-gateway -f -The unit runs two **ExecStartPre** scripts on first start: +The unit runs two **ExecStartPre** steps on first start: -1. **init-pki.sh** generates a self-signed PKI bundle for mTLS. -2. **init-gateway-env.sh** generates the environment configuration - file. +1. **openshell-gateway generate-certs** generates a self-signed PKI + bundle for mTLS. +2. **init-gateway-config.sh** generates the gateway TOML file. -Both scripts are idempotent and skip generation if their output files +Both steps are idempotent and skip generation if their output files already exist. To persist the service across logouts: @@ -129,11 +130,20 @@ To persist the service across logouts: # CONFIGURATION -The systemd user unit reads configuration from -*~/.config/openshell/gateway.env*. See **openshell-gateway.env**(5) -for the full variable reference. +The systemd user unit launches the gateway with: -To override individual settings without modifying gateway.env: + openshell-gateway --config ~/.local/state/openshell/gateway/config.toml \ + --db-url sqlite:~/.local/state/openshell/gateway/openshell.db + +Gateway listener, TLS, and compute driver settings live in +*~/.local/state/openshell/gateway/config.toml*. The database URL stays +on **--db-url** because the gateway rejects `database_url` in TOML. + +For compatibility, the unit also reads optional environment overrides +from *~/.config/openshell/gateway.env*. Gateway environment variables +in that file continue to override TOML values. + +To override individual settings without modifying the generated TOML: systemctl --user edit openshell-gateway @@ -147,19 +157,19 @@ This creates a drop-in override that persists across package upgrades. */usr/lib/systemd/user/openshell-gateway.service* : Systemd user unit file. -*/usr/libexec/openshell/init-pki.sh* -: PKI bootstrap script. - -*/usr/libexec/openshell/init-gateway-env.sh* -: Gateway environment file generator. +*/usr/libexec/openshell/init-gateway-config.sh* +: Gateway TOML file generator. *~/.config/openshell/gateway.env* -: Gateway environment configuration (generated on first start). +: Optional legacy environment overrides. + +*~/.local/state/openshell/gateway/config.toml* +: Gateway TOML configuration (generated on first start). *~/.local/state/openshell/tls/* : Auto-generated TLS certificates. -*~/.local/state/openshell/gateway.db* +*~/.local/state/openshell/gateway/openshell.db* : SQLite database for gateway state. *~/.config/openshell/gateways/openshell/mtls/* @@ -176,11 +186,10 @@ Check gateway health from the CLI: openshell gateway add --local https://127.0.0.1:8080 openshell status -Override the API port via a systemd drop-in: +Override the API port in the generated TOML: - systemctl --user edit openshell-gateway - # Add: [Service] - # Add: Environment=OPENSHELL_SERVER_PORT=9090 + $EDITOR ~/.local/state/openshell/gateway/config.toml + systemctl --user restart openshell-gateway # SEE ALSO diff --git a/deploy/man/openshell-gateway.env.5.md b/deploy/man/openshell-gateway.env.5.md index ec3f466a1..3ceb3905d 100644 --- a/deploy/man/openshell-gateway.env.5.md +++ b/deploy/man/openshell-gateway.env.5.md @@ -8,15 +8,15 @@ date: 2025 # NAME -openshell-gateway.env - OpenShell gateway environment configuration +openshell-gateway.env - legacy OpenShell gateway environment overrides # DESCRIPTION -The **openshell-gateway.env** file contains environment variables that -configure the OpenShell gateway server when running as a systemd user -service. It is generated automatically on first start by -**init-gateway-env.sh** and is not overwritten on subsequent starts or -package upgrades. +The **openshell-gateway.env** file contains optional environment +overrides for the OpenShell gateway server when running as a systemd +user service. Packaged defaults live in the gateway TOML file; this +environment file is kept for compatibility and for settings that must +remain outside TOML, such as **OPENSHELL_DB_URL**. The file uses the standard systemd **EnvironmentFile** format: one **KEY=VALUE** pair per line. Lines beginning with **#** are comments. @@ -33,62 +33,64 @@ The systemd user unit reads it via: EnvironmentFile=-~/.config/openshell/gateway.env The **-** prefix means the service starts normally if the file does not -exist (the unit has built-in defaults for all required settings). +exist. # VARIABLES ## Gateway -**OPENSHELL_BIND_ADDRESS** (default: 0.0.0.0) -: IP address to bind all listeners to. The RPM default of **0.0.0.0** - exposes the gateway on all network interfaces; mTLS must remain - enabled to prevent unauthenticated access. Set to **127.0.0.1** for - local-only access. +**OPENSHELL_BIND_ADDRESS** +: IP address to bind all listeners to. Overrides + `bind_address` in the TOML file. -**OPENSHELL_SERVER_PORT** (default: 8080) -: Port for the multiplexed gRPC/HTTP API. +**OPENSHELL_SERVER_PORT** +: Port for the multiplexed gRPC/HTTP API. Overrides the port in the + TOML `bind_address`. -**OPENSHELL_HEALTH_PORT** (default: 0) +**OPENSHELL_HEALTH_PORT** : Port for unauthenticated health endpoints (/healthz, /readyz). Set to a non-zero value to enable a dedicated health listener. -**OPENSHELL_METRICS_PORT** (default: 0) +**OPENSHELL_METRICS_PORT** : Port for Prometheus metrics endpoint (/metrics). Set to a non-zero value to enable a dedicated metrics listener. -**OPENSHELL_LOG_LEVEL** (default: info) +**OPENSHELL_LOG_LEVEL** : Log verbosity: **trace**, **debug**, **info**, **warn**, **error**. -**OPENSHELL_DRIVERS** (default: podman) +**OPENSHELL_DRIVERS** : Compute driver for sandbox management. Options: **podman**, - **docker**, **kubernetes**. The RPM unit defaults to **podman**. + **docker**, **kubernetes**, **vm**. Overrides `compute_drivers` in + the TOML file. -**OPENSHELL_DB_URL** (default: sqlite://$XDG_STATE_HOME/openshell/gateway.db) -: SQLite database URL for gateway state persistence. +**OPENSHELL_DB_URL** +: SQLite database URL for gateway state persistence. The gateway + rejects `database_url` in TOML, so packages pass this value through + **--db-url** when it is set. ## TLS -**OPENSHELL_TLS_CERT** (default: auto-generated path) +**OPENSHELL_TLS_CERT** : Path to server TLS certificate. -**OPENSHELL_TLS_KEY** (default: auto-generated path) +**OPENSHELL_TLS_KEY** : Path to server TLS private key. -**OPENSHELL_TLS_CLIENT_CA** (default: auto-generated path) +**OPENSHELL_TLS_CLIENT_CA** : Path to CA certificate for client certificate verification. When set without **OPENSHELL_OIDC_ISSUER**, mTLS is required. When both are set, callers may authenticate via Bearer token or client certificate. -**OPENSHELL_DISABLE_TLS** (default: unset) +**OPENSHELL_DISABLE_TLS** : Set to **true** to disable TLS entirely and listen on plaintext - HTTP. Not recommended for production. When the bind address is - **0.0.0.0** (the RPM default), disabling TLS exposes the API to the - entire network without authentication. Restrict + HTTP. Not recommended for production. When the bind address is a + public interface, disabling TLS exposes the API to the network + without authentication. Restrict **OPENSHELL_BIND_ADDRESS** to **127.0.0.1** or place the gateway behind a TLS-terminating reverse proxy. -**OPENSHELL_SERVER_SAN** (default: unset) +**OPENSHELL_SERVER_SAN** : Comma-separated SANs configured on the gateway server certificate. Wildcard DNS SANs also enable sandbox service URLs under that domain. diff --git a/deploy/snap/README.md b/deploy/snap/README.md index ece73f680..944168190 100644 --- a/deploy/snap/README.md +++ b/deploy/snap/README.md @@ -88,7 +88,7 @@ The snap exposes the CLI: - `openshell` -It also defines a system service running the gateway with the Docker driver. +It also defines a system service with packaged Docker driver settings. - `openshell.gateway` @@ -98,9 +98,9 @@ to move the gateway to the refreshed snap revision. `openshell-sandbox` is staged next to `openshell-gateway` as the Docker supervisor binary. The gateway app starts through a small wrapper that writes -`$SNAP_COMMON/gateway.toml` on first start and points the in-process Docker -driver at `$SNAP/bin/openshell-sandbox`. The service stores its gateway -database under `$SNAP_COMMON`. +`$SNAP_COMMON/gateway.toml` on first start and records Docker driver settings, +including `$SNAP/bin/openshell-sandbox` as the supervisor binary. The service +stores its gateway database under `$SNAP_COMMON`. ## Interfaces @@ -140,19 +140,17 @@ override is required. The OpenShell snap still requires the Docker snap because it relies on the `docker:docker-daemon` slot; it does not work with Docker installed from a Debian package or Docker's upstream packages. -The service runs the gateway with the Docker driver enabled: +The service runs the gateway with an explicit config file and database URL: ```shell openshell.gateway \ - --drivers docker \ - --disable-tls \ - --port 17670 \ - --db-url "sqlite:$SNAP_COMMON/gateway.db?mode=rwc" \ - --config "$SNAP_COMMON/gateway.toml" + --config "$SNAP_COMMON/gateway.toml" \ + --db-url "sqlite:$SNAP_COMMON/gateway.db?mode=rwc" ``` This stores the gateway SQLite database at `/var/snap/openshell/common/gateway.db`. The generated TOML stores Docker +gateway settings such as the bind address and plaintext TLS mode, plus Docker driver settings such as the supervisor binary path, network name, sandbox namespace, sandbox image, pull policy, and callback endpoint. diff --git a/deploy/snap/bin/openshell-gateway-wrapper b/deploy/snap/bin/openshell-gateway-wrapper index 19e24b52b..e10e4a49a 100755 --- a/deploy/snap/bin/openshell-gateway-wrapper +++ b/deploy/snap/bin/openshell-gateway-wrapper @@ -4,24 +4,13 @@ set -eu -CONFIG_FILE="${OPENSHELL_GATEWAY_CONFIG:-${SNAP_COMMON}/gateway.toml}" +CANONICAL_CONFIG_FILE="${SNAP_COMMON}/gateway.toml" +CONFIG_FILE="${OPENSHELL_GATEWAY_CONFIG:-$CANONICAL_CONFIG_FILE}" +DB_URL="${OPENSHELL_DB_URL:-sqlite:${SNAP_COMMON}/gateway.db?mode=rwc}" -if [ ! -f "$CONFIG_FILE" ]; then - mkdir -p "$(dirname "$CONFIG_FILE")" - cat > "$CONFIG_FILE" << EOF -[openshell] -version = 1 - -[openshell.drivers.docker] -default_image = "ghcr.io/nvidia/openshell-community/sandboxes/base:latest" -image_pull_policy = "IfNotPresent" -sandbox_namespace = "docker-snap" -grpc_endpoint = "http://host.openshell.internal:17670" -supervisor_bin = "${SNAP}/bin/openshell-sandbox" -network_name = "openshell-snap" -EOF - chmod 600 "$CONFIG_FILE" +if [ ! -f "$CANONICAL_CONFIG_FILE" ]; then + "${SNAP}/bin/init-gateway-config.sh" snap "$CANONICAL_CONFIG_FILE" "${SNAP}/bin/openshell-sandbox" fi export OPENSHELL_GATEWAY_CONFIG="$CONFIG_FILE" -exec "${SNAP}/bin/openshell-gateway" "$@" +exec "${SNAP}/bin/openshell-gateway" --config "$CONFIG_FILE" --db-url "$DB_URL" "$@" diff --git a/deploy/snap/meta/snap.yaml.in b/deploy/snap/meta/snap.yaml.in index 4175da0ac..920dd9141 100644 --- a/deploy/snap/meta/snap.yaml.in +++ b/deploy/snap/meta/snap.yaml.in @@ -35,12 +35,6 @@ apps: daemon: simple refresh-mode: endure environment: - OPENSHELL_BIND_ADDRESS: 127.0.0.1 - OPENSHELL_SERVER_PORT: 17670 - OPENSHELL_DB_URL: "sqlite:$SNAP_COMMON/gateway.db?mode=rwc" - OPENSHELL_DISABLE_TLS: true - OPENSHELL_DRIVERS: docker - OPENSHELL_GATEWAY_CONFIG: "$SNAP_COMMON/gateway.toml" XDG_DATA_HOME: "$SNAP_COMMON" # Used for creating and locating certain sockets. XDG_RUNTIME_DIR: "$SNAP_COMMON" diff --git a/docs/reference/sandbox-compute-drivers.mdx b/docs/reference/sandbox-compute-drivers.mdx index 9055799c3..cf4ecc482 100644 --- a/docs/reference/sandbox-compute-drivers.mdx +++ b/docs/reference/sandbox-compute-drivers.mdx @@ -14,21 +14,22 @@ Every compute driver runs the OpenShell supervisor inside the sandbox workload. ## Configure a Compute Driver -Configure the compute driver on the gateway. Current releases accept one driver per gateway: +Configure the compute driver on the gateway. Current releases accept one driver per gateway. Set `compute_drivers` in the gateway TOML file: -```shell -openshell-gateway --drivers docker +```toml +[openshell.gateway] +compute_drivers = ["docker"] ``` -You can also set the driver with `OPENSHELL_DRIVERS`. Supported values are `docker`, `podman`, `kubernetes`, and `vm`. +Supported values are `docker`, `podman`, `kubernetes`, and `vm`. -When `--drivers` and `OPENSHELL_DRIVERS` are unset, the gateway auto-detects Kubernetes, then Podman, then Docker by CLI availability or a local Unix socket. The VM driver is never auto-detected; configure it explicitly with `--drivers vm`. +When `compute_drivers` is unset, the gateway auto-detects Kubernetes, then Podman, then Docker by CLI availability or a local Unix socket. The VM driver is never auto-detected; configure it explicitly with `compute_drivers = ["vm"]` or set `OPENSHELL_DRIVERS=vm` in the launch environment. Common gateway options: -| Option | Environment variable | Description | -|---|---|---| -| `--drivers ` | `OPENSHELL_DRIVERS` | Select the compute driver. Supported values are `docker`, `podman`, `kubernetes`, and `vm`. | +| Gateway TOML option | Description | +|---|---| +| `compute_drivers = [""]` | Select the compute driver. Supported values are `docker`, `podman`, `kubernetes`, and `vm`. | Set driver-specific values such as sandbox images, callback endpoints, network names, TLS material, and VM sizing in the gateway TOML file. See the [Gateway Configuration File](./gateway-config) reference for the full `[openshell.drivers.]` schema. @@ -45,7 +46,7 @@ The gateway talks to the Docker daemon to create sandbox containers. Docker is a For maintainer-level implementation details, refer to the [Docker driver README](https://github.com/NVIDIA/OpenShell/blob/main/crates/openshell-driver-docker/README.md). -Select Docker with `--drivers docker` or `OPENSHELL_DRIVERS=docker`. Configure Docker driver values such as `grpc_endpoint`, `network_name`, `supervisor_bin`, `supervisor_image`, `image_pull_policy`, and `guest_tls_*` in `[openshell.drivers.docker]`. +Select Docker with `compute_drivers = ["docker"]` in `[openshell.gateway]`. Configure Docker driver values such as `grpc_endpoint`, `network_name`, `supervisor_bin`, `supervisor_image`, `image_pull_policy`, and `guest_tls_*` in `[openshell.drivers.docker]`. For GPU-backed Docker sandboxes, configure Docker CDI before starting the gateway so OpenShell can detect the daemon capability. @@ -57,7 +58,7 @@ The gateway talks to the Podman API socket. The Podman driver requires Podman 5. For maintainer-level implementation details, refer to the [Podman driver README](https://github.com/NVIDIA/OpenShell/blob/main/crates/openshell-driver-podman/README.md) and [Podman networking notes](https://github.com/NVIDIA/OpenShell/blob/main/crates/openshell-driver-podman/NETWORKING.md). -Select Podman with `--drivers podman` or `OPENSHELL_DRIVERS=podman`. Configure Podman driver values such as `socket_path`, `network_name`, `supervisor_image`, `stop_timeout_secs`, `image_pull_policy`, `grpc_endpoint`, and `guest_tls_*` in `[openshell.drivers.podman]`. +Select Podman with `compute_drivers = ["podman"]` in `[openshell.gateway]`. Configure Podman driver values such as `socket_path`, `network_name`, `supervisor_image`, `stop_timeout_secs`, `image_pull_policy`, `grpc_endpoint`, and `guest_tls_*` in `[openshell.drivers.podman]`. ## MicroVM Driver @@ -77,15 +78,16 @@ For maintainer-level implementation details, refer to the [VM driver README](htt The VM driver is opt-in. Release packages can install `openshell-driver-vm`, but the gateway does not select it unless you configure the driver explicitly. -For a one-off gateway process, pass `--drivers vm`: +Enable VM by setting `compute_drivers = ["vm"]` in the gateway TOML file: -```shell -openshell-gateway --drivers vm +```toml +[openshell.gateway] +compute_drivers = ["vm"] ``` -For a service, set `OPENSHELL_DRIVERS=vm` in the service environment file and restart the service. Homebrew creates `$(brew --prefix)/var/openshell/gateway.env` with a commented `OPENSHELL_DRIVERS=vm` entry. Debian and RPM user services read `~/.config/openshell/gateway.env`. +For a launch-time override, set `OPENSHELL_DRIVERS=vm` in the gateway environment and restart the service. -Select VM with `--drivers vm` or `OPENSHELL_DRIVERS=vm`. Configure VM driver values such as `grpc_endpoint`, `driver_dir`, `state_dir`, `default_image`, `bootstrap_image`, `vcpus`, `mem_mib`, `overlay_disk_mib`, `krun_log_level`, and `guest_tls_*` in `[openshell.drivers.vm]`. The VM `state_dir` stores overlay disks, console logs, runtime state, image-rootfs cache, and the private `run/compute-driver.sock` socket. +Configure VM driver values such as `grpc_endpoint`, `driver_dir`, `state_dir`, `default_image`, `bootstrap_image`, `vcpus`, `mem_mib`, `overlay_disk_mib`, `krun_log_level`, and `guest_tls_*` in `[openshell.drivers.vm]`. The VM `state_dir` stores overlay disks, console logs, runtime state, image-rootfs cache, and the private `run/compute-driver.sock` socket. The gateway starts `openshell-driver-vm` over a private Unix socket and passes its process ID so the driver can reject unexpected local clients. The driver's standalone TCP listener is disabled unless `--allow-unauthenticated-tcp` is set for local development. @@ -107,7 +109,7 @@ For maintainer-level implementation details, refer to the [Kubernetes driver REA | Gateway configuration | Helm value | Description | |---|---|---| -| `compute_drivers = ["kubernetes"]` or `--drivers kubernetes` | Not applicable | Select the Kubernetes compute driver. | +| `compute_drivers = ["kubernetes"]` | Not applicable | Select the Kubernetes compute driver. | | `[openshell.drivers.kubernetes].namespace` | `server.sandboxNamespace` | Set the namespace for sandbox resources. The Helm chart defaults to the release namespace when left empty. | | `default_image` | `server.sandboxImage` | Set the default sandbox image. | | `image_pull_policy` | `server.sandboxImagePullPolicy` | Set the Kubernetes image pull policy for sandbox pods. | diff --git a/python/openshell/release_formula_test.py b/python/openshell/release_formula_test.py index f2f7bf787..95143a433 100644 --- a/python/openshell/release_formula_test.py +++ b/python/openshell/release_formula_test.py @@ -3,8 +3,10 @@ from __future__ import annotations +import os import subprocess import sys +import tomllib from pathlib import Path @@ -51,20 +53,117 @@ 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 "OPENSHELL_DRIVERS:" not in formula - assert "#OPENSHELL_DRIVERS=vm" in formula - assert 'OPENSHELL_GATEWAY_CONFIG: "#{var}/openshell/gateway.toml"' in formula - assert 'driver_dir = "#{opt_libexec}"' in formula - assert 'supervisor_image = "ghcr.io/nvidia/openshell/supervisor:0.0.10"' in formula + assert "OPENSHELL_DRIVERS: " not in formula + assert 'OPENSHELL_GATEWAY_CONFIG: "#{var}/openshell/gateway.toml"' not in formula + assert '(libexec/"init-gateway-config.sh").write' in formula + assert 'bind_address = "127.0.0.1:17670"' in formula + assert '# compute_drivers = ["vm"]' in formula + assert '# compute_drivers = ["docker"]' in formula + assert "[openshell.gateway.tls]" in formula + assert 'cert_path = $(toml_string "${pki_dir}/server/tls.crt")' in formula + assert 'driver_dir = $(toml_string "$driver_dir")' in formula + assert '"#{opt_libexec}/init-gateway-config.sh" homebrew' in formula + assert '"ghcr.io/nvidia/openshell/supervisor:0.0.10"' in formula assert 'run opt_libexec/"openshell-gateway-homebrew-service"' in formula + assert 'canonical_gateway_config="#{var}/openshell/gateway.toml"' in formula assert ( - 'docker_tls_dir="${OPENSHELL_DOCKER_TLS_DIR:-${HOME}/.local/state/openshell/homebrew/tls}"' + 'gateway_config="${OPENSHELL_GATEWAY_CONFIG:-${canonical_gateway_config}}"' + in formula + ) + assert ( + 'gateway_db_url="${OPENSHELL_DB_URL:-sqlite:#{var}/openshell/gateway/openshell.db}"' + ) in formula + assert ( + 'exec "#{opt_bin}/openshell-gateway" --config "${gateway_config}" --db-url "${gateway_db_url}"' ) in formula - assert 'guest_tls_ca = "${docker_tls_dir}/ca.crt"' in formula + assert 'docker_tls_dir="${HOME}/.local/state/openshell/homebrew/tls"' in formula + assert "OPENSHELL_CONFIG_" not in formula + assert "OPENSHELL_DOCKER_TLS_DIR" not in formula assert 'gateway_env="#{var}/openshell/gateway.env"' in formula assert '. "${gateway_env}"' in formula + assert 'gateway_env = var/"openshell/gateway.env"' not in formula + assert "#OPENSHELL_GATEWAY_CONFIG=#{var}/openshell/gateway.toml" not in formula + assert "environment_variables(" not in formula + assert " OPENSHELL_BIND_ADDRESS:" not in formula + assert " OPENSHELL_SERVER_PORT:" not in formula + assert " OPENSHELL_TLS_CERT:" not in formula assert "OPENSHELL_DRIVER_DIR:" not in formula assert "OPENSHELL_DOCKER_SUPERVISOR_IMAGE:" not in formula assert 'OPENSHELL_DOCKER_TLS_CA: "#{var}/openshell/tls/ca.crt"' not in formula assert "entitlements.atomic_write" in formula assert "brew services restart openshell" in formula + + +def test_gateway_config_helper_ignores_legacy_gateway_environment( + tmp_path: Path, +) -> None: + repo_root = Path(__file__).resolve().parents[2] + config = tmp_path / "gateway.toml" + pki_dir = tmp_path / "tls" + driver_dir = tmp_path / "libexec" + vm_state_dir = tmp_path / "vm-driver" + + env = { + "PATH": os.environ.get("PATH", "/usr/bin:/bin"), + "OPENSHELL_BIND_ADDRESS": "::1", + "OPENSHELL_SERVER_PORT": "19090", + "OPENSHELL_DRIVERS": "vm", + "OPENSHELL_TLS_CERT": "/legacy/server.crt", + "OPENSHELL_TLS_KEY": "/legacy/server.key", + "OPENSHELL_TLS_CLIENT_CA": "/legacy/ca.crt", + } + subprocess.run( + [ + str(repo_root / "deploy/common/init-gateway-config.sh"), + "deb", + str(config), + str(pki_dir), + str(driver_dir), + str(vm_state_dir), + ], + check=True, + env=env, + ) + + contents = config.read_text(encoding="utf-8") + assert '# compute_drivers = ["vm"]' in contents + + data = tomllib.loads(contents) + openshell = data["openshell"] + gateway = openshell["gateway"] + assert gateway["bind_address"] == "127.0.0.1:17670" + assert "compute_drivers" not in gateway + assert gateway["tls"]["cert_path"] == str(pki_dir / "server/tls.crt") + assert gateway["tls"]["key_path"] == str(pki_dir / "server/tls.key") + assert gateway["tls"]["client_ca_path"] == str(pki_dir / "ca.crt") + assert openshell["drivers"]["vm"]["driver_dir"] == str(driver_dir) + assert openshell["drivers"]["vm"]["state_dir"] == str(vm_state_dir) + assert openshell["drivers"]["vm"]["grpc_endpoint"] == "https://127.0.0.1:17670" + + +def test_snap_gateway_config_does_not_select_compute_driver( + tmp_path: Path, +) -> None: + repo_root = Path(__file__).resolve().parents[2] + config = tmp_path / "gateway.toml" + supervisor_bin = tmp_path / "snap/bin/openshell-sandbox" + + subprocess.run( + [ + str(repo_root / "deploy/common/init-gateway-config.sh"), + "snap", + str(config), + str(supervisor_bin), + ], + check=True, + ) + + contents = config.read_text(encoding="utf-8") + assert '# compute_drivers = ["docker"]' in contents + + data = tomllib.loads(contents) + openshell = data["openshell"] + gateway = openshell["gateway"] + assert "compute_drivers" not in gateway + assert gateway["disable_tls"] is True + assert openshell["drivers"]["docker"]["supervisor_bin"] == str(supervisor_bin) diff --git a/snapcraft.yaml b/snapcraft.yaml index 5f27ead1a..06db49b59 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -41,12 +41,6 @@ apps: daemon: simple refresh-mode: endure environment: - OPENSHELL_BIND_ADDRESS: 127.0.0.1 - OPENSHELL_SERVER_PORT: 17670 - OPENSHELL_DB_URL: "sqlite:$SNAP_COMMON/gateway.db?mode=rwc" - OPENSHELL_DISABLE_TLS: "true" - OPENSHELL_DRIVERS: docker - OPENSHELL_GATEWAY_CONFIG: "$SNAP_COMMON/gateway.toml" XDG_DATA_HOME: "$SNAP_COMMON" XDG_RUNTIME_DIR: "$SNAP_COMMON" plugs: @@ -91,6 +85,8 @@ parts: "$CRAFT_PART_INSTALL/bin/openshell-sandbox" install -D -m 0755 "$CRAFT_PROJECT_DIR/deploy/snap/bin/openshell-gateway-wrapper" \ "$CRAFT_PART_INSTALL/bin/openshell-gateway-wrapper" + install -D -m 0755 "$CRAFT_PROJECT_DIR/deploy/common/init-gateway-config.sh" \ + "$CRAFT_PART_INSTALL/bin/init-gateway-config.sh" install -D -m 0644 "$CRAFT_PROJECT_DIR/LICENSE" \ "$CRAFT_PART_INSTALL/usr/share/doc/openshell/LICENSE" install -D -m 0644 "$CRAFT_PROJECT_DIR/README.md" \ diff --git a/tasks/scripts/package-deb.sh b/tasks/scripts/package-deb.sh index 5705e3385..299c7b29c 100755 --- a/tasks/scripts/package-deb.sh +++ b/tasks/scripts/package-deb.sh @@ -115,7 +115,7 @@ stage_binary "$OPENSHELL_DRIVER_VM_BINARY" "$pkgroot/usr/libexec/openshell/opens # Per-user systemd unit. Each user enables it via `systemctl --user`. install -D -m 0644 "$src_dir/openshell-gateway.service" \ "$pkgroot/usr/lib/systemd/user/openshell-gateway.service" -install -D -m 0755 "$src_dir/init-gateway-config.sh" \ +install -D -m 0755 "${repo_root}/deploy/common/init-gateway-config.sh" \ "$pkgroot/usr/libexec/openshell/init-gateway-config.sh" # --------------------------------------------------------------------------- diff --git a/tasks/scripts/package-snap.sh b/tasks/scripts/package-snap.sh index 8c299d352..491cf8e20 100755 --- a/tasks/scripts/package-snap.sh +++ b/tasks/scripts/package-snap.sh @@ -184,6 +184,8 @@ stage_binary "$OPENSHELL_GATEWAY_BINARY" "$snap_root/bin/openshell-gateway" stage_binary "$OPENSHELL_DOCKER_SUPERVISOR_BINARY" "$snap_root/bin/openshell-sandbox" install -D -m 0755 "${repo_root}/deploy/snap/bin/openshell-gateway-wrapper" \ "$snap_root/bin/openshell-gateway-wrapper" +install -D -m 0755 "${repo_root}/deploy/common/init-gateway-config.sh" \ + "$snap_root/bin/init-gateway-config.sh" install -D -m 0644 "${repo_root}/LICENSE" "$snap_root/usr/share/doc/openshell/LICENSE" install -D -m 0644 "${repo_root}/README.md" "$snap_root/usr/share/doc/openshell/README.md" diff --git a/tasks/scripts/release.py b/tasks/scripts/release.py index df61e0907..c25d7d239 100644 --- a/tasks/scripts/release.py +++ b/tasks/scripts/release.py @@ -229,6 +229,14 @@ def _homebrew_supervisor_image(release_tag: str) -> str: return f"ghcr.io/nvidia/openshell/supervisor:{image_tag}" +def _homebrew_gateway_config_helper() -> str: + return ( + (_repo_root() / "deploy/common/init-gateway-config.sh") + .read_text(encoding="utf-8") + .rstrip() + ) + + def render_homebrew_formula( *, release_tag: str, @@ -241,6 +249,7 @@ def render_homebrew_formula( version = release_tag.removeprefix("v") docker_supervisor_image = _homebrew_supervisor_image(release_tag) + gateway_config_helper = _homebrew_gateway_config_helper() return f"""# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # @@ -280,6 +289,11 @@ def install libexec.install "openshell-driver-vm" end + (libexec/"init-gateway-config.sh").write <<~'SH' +{gateway_config_helper} + SH + chmod 0755, libexec/"init-gateway-config.sh" + (libexec/"openshell-gateway-homebrew-service").write <<~SH #!/bin/sh set -eu @@ -296,44 +310,23 @@ def install set +a fi - docker_tls_dir="${{OPENSHELL_DOCKER_TLS_DIR:-${{HOME}}/.local/state/openshell/homebrew/tls}}" + docker_tls_dir="${{HOME}}/.local/state/openshell/homebrew/tls" mkdir -p "${{docker_tls_dir}}/client" chmod 700 "${{docker_tls_dir}}" "${{docker_tls_dir}}/client" /usr/bin/install -m 0644 "#{{var}}/openshell/tls/ca.crt" "${{docker_tls_dir}}/ca.crt" /usr/bin/install -m 0644 "#{{var}}/openshell/tls/client/tls.crt" "${{docker_tls_dir}}/client/tls.crt" /usr/bin/install -m 0600 "#{{var}}/openshell/tls/client/tls.key" "${{docker_tls_dir}}/client/tls.key" - gateway_config="${{OPENSHELL_GATEWAY_CONFIG:-#{{var}}/openshell/gateway.toml}}" - if [ ! -f "${{gateway_config}}" ]; then - mkdir -p "$(dirname "${{gateway_config}}")" "#{{var}}/openshell/vm-driver" - cat > "${{gateway_config}}" < @@ -377,15 +354,6 @@ def post_install service do run opt_libexec/"openshell-gateway-homebrew-service" - environment_variables( - OPENSHELL_BIND_ADDRESS: "127.0.0.1", - OPENSHELL_SERVER_PORT: "{LOCAL_GATEWAY_PORT}", - OPENSHELL_TLS_CERT: "#{{var}}/openshell/tls/server/tls.crt", - OPENSHELL_TLS_KEY: "#{{var}}/openshell/tls/server/tls.key", - OPENSHELL_TLS_CLIENT_CA: "#{{var}}/openshell/tls/ca.crt", - OPENSHELL_DB_URL: "sqlite:#{{var}}/openshell/gateway/openshell.db", - OPENSHELL_GATEWAY_CONFIG: "#{{var}}/openshell/gateway.toml", - ) keep_alive successful_exit: false log_path var/"log/openshell/openshell-gateway.out.log" error_log_path var/"log/openshell/openshell-gateway.err.log"