Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions deploy/deb/control.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Maintainer: NVIDIA OpenShell Maintainers <openshell@nvidia.com>
Section: utils
Priority: optional
Pre-Depends: init-system-helpers (>= 1.54~)
Recommends: podman
Homepage: https://github.com/NVIDIA/OpenShell
Description: Safe, sandboxed runtimes for autonomous AI agents
OpenShell provides host-side command-line and gateway components for
Expand Down
21 changes: 13 additions & 8 deletions deploy/deb/openshell-gateway.service
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
[Unit]
Description=OpenShell Gateway
Documentation=https://github.com/NVIDIA/OpenShell
After=default.target
After=default.target podman.socket
Requires=podman.socket

[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_BIND_ADDRESS=0.0.0.0
Environment=OPENSHELL_SERVER_PORT=8080
Environment=OPENSHELL_DRIVERS=podman
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_GRPC_ENDPOINT=https://127.0.0.1:17670
# Leave OPENSHELL_GRPC_ENDPOINT unset so Podman auto-detects a
# host.containers.internal callback using the configured listener port.
Environment=OPENSHELL_SSH_GATEWAY_HOST=127.0.0.1
Environment=OPENSHELL_SSH_GATEWAY_PORT=17670
Environment=OPENSHELL_SSH_GATEWAY_PORT=8080
Environment=OPENSHELL_SUPERVISOR_IMAGE=ghcr.io/nvidia/openshell/supervisor:latest
Environment=OPENSHELL_SANDBOX_IMAGE=ghcr.io/nvidia/openshell-community/sandboxes/base:latest
Environment=OPENSHELL_VM_DRIVER_STATE_DIR=%S/openshell/vm-driver
Environment=OPENSHELL_VM_TLS_CA=%S/openshell/tls/ca.crt
Environment=OPENSHELL_VM_TLS_CERT=%S/openshell/tls/client/tls.crt
Expand All @@ -26,9 +31,9 @@ Environment=OPENSHELL_DOCKER_TLS_KEY=%S/openshell/tls/client/tls.key
Environment=OPENSHELL_PODMAN_TLS_CA=%S/openshell/tls/ca.crt
Environment=OPENSHELL_PODMAN_TLS_CERT=%S/openshell/tls/client/tls.crt
Environment=OPENSHELL_PODMAN_TLS_KEY=%S/openshell/tls/client/tls.key
EnvironmentFile=-%h/.config/openshell/gateway.env
ExecStartPre=/usr/bin/openshell-gateway generate-certs --output-dir %S/openshell/tls --server-san host.openshell.internal
ExecStart=/usr/bin/openshell-gateway
EnvironmentFile=-%E/openshell/gateway.env
ExecStartPre=/usr/bin/openshell-gateway generate-certs --output-dir %S/openshell/tls --server-san host.openshell.internal --server-san host.containers.internal --server-san host.docker.internal
ExecStart=/bin/bash /usr/libexec/openshell/openshell-gateway-start.sh /usr/libexec/openshell/init-gateway-env.sh %E/openshell/gateway.env /usr/bin/openshell-gateway
Restart=on-failure
RestartSec=5s
PrivateTmp=true
Expand Down
4 changes: 2 additions & 2 deletions deploy/rpm/init-gateway-env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

# Generate the gateway environment configuration file on first start.
#
# Called from the systemd ExecStartPre directive to bootstrap the
# gateway configuration. Idempotent: exits immediately if the file
# Called from systemd startup helpers to bootstrap the gateway
# configuration. Idempotent: exits immediately if the file
# already exists.
#
# Usage:
Expand Down
39 changes: 39 additions & 0 deletions deploy/systemd/openshell-gateway-start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/bin/bash
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# Generate the gateway environment file if needed, export the generated
# first-start SSH handshake secret, then exec the gateway. systemd reads
# EnvironmentFile= before ExecStartPre=, so a file generated during service
# startup is not visible to the first ExecStart= process.

set -euo pipefail

if [ "$#" -lt 3 ]; then
echo "Usage: openshell-gateway-start.sh <env-generator> <env-file> <gateway> [args...]" >&2
exit 2
fi

ENV_GENERATOR="$1"
ENV_FILE="$2"
shift 2

"${ENV_GENERATOR}" "${ENV_FILE}"

if [ -z "${OPENSHELL_SSH_HANDSHAKE_SECRET:-}" ]; then
while IFS= read -r line || [ -n "${line}" ]; do
case "${line}" in
OPENSHELL_SSH_HANDSHAKE_SECRET=*)
export OPENSHELL_SSH_HANDSHAKE_SECRET="${line#OPENSHELL_SSH_HANDSHAKE_SECRET=}"
break
;;
esac
done < "${ENV_FILE}"
fi

if [ -z "${OPENSHELL_SSH_HANDSHAKE_SECRET:-}" ]; then
echo "OPENSHELL_SSH_HANDSHAKE_SECRET is not set in ${ENV_FILE}" >&2
exit 1
fi

exec "$@"
4 changes: 2 additions & 2 deletions docs/about/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ brew services restart openshell

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, 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, a Podman-backed systemd user service, and the VM driver helper.

The Debian user service listens on `https://127.0.0.1:17670` and generates a local mTLS bundle before the gateway starts. The CLI reads the client bundle from `~/.config/openshell/gateways/openshell/mtls/`.
The Linux user service listens on `https://127.0.0.1:8080` and generates a local mTLS bundle before the gateway starts. The CLI reads the client bundle from `~/.config/openshell/gateways/openshell/mtls/`.

The installer starts the service for you. Use systemd user commands when you need to inspect, restart, or stop the gateway service:

Expand Down
4 changes: 3 additions & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ start_user_gateway() {
if ! as_target_user systemctl --user daemon-reload; then
info "could not reach the user systemd manager for ${TARGET_USER}"
info "restart the gateway later with: systemctl --user enable openshell-gateway && systemctl --user restart openshell-gateway"
info "then register it with: openshell gateway add https://127.0.0.1:17670 --local --name openshell"
info "then register it with: openshell gateway add https://127.0.0.1:${LOCAL_GATEWAY_PORT} --local --name openshell"
return 0
fi

Expand Down Expand Up @@ -744,6 +744,7 @@ install_linux_deb() {
info "installing ${_deb_file}..."
install_deb_package "$_deb_path"
info "installed ${APP_NAME} package from ${RELEASE_TAG}"
LOCAL_GATEWAY_PORT="8080"
start_user_gateway
}

Expand Down Expand Up @@ -791,6 +792,7 @@ install_linux_rpm() {
info "installing ${_rpm_file} and ${_gateway_rpm_file}..."
install_rpm_packages "${_tmpdir}/${_rpm_file}" "${_tmpdir}/${_gateway_rpm_file}"
info "installed ${APP_NAME} RPM packages from ${RELEASE_TAG}"
LOCAL_GATEWAY_PORT="8080"
start_user_gateway
}

Expand Down
8 changes: 5 additions & 3 deletions openshell.spec
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,9 @@ Type=exec
ExecStartPre=%{_libexecdir}/openshell/init-pki.sh %%S/openshell/tls

# Auto-generate gateway.env (SSH handshake secret + commented config
# reference) on first start if not present.
# reference) on first start if not present. ExecStart exports the generated
# secret so the first gateway process receives it.
# %%E expands to $XDG_CONFIG_HOME (~/.config) in user units.
ExecStartPre=%{_libexecdir}/openshell/init-gateway-env.sh %%E/openshell/gateway.env
EnvironmentFile=-%%E/openshell/gateway.env
Environment=OPENSHELL_BIND_ADDRESS=0.0.0.0
Environment=OPENSHELL_DRIVERS=podman
Expand All @@ -171,7 +171,7 @@ Environment=OPENSHELL_TLS_CLIENT_CA=%%S/openshell/tls/ca.crt
Environment=OPENSHELL_PODMAN_TLS_CA=%%S/openshell/tls/ca.crt
Environment=OPENSHELL_PODMAN_TLS_CERT=%%S/openshell/tls/client/tls.crt
Environment=OPENSHELL_PODMAN_TLS_KEY=%%S/openshell/tls/client/tls.key
ExecStart=/usr/bin/openshell-gateway
ExecStart=/bin/bash %{_libexecdir}/openshell/openshell-gateway-start.sh %{_libexecdir}/openshell/init-gateway-env.sh %%E/openshell/gateway.env %{_bindir}/openshell-gateway
StateDirectory=openshell
Restart=on-failure
RestartSec=5
Expand All @@ -190,6 +190,7 @@ EOF
install -d %{buildroot}%{_libexecdir}/%{name}
install -pm 0755 deploy/rpm/init-pki.sh %{buildroot}%{_libexecdir}/%{name}/init-pki.sh
install -pm 0755 deploy/rpm/init-gateway-env.sh %{buildroot}%{_libexecdir}/%{name}/init-gateway-env.sh
install -pm 0755 deploy/systemd/openshell-gateway-start.sh %{buildroot}%{_libexecdir}/%{name}/openshell-gateway-start.sh
# Patch commented image defaults to match the build type (dev or latest).
# The source file uses :latest as a generic reference; the installed copy
# reflects what this RPM actually expects from the registry.
Expand Down Expand Up @@ -277,6 +278,7 @@ PYTHONPATH=%{buildroot}%{python3_sitelib} %{python3} -c "from importlib.metadata
%{_userunitdir}/%{name}-gateway.service
%{_libexecdir}/%{name}/init-pki.sh
%{_libexecdir}/%{name}/init-gateway-env.sh
%{_libexecdir}/%{name}/openshell-gateway-start.sh
%{_mandir}/man8/openshell-gateway.8*
%{_mandir}/man5/openshell-gateway.env.5*

Expand Down
21 changes: 20 additions & 1 deletion tasks/scripts/gateway.sh
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ port_is_in_use() {
(echo >/dev/tcp/127.0.0.1/"${port}") >/dev/null 2>&1
}

generate_secret() {
if command_available openssl; then
openssl rand -hex 32
else
od -An -tx1 -N32 /dev/urandom | tr -dc '0-9a-f'
fi
}

register_gateway_metadata() {
local name=$1
local endpoint=$2
Expand Down Expand Up @@ -250,9 +258,17 @@ STATE_DIR="${OPENSHELL_GATEWAY_STATE_DIR:-${ROOT}/.cache/gateway-${DRIVER}}"
SANDBOX_NAMESPACE="${OPENSHELL_SANDBOX_NAMESPACE:-${DRIVER}-dev}"
SANDBOX_IMAGE="${OPENSHELL_SANDBOX_IMAGE:-ghcr.io/nvidia/openshell-community/sandboxes/base:latest}"
SANDBOX_IMAGE_PULL_POLICY="${OPENSHELL_SANDBOX_IMAGE_PULL_POLICY:-IfNotPresent}"
if [[ "${DRIVER}" == "podman" ]]; then
SANDBOX_IMAGE_PULL_POLICY="${OPENSHELL_SANDBOX_IMAGE_PULL_POLICY:-missing}"
fi
LOG_LEVEL="${OPENSHELL_LOG_LEVEL:-info}"
BIND_ADDRESS="${OPENSHELL_BIND_ADDRESS:-127.0.0.1}"
SSH_HANDSHAKE_SECRET="${OPENSHELL_SSH_HANDSHAKE_SECRET:-$(generate_secret)}"

if [[ "${DRIVER}" == "podman" ]]; then
# Podman sandboxes call back through host.containers.internal, so the
# development plaintext gateway must listen beyond loopback.
BIND_ADDRESS="${OPENSHELL_BIND_ADDRESS:-0.0.0.0}"
require_podman_service
SUPERVISOR_IMAGE="${OPENSHELL_SUPERVISOR_IMAGE:-openshell/supervisor:dev}"
ensure_podman_supervisor_image "${SUPERVISOR_IMAGE}"
Expand Down Expand Up @@ -287,6 +303,7 @@ echo " gateway: ${GATEWAY_NAME}"
echo " endpoint: ${GATEWAY_ENDPOINT}"
echo " namespace: ${SANDBOX_NAMESPACE}"
echo " state dir: ${STATE_DIR}"
echo " bind: ${BIND_ADDRESS}"
if [[ "${DRIVER}" == "podman" ]]; then
echo " supervisor image: ${OPENSHELL_SUPERVISOR_IMAGE}"
fi
Expand All @@ -295,11 +312,13 @@ echo "Active gateway set to '${GATEWAY_NAME}'. The CLI now targets this gateway
echo

exec "${GATEWAY_BIN}" \
--bind-address "${BIND_ADDRESS}" \
--port "${PORT}" \
--log-level "${LOG_LEVEL}" \
--drivers "${DRIVER}" \
--disable-tls \
--db-url "sqlite:${STATE_DIR}/gateway.db?mode=rwc" \
--sandbox-namespace "${SANDBOX_NAMESPACE}" \
--sandbox-image "${SANDBOX_IMAGE}" \
--sandbox-image-pull-policy "${SANDBOX_IMAGE_PULL_POLICY}"
--sandbox-image-pull-policy "${SANDBOX_IMAGE_PULL_POLICY}" \
--ssh-handshake-secret "${SSH_HANDSHAKE_SECRET}"
9 changes: 9 additions & 0 deletions tasks/scripts/package-deb.sh
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ stage_binary "$OPENSHELL_DRIVER_VM_BINARY" "$pkgroot/usr/libexec/openshell/opens
install -D -m 0644 "$src_dir/openshell-gateway.service" \
"$pkgroot/usr/lib/systemd/user/openshell-gateway.service"

# First-start user configuration bootstrap. The service uses the gateway
# binary for PKI generation and these helpers for the SSH handshake secret.
install -D -m 0755 "${repo_root}/deploy/rpm/init-gateway-env.sh" \
"$pkgroot/usr/libexec/openshell/init-gateway-env.sh"
install -D -m 0755 "${repo_root}/deploy/systemd/openshell-gateway-start.sh" \
"$pkgroot/usr/libexec/openshell/openshell-gateway-start.sh"

# ---------------------------------------------------------------------------
# DEBIAN/ control directory
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -177,6 +184,8 @@ dpkg-deb -x "$package_file" "$extract_dir"
"$extract_dir/usr/bin/openshell" --version
"$extract_dir/usr/bin/openshell-gateway" --version
"$extract_dir/usr/libexec/openshell/openshell-driver-vm" --version
test -x "$extract_dir/usr/libexec/openshell/init-gateway-env.sh"
test -x "$extract_dir/usr/libexec/openshell/openshell-gateway-start.sh"

if command -v systemd-analyze >/dev/null 2>&1; then
# verify --user catches user-scope-specific issues like StateDirectory=
Expand Down
Loading