Skip to content
Draft
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
59 changes: 58 additions & 1 deletion docs/about/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Install OpenShell with a single command:
curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install.sh | sh
```

The script detects your operating system and installs the OpenShell CLI and gateway with your native package manager. It then starts the local gateway server so you can begin creating sandboxes.
The script detects your operating system and installs the OpenShell CLI and gateway. On Linux, the Snap path is preferred when `snapd` is available; otherwise the script uses the native DEB or RPM package. The gateway then starts automatically so you can begin creating sandboxes.

You can also download release artifacts directly from the [OpenShell GitHub Releases](https://github.com/NVIDIA/OpenShell/releases) page.

Expand Down Expand Up @@ -51,6 +51,8 @@ brew services restart openshell

## Linux

On distributions that ship with `snapd`, the install script uses the Snap path described below. On hosts without `snapd` (or with `OPENSHELL_INSTALL_METHOD=classic` set), the script falls back to the classic package manager.

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.
Expand All @@ -75,6 +77,61 @@ To keep the user service running after logout, enable linger:
sudo loginctl enable-linger $USER
```

## Snap

On Linux distributions that ship with `snapd`, the install script installs OpenShell from the Snap Store. The OpenShell snap bundles the CLI, the terminal UI, and a managed gateway daemon. snapd handles upgrades and rollback; the gateway runs as a system service inside the snap.

You can also install the snap directly:

```shell
sudo snap install openshell
```

The snap requires the Docker snap. `default-provider: docker` on the OpenShell snap installs the Docker snap automatically on first use, but you can install it up front with:

```shell
sudo snap install docker
```

### Connect the required interfaces

Strict confinement requires explicit `snap connect` for several interfaces. The installer runs these for you on Snap installs; run them by hand if you install the snap manually:

```shell
sudo snap connect openshell:docker docker:docker-daemon
sudo snap connect openshell:log-observe
sudo snap connect openshell:ssh-keys
sudo snap connect openshell:system-observe
```

The Docker slot is the Docker snap's `docker-daemon` slot; OpenShell does not work with a host-installed Docker Engine. The installer is best-effort: if a connect fails (for example because the Docker snap is not yet running), the snap still installs and the installer prints a warning.

### Verify the gateway

The snap-managed gateway service is `openshell.gateway`. Inspect it with:

```shell
snap services openshell
snap logs -n 100 openshell.gateway
```

Register the gateway with the CLI:

```shell
openshell gateway add https://127.0.0.1:17670 --local --name openshell
openshell status
```

The gateway listens on `https://127.0.0.1:17670` and stores its state under `/var/snap/openshell/common/`. Override gateway settings by creating `/var/snap/openshell/common/gateway.toml`.

### When to choose Snap

Use Snap when `snapd` is available and you want atomic upgrades and rollback, a single self-contained install that bundles the Docker provider, or a desktop launcher that surfaces the OpenShell terminal UI in the application menu.

Use DEB or RPM when `snapd` is unavailable or inappropriate (immutable distros, headless servers, locked-down images), when you need direct `systemd --user` integration for the gateway, or when you already run Docker Engine from a non-snap source.

Set `OPENSHELL_INSTALL_METHOD=classic` to force the classic package on hosts that also have `snapd` available.

## Kubernetes

Kubernetes deployments use the OpenShell Helm chart. For step-by-step installation, refer to [Kubernetes Setup](/kubernetes/setup). For chart values and packaging details, refer to the [Helm chart README](https://github.com/NVIDIA/OpenShell/blob/main/deploy/helm/openshell/README.md).
Expand Down
157 changes: 143 additions & 14 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
#
# Install OpenShell from a GitHub release.
#
# Linux installs either the Debian or RPM packages from the selected release.
# Apple Silicon macOS installs the generated Homebrew formula, so Homebrew owns
# the binary layout and launchd service lifecycle.
# Linux prefers the Snap path when snapd is available. Otherwise Linux installs
# the Debian or RPM packages from the selected release. Apple Silicon macOS
# installs the generated Homebrew formula, so Homebrew owns the binary layout
# and launchd service lifecycle.
#
set -e

Expand All @@ -21,6 +22,12 @@ HOMEBREW_FORMULA_NAME="openshell"
BREAKING_RELEASE_VERSION="0.0.37"
LINUX_PACKAGE_GLIBC_MIN_VERSION="2.31"
UPGRADE_NOTICE_ACK="${OPENSHELL_ACK_BREAKING_UPGRADE:-}"
SNAP_PACKAGE_NAME="openshell"
SNAP_DOCKER_SLOT="docker:docker-daemon"
SNAP_DEFAULT_CHANNEL="latest/stable"
SNAP_DEV_CHANNEL="latest/edge"
LINUX_INSTALL_METHOD_SNAP="snap"
LINUX_INSTALL_METHOD_CLASSIC="classic"

info() {
printf '%s: %s\n' "$APP_NAME" "$*" >&2
Expand Down Expand Up @@ -54,13 +61,27 @@ ENVIRONMENT VARIABLES:
OPENSHELL_ACK_BREAKING_UPGRADE
Set to 1 only after backing up and cleaning up a
pre-v0.0.37 installation.
OPENSHELL_INSTALL_METHOD
Linux only. Selects snap or classic. Accepted values:
snap install from the Snap Store
classic install via dpkg or rpm
When unset, snap is used when snapd is available and
running, otherwise the classic package manager is used.
Set OPENSHELL_INSTALL_METHOD=classic on distros where
snapd is not desired.

NOTES:
When OPENSHELL_VERSION is unset, this resolves the latest tagged release
from ${GITHUB_URL}/releases/latest.

Linux installs the Debian package on amd64/arm64 or the RPM packages on
x86_64/aarch64, depending on the host package manager.
Linux prefers the snap method when snapd is available. The Snap channel
follows OPENSHELL_VERSION: 'dev' selects ${SNAP_DEV_CHANNEL:-latest/edge},
tagged releases select ${SNAP_DEFAULT_CHANNEL:-latest/stable}. snapd
manages the gateway as the 'openshell.gateway' service.

On systems without snapd, Linux installs the Debian package on
amd64/arm64 or the RPM packages on x86_64/aarch64, depending on the
host package manager.
macOS installs the release Homebrew formula on Apple Silicon and starts a
brew services-backed local gateway.
EOF
Expand Down Expand Up @@ -477,6 +498,69 @@ linux_package_method() {
fi
}

has_snapd() {
if [ "${OPENSHELL_INSTALL_SH_TEST:-0}" = "1" ]; then
case "${OPENSHELL_TEST_SNAPD_AVAILABLE:-0}" in
1) return 0 ;;
0) return 1 ;;
esac
fi

command -v snap >/dev/null 2>&1 || return 1
[ -S /run/snapd.socket ] || [ -S /var/run/snapd.socket ] || return 1
return 0
}

host_supports_native_package() {
has_cmd dpkg || has_cmd rpm
}

check_docker_not_installed_natively() {
if ! command -v docker >/dev/null 2>&1; then
return 0
fi

_docker_path="$(command -v docker)"
case "$_docker_path" in
/snap/bin/docker | /var/lib/snapd/snap/bin/docker)
return 0
;;
esac

error "Docker is installed via a non-snap package. The Docker snap cannot be installed alongside a native Docker installation. Please uninstall the native Docker package and try again, or install OpenShell via dpkg/rpm instead of snap."
}

resolve_linux_install_method() {
_method="${OPENSHELL_INSTALL_METHOD:-}"
if [ -n "$_method" ]; then
case "$_method" in
"$LINUX_INSTALL_METHOD_SNAP" | "$LINUX_INSTALL_METHOD_CLASSIC")
echo "$_method"
return 0
;;
*)
error "OPENSHELL_INSTALL_METHOD must be '${LINUX_INSTALL_METHOD_SNAP}' or '${LINUX_INSTALL_METHOD_CLASSIC}' (got: ${_method})"
;;
esac
fi

if has_snapd; then
echo "$LINUX_INSTALL_METHOD_SNAP"
elif host_supports_native_package; then
echo "$LINUX_INSTALL_METHOD_CLASSIC"
else
error "OpenShell Linux installs require either snapd or dpkg/rpm"
fi
}

resolve_snap_channel() {
if [ "${RELEASE_TAG:-}" = "dev" ]; then
echo "$SNAP_DEV_CHANNEL"
else
echo "$SNAP_DEFAULT_CHANNEL"
fi
}

set_linux_target_runtime_dir() {
if [ "$(id -u)" -eq "$TARGET_UID" ] && [ -n "${XDG_RUNTIME_DIR:-}" ]; then
TARGET_RUNTIME_DIR="$XDG_RUNTIME_DIR"
Expand Down Expand Up @@ -866,6 +950,44 @@ print_gateway_add_output() {
done
}

install_linux_snap() {
_channel="$(resolve_snap_channel)"

info "installing ${APP_NAME} from the Snap Store (channel: ${_channel})..."
as_root snap install "$SNAP_PACKAGE_NAME" --channel="$_channel"

# TODO: Remove this check once snapd 2.76 is released, which lifts the
# restriction preventing snap packages from being installed alongside a
# native Docker installation. At that point the docker snap can be
# pre-installed unconditionally.
check_docker_not_installed_natively

info "pre-installing the Docker snap..."
as_root snap install docker

info "connecting required interfaces..."
as_root snap connect "${SNAP_PACKAGE_NAME}:docker" "$SNAP_DOCKER_SLOT" || \
warn "could not connect ${SNAP_PACKAGE_NAME}:docker to ${SNAP_DOCKER_SLOT}; the Docker driver will not work until this is fixed"
for _plug in log-observe system-observe ssh-keys; do
as_root snap connect "${SNAP_PACKAGE_NAME}:${_plug}" || \
warn "could not connect ${SNAP_PACKAGE_NAME}:${_plug}; some features may be limited"
done

info "installed ${APP_NAME} from the Snap Store (${_channel})"
info "snapd manages the OpenShell gateway as the 'openshell.gateway' service."
info ""
info "Next steps:"
info " snap services openshell"
info " openshell status"
info " openshell gateway add https://127.0.0.1:17670 --local --name openshell"
if ! command -v snap >/dev/null 2>&1; then
info ""
info "note: the 'openshell' binary is provided by the snap."
info "Make sure snap is in your PATH, or invoke openshell with the"
info "absolute path, before running the next-step commands."
fi
}

install_linux_deb() {
check_linux_deb_platform
set_linux_target_runtime_dir
Expand Down Expand Up @@ -1033,16 +1155,23 @@ main() {

case "$PLATFORM" in
linux)
require_linux_package_glibc
case "$(linux_package_method)" in
deb)
install_linux_deb
;;
rpm)
install_linux_rpm
case "$(resolve_linux_install_method)" in
"$LINUX_INSTALL_METHOD_SNAP")
install_linux_snap
;;
*)
error "unsupported Linux package method"
"$LINUX_INSTALL_METHOD_CLASSIC")
require_linux_package_glibc
case "$(linux_package_method)" in
deb)
install_linux_deb
;;
rpm)
install_linux_rpm
;;
*)
error "unsupported Linux package method"
;;
esac
;;
esac
;;
Expand Down
78 changes: 78 additions & 0 deletions tasks/scripts/test-install-sh.sh
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,82 @@ assert_glibc_preflight_fails \
"OpenShell Linux packages require glibc >= 2.31; detected musl or unsupported libc." \
setup_ldd_musl

assert_eq() {
local name=$1
local expected=$2
local actual=$3
if [ "$expected" != "$actual" ]; then
echo "FAIL: ${name}: expected '${expected}', got '${actual}'" >&2
exit 1
fi
}

# has_snapd: with the test-mode override, OPENSHELL_TEST_SNAPD_AVAILABLE selects
# the result. In real environments the function probes for `snap` and the snapd
# socket, which the test cannot exercise without root or a real snapd.
(export OPENSHELL_TEST_SNAPD_AVAILABLE=1; has_snapd) || {
echo "FAIL: has_snapd with OPENSHELL_TEST_SNAPD_AVAILABLE=1" >&2
exit 1
}
(export OPENSHELL_TEST_SNAPD_AVAILABLE=0; has_snapd) && {
echo "FAIL: has_snapd with OPENSHELL_TEST_SNAPD_AVAILABLE=0 should fail" >&2
exit 1
}

# resolve_snap_channel
export RELEASE_TAG=""
assert_eq "resolve_snap_channel default" "latest/stable" "$(resolve_snap_channel)"
export RELEASE_TAG="dev"
assert_eq "resolve_snap_channel dev" "latest/edge" "$(resolve_snap_channel)"
export RELEASE_TAG="v0.0.37"
assert_eq "resolve_snap_channel tagged" "latest/stable" "$(resolve_snap_channel)"
export RELEASE_TAG=""
unset RELEASE_TAG

# resolve_linux_install_method: explicit env var wins over the probe
export OPENSHELL_TEST_SNAPD_AVAILABLE=1
export OPENSHELL_INSTALL_METHOD=snap
assert_eq "resolve_linux_install_method snap explicit" "snap" "$(resolve_linux_install_method)"
export OPENSHELL_INSTALL_METHOD=classic
assert_eq "resolve_linux_install_method classic explicit overrides snap probe" "classic" "$(resolve_linux_install_method)"
export OPENSHELL_TEST_SNAPD_AVAILABLE=0
export OPENSHELL_INSTALL_METHOD=classic
assert_eq "resolve_linux_install_method classic explicit" "classic" "$(resolve_linux_install_method)"
unset OPENSHELL_INSTALL_METHOD

# resolve_linux_install_method: snap when available
export OPENSHELL_TEST_SNAPD_AVAILABLE=1
unset OPENSHELL_INSTALL_METHOD
assert_eq "resolve_linux_install_method snap auto" "snap" "$(resolve_linux_install_method)"

# resolve_linux_install_method: invalid env var exits non-zero
export OPENSHELL_INSTALL_METHOD=invalid
_snap_err="${tmpdir}/snap-err"
if (resolve_linux_install_method) >/dev/null 2>"$_snap_err"; then
echo "FAIL: resolve_linux_install_method should fail on invalid value" >&2
exit 1
fi
if ! grep -Fq "OPENSHELL_INSTALL_METHOD must be" "$_snap_err"; then
echo "FAIL: resolve_linux_install_method: missing expected error message" >&2
cat "$_snap_err" >&2 || true
exit 1
fi
unset OPENSHELL_INSTALL_METHOD

# resolve_linux_install_method: snap absent + clean PATH (no dpkg/rpm) exits non-zero
export OPENSHELL_TEST_SNAPD_AVAILABLE=0
_real_path="$PATH"
_clean_path="${tmpdir}/clean-path"
mkdir -p "$_clean_path"
PATH="$_clean_path"
unset OPENSHELL_INSTALL_METHOD
if (resolve_linux_install_method) >/dev/null 2>"$_snap_err"; then
PATH="$_real_path"
echo "FAIL: resolve_linux_install_method should fail without snap or native" >&2
exit 1
fi
PATH="$_real_path"
export OPENSHELL_TEST_SNAPD_AVAILABLE=1
unset OPENSHELL_INSTALL_METHOD

echo "install.sh libc preflight tests passed"
Loading