Skip to content
Merged
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
20 changes: 20 additions & 0 deletions .github/workflows/release-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,11 @@ jobs:
- name: Install and check status
run: |
set -euo pipefail
other_user=openshell-canary-other
sudo useradd --create-home "$other_user"
other_uid=$(id -u "$other_user")
sudo loginctl enable-linger "$other_user"
sudo systemctl start "user@${other_uid}.service"
systemctl --user set-environment \
"OPENSHELL_TELEMETRY_ENABLED=${OPENSHELL_TELEMETRY_ENABLED}"
curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/${{ github.event.workflow_run.head_sha || github.sha }}/install.sh |
Expand All @@ -238,6 +243,21 @@ jobs:
snap services openshell
systemctl --user is-enabled --quiet snap.openshell.user-gateway.service
systemctl --user is-active snap.openshell.user-gateway.service
sudo -u "$other_user" env \
HOME="/home/${other_user}" \
XDG_RUNTIME_DIR="/run/user/${other_uid}" \
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/${other_uid}/bus" \
systemctl --user daemon-reload
! sudo -u "$other_user" env \
HOME="/home/${other_user}" \
XDG_RUNTIME_DIR="/run/user/${other_uid}" \
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/${other_uid}/bus" \
systemctl --user is-enabled --quiet snap.openshell.user-gateway.service
! sudo -u "$other_user" env \
HOME="/home/${other_user}" \
XDG_RUNTIME_DIR="/run/user/${other_uid}" \
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/${other_uid}/bus" \
systemctl --user is-active --quiet snap.openshell.user-gateway.service
! systemctl is-enabled --quiet snap.openshell.system-gateway.service
! systemctl is-active --quiet snap.openshell.system-gateway.service
journalctl --user -u snap.openshell.user-gateway.service --no-pager |
Expand Down
34 changes: 27 additions & 7 deletions docs/about/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,22 @@ The snap does not migrate existing Debian, RPM, or Homebrew installs. Remove any

```shell
sudo snap install openshell
systemctl --user enable --now snap.openshell.user-gateway.service
```

The user gateway is disabled globally by default. The install script enables it only for the user who ran the script. After a direct snap installation, choose one user account to run the gateway and enable it with the command above. Do not enable the gateway for multiple users who may be logged in at the same time: every user gateway binds to `127.0.0.1:17670`, so only one can run on the system at a time.

Older OpenShell snap revisions could enable the user gateway globally, causing it to start for every user. Remove that global enablement once, then enable the gateway from the account that will run it:

```shell
sudo systemctl disable --global snap.openshell.user-gateway.service
systemctl --user enable --now snap.openshell.user-gateway.service
```

The global command prevents automatic activation for other users but does not stop gateways already running in their user sessions. Run the following command from each other affected user's session:

```shell
systemctl --user disable --now snap.openshell.user-gateway.service
```

The snap installs the standalone policy prover as `openshell.prover`. The
Expand Down Expand Up @@ -187,6 +203,7 @@ To use the user gateway, start it and replace the previous CLI registration afte

```shell
sudo snap set openshell gateway-mode=user
systemctl --user enable --now snap.openshell.user-gateway.service
systemctl --user is-active snap.openshell.user-gateway.service
openshell gateway list
old_registration="NAME_FROM_GATEWAY_LIST"
Expand All @@ -195,6 +212,8 @@ openshell gateway add https://127.0.0.1:17670 --local --name openshell
openshell status
```

Setting `gateway-mode=user` disables the system gateway but does not enable or disable any user's gateway service. Enable the service for only one selected user. To hand the gateway to another user, first run `systemctl --user disable --now snap.openshell.user-gateway.service` from the current gateway user's session, then run the enable command from the new user's session. Otherwise, the two services will compete for `127.0.0.1:17670`.

To use the system gateway, start it, wait for it to generate its root-owned credentials, then copy its client bundle and replace the previous CLI registration:

```shell
Expand Down Expand Up @@ -225,7 +244,7 @@ sudo snap connect openshell:log-observe
sudo snap connect openshell:system-observe
sudo snap connect openshell:docker :docker
systemctl --user reset-failed snap.openshell.user-gateway.service
snap start --user openshell.user-gateway
systemctl --user enable --now snap.openshell.user-gateway.service
```

For an installation upgraded from a legacy system gateway, use `sudo systemctl reset-failed snap.openshell.system-gateway.service` and `sudo snap restart openshell.system-gateway` instead.
Expand Down Expand Up @@ -254,6 +273,13 @@ brew uninstall nvidia/openshell/openshell
rm -rf "$(brew --prefix)/var/openshell"
```

Snap:

```shell
systemctl --user disable --now snap.openshell.user-gateway.service
sudo snap remove --purge openshell
```

Debian and Ubuntu:

```shell
Expand All @@ -270,12 +296,6 @@ sudo dnf remove openshell-gateway openshell-prover openshell
rm -rf "${XDG_STATE_HOME:-$HOME/.local/state}/openshell"
```

Snap:

```shell
sudo snap remove --purge openshell
```

Remove any custom config or database set through `OPENSHELL_GATEWAY_CONFIG` or `OPENSHELL_DB_URL` separately.

## Next Steps
Expand Down
16 changes: 10 additions & 6 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1493,12 +1493,16 @@ Install Docker Engine from a system package or Docker's package repository, then
if [ "$_gateway_mode" = system ]; then
warn "restarting the OpenShell gateway to use the refreshed snap; active sandbox sessions will be interrupted"
as_root snap restart openshell.system-gateway
elif [ "$_existing_snap" = true ]; then
warn "restarting the OpenShell user gateway to use the refreshed snap; active sandbox sessions will be interrupted"
# as root, snap restart automatically restarts the given user services for
# all active users (there should just be one), and is forwards-compatible
# with planned changes to the snap user daemon commands.
as_root snap restart openshell.user-gateway
else
# In case refreshing from an old snap which had a globally-enabled user
# gateway service, globally disable it, then just enable for the target user
as_root systemctl --global disable snap.openshell.user-gateway.service
as_target_user systemctl --user daemon-reload
as_target_user systemctl --user enable --now snap.openshell.user-gateway.service
if [ "$_existing_snap" = true ]; then
warn "restarting the OpenShell user gateway to use the refreshed snap; active sandbox sessions will be interrupted"
as_target_user systemctl --user restart snap.openshell.user-gateway.service
fi
fi

info "registering local gateway as ${TARGET_USER}..."
Expand Down
2 changes: 1 addition & 1 deletion skills/debug-openshell-cluster/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Common findings:
- `No active gateway`: register one with `openshell gateway add <endpoint>`.
- Connection refused: gateway process is not running, service exposure is wrong, or a port-forward/proxy is not active.
- TLS/certificate errors: the endpoint scheme or trust chain is wrong, a CLI mTLS bundle does not match the gateway CA, a supervisor is missing the gateway CA, or TLS termination does not match the gateway listener. Workloads and supervisors should not contain a user TLS client certificate or private key.
- Fresh snap installations use `openshell.user-gateway`; upgraded installations retain their system-owned state through `openshell.system-gateway`. Existing HTTPS registrations continue to work after the service rename. Follow the published snap installation steps to enroll a new user, replace an old HTTP registration, or change modes safely through the `disable` maintenance state. If `gateway-mode=disable`, no local snap gateway is expected to run.
- Fresh snap installations select `gateway-mode=user`, but the user gateway is globally disabled by default. `install.sh` enables `snap.openshell.user-gateway.service` only for its target user; direct snap installations must select one user and run `systemctl --user enable --now snap.openshell.user-gateway.service` from that user's session. Do not enable it for multiple concurrently logged-in users: every instance binds to `127.0.0.1:17670`, so later instances fail with an address-in-use error. Older snap revisions could leave the unit globally enabled; remove that state with `sudo systemctl disable --global snap.openshell.user-gateway.service`, then disable and stop the service from every other affected user's session. The global command does not stop running user services. Setting `gateway-mode=user` disables the system gateway without changing any user's service. To change the selected user, disable and stop the current user's unit before enabling the new user's unit. Upgraded installations retain their system-owned state through `openshell.system-gateway`. Existing HTTPS registrations continue to work after the service rename. Follow the published snap installation steps to enroll a new user, replace an old HTTP registration, or change modes safely through the `disable` maintenance state. If `gateway-mode=disable`, no local snap gateway is expected to run.
- `Unauthenticated` from an edge or OIDC gateway: refresh stored credentials with `openshell gateway login [name]`, then retry. Use `gateway logout` only when intentionally clearing local credentials.
- Operator credential retrieval requires direct gateway mTLS, `[openshell.gateway.mtls_auth] operator_enabled = true`, and a verified certificate with exact `OU=operator`. OIDC admin roles and forwarded certificate headers do not grant access. Do not combine the operator certificate with an `Authorization` header. Enabling this role grants gateway-wide administration; never repair a denial by issuing operator certificates to ordinary users. See the published gateway authentication and provider references.
- A direct development endpoint with a private or self-signed certificate can be isolated with `--gateway-endpoint <url> --gateway-insecure`; do not persist or recommend insecure verification for shared gateways.
Expand Down
7 changes: 4 additions & 3 deletions snap/hooks/configure
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# Apply the configured gateway service model. The disabled mode provides a
# safe intermediate state while changing between user and system gateways.
# Apply the configured gateway service model. User gateway activation belongs
# to each user's systemd manager; user mode only disables the conflicting
# system gateway. The disabled mode provides a safe intermediate state while
# changing between user and system gateways.

set -eu

gateway_mode=$(snapctl get gateway-mode)
case "$gateway_mode" in
user)
snapctl stop --disable "${SNAP_INSTANCE_NAME}.system-gateway"
snapctl start --enable "${SNAP_INSTANCE_NAME}.user-gateway"
;;
system)
snapctl stop --disable "${SNAP_INSTANCE_NAME}.user-gateway"
Expand Down
4 changes: 2 additions & 2 deletions snap/hooks/install
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# Fresh installations use the per-user gateway. The configure hook applies the
# service selection after this transaction commits.
# Fresh installations select the per-user gateway model. Each user enables the
# gateway through their own systemd user manager.

set -eu

Expand Down
3 changes: 2 additions & 1 deletion snap/hooks/post-refresh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
# One-time migration for installations that predate per-user snap gateways.
# Existing installations retain the system gateway; fresh installations record
# gateway-mode=user in the install hook and skip this migration. The configure
# hook applies the selected service after this transaction commits.
# hook applies system and disabled modes after this transaction commits. User
# gateway activation remains owned by each user's systemd manager.

set -eu

Expand Down
23 changes: 21 additions & 2 deletions snapcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ description: |

The OpenShell snap ships a CLI (`openshell`), a terminal UI
(`openshell.term`), a standalone policy prover (`openshell.prover`), and a
managed gateway daemon (`openshell.user-gateway`).
managed gateway daemon (`openshell.user-gateway`). The user gateway is
disabled by default so that one selected user can run it.

**Setup instructions**

Expand All @@ -23,12 +24,30 @@ description: |

Snap Store installations automatically connect the required interfaces.

Enable and start the gateway from the single user account that will run
it:

systemctl --user enable --now snap.openshell.user-gateway.service

Do not enable the gateway for multiple users who may be logged in at the
same time. Every user gateway binds to 127.0.0.1:17670, so only one can
run on the system at a time.

Older OpenShell snap revisions could enable the user gateway globally.
When upgrading such an installation, remove that global enablement so
the gateway does not start automatically for other users:

sudo systemctl disable --global snap.openshell.user-gateway.service

This does not stop gateways already running in other user sessions.
Those users must disable and stop the service from their own sessions.

If the OpenShell snap was installed before Docker was running, its user
service may have hit the systemd start limit. Resolve this from the
affected user's session after Docker is available:

systemctl --user reset-failed snap.openshell.user-gateway.service
snap start --user openshell.user-gateway
systemctl --user restart snap.openshell.user-gateway.service

2. Register the per-user gateway after it provisions its mTLS credentials:

Expand Down
8 changes: 7 additions & 1 deletion tasks/scripts/test-install-sh.sh
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,10 @@ assert_snap_user_refresh_flow() {
calls="$(cat "$out")"
expected="wait:user-docker
root:snap refresh openshell --channel=latest/stable
root:snap restart openshell.user-gateway
root:systemctl --global disable snap.openshell.user-gateway.service
target:systemctl --user daemon-reload
target:systemctl --user enable --now snap.openshell.user-gateway.service
target:systemctl --user restart snap.openshell.user-gateway.service
wait:user-gateway-listener
register:user-gateway
wait:gateway-status"
Expand All @@ -382,6 +385,9 @@ assert_snap_install_flow \
1 0 "" \
"wait:user-docker
root:snap install openshell --channel=latest/stable
root:systemctl --global disable snap.openshell.user-gateway.service
target:systemctl --user daemon-reload
target:systemctl --user enable --now snap.openshell.user-gateway.service
wait:user-gateway-listener
register:user-gateway
wait:gateway-status"
Expand Down
26 changes: 24 additions & 2 deletions tasks/scripts/test-packaging-assets.sh
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,17 @@ assert_contains "$snapcraft" 'daemon-scope: system'
assert_contains "$snapcraft" 'daemon-scope: user'
assert_contains "$snapcraft" 'install-mode: disable'
assert_not_contains "$snapcraft" 'install-mode: enable'
for app in system-gateway user-gateway; do
if ! awk -v app="$app" '
$0 == " " app ":" { found = 1; in_app = 1; next }
in_app && /^ [[:alnum:]_-]+:$/ { exit !disabled }
in_app && /install-mode: disable/ { disabled = 1 }
END { exit !(found && disabled) }
' "$snapcraft"; then
echo "FAIL: snap ${app} must use install-mode: disable" >&2
exit 1
fi
done
if grep -Eq '^ gateway:$' "$snapcraft"; then
echo "FAIL: removed snap gateway app must not remain declared" >&2
exit 1
Expand Down Expand Up @@ -219,8 +230,19 @@ assert_not_contains "$snap_install_docs" "snap connect openshell:network"
assert_not_contains "$snap_install_docs" "snap connect openshell:network-bind"
assert_contains "$snap_install_docs" "snap connect openshell:docker :docker"
assert_contains "$snap_install_docs" "systemctl --user reset-failed snap.openshell.user-gateway.service"
assert_contains "$snap_install_docs" "snap start --user openshell.user-gateway"
assert_contains "$snapcraft" "snap start --user openshell.user-gateway"
assert_contains "$snap_install_docs" "systemctl --user enable --now snap.openshell.user-gateway.service"
assert_contains "$snapcraft" "systemctl --user enable --now snap.openshell.user-gateway.service"
assert_contains "$ROOT/install.sh" "systemctl --user enable --now snap.openshell.user-gateway.service"
assert_contains "$ROOT/install.sh" "systemctl --global disable snap.openshell.user-gateway.service"
assert_contains "$snap_install_docs" 'every user gateway binds to `127.0.0.1:17670`'
assert_contains "$snap_install_docs" "systemctl --user disable --now snap.openshell.user-gateway.service"
assert_contains "$snapcraft" "Every user gateway binds to 127.0.0.1:17670"
assert_contains "$snap_install_docs" "sudo systemctl disable --global snap.openshell.user-gateway.service"
assert_contains "$snapcraft" "sudo systemctl disable --global snap.openshell.user-gateway.service"
for snap_file in "$ROOT/install.sh" "$snapcraft" "$snap_install_docs"; do
assert_not_contains "$snap_file" "snap start --user openshell.user-gateway"
assert_not_contains "$snap_file" "snap restart openshell.user-gateway"
done
assert_contains "$snap_install_docs" "openshell.user-gateway"
assert_contains "$snap_install_docs" "openshell.system-gateway"
assert_contains "$snap_install_docs" "Existing HTTPS registrations continue to work"
Expand Down
6 changes: 3 additions & 3 deletions tasks/scripts/test-snap-configure-hook.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ EOF
}

run_hook user
expected=$'stop --disable openshell.system-gateway\nstart --enable openshell.user-gateway'
expected='stop --disable openshell.system-gateway'
[[ $(cat "$work/snapctl.log") == "$expected" ]]

run_hook system
Expand Down Expand Up @@ -61,7 +61,7 @@ fi

cat >"$work/failure-bin/snapctl" <<EOF
#!/bin/sh
if [ "\${1:-}" = get ]; then printf '%s\n' user; exit 0; fi
if [ "\${1:-}" = get ]; then printf '%s\n' system; exit 0; fi
printf '%s\n' "\$*" >>'$work/start-failure.log'
[ "\${1:-}" != start ]
EOF
Expand All @@ -70,7 +70,7 @@ if PATH="$work/failure-bin:$PATH" SNAP_INSTANCE_NAME=openshell "$hook"; then
echo "FAIL: configure hook ignored selected-service start failure" >&2
exit 1
fi
expected=$'stop --disable openshell.system-gateway\nstart --enable openshell.user-gateway'
expected=$'stop --disable openshell.user-gateway\nstart --enable openshell.system-gateway'
[[ $(cat "$work/start-failure.log") == "$expected" ]]

echo "Snap configure hook tests passed"
Loading