Skip to content
Open
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -212,5 +212,9 @@ rfc.md
*.tar.xz
*.tar.bz2

# Snap build artifacts
*.snap
*.comp

# Markdown/mermaid lint tooling deps
scripts/lint-mermaid/node_modules/
183 changes: 183 additions & 0 deletions deploy/snap/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# Building a snap package

OpenShell snap packages are defined by the root `snapcraft.yaml` and built with
Snapcraft from source.

The helper task under `tasks/` still stages the same payload from pre-built
binaries when you want to inspect the snap root or produce local artifacts.

Comment on lines +1 to +8
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the description.

## Prerequisites

- Linux on `amd64` or `arm64`
- `snap` from `snapd`
- `snapcraft`
- Docker from the Docker snap (`sudo snap install docker`)

## Build with Snapcraft

Build the snap from source with the root manifest:

```shell
snapcraft pack
```

The manifest builds the Rust binaries inside Snapcraft, installs the CLI,
gateway, and sandbox supervisor into the snap, and keeps the same runtime
environment as the current deployment logic.

## Staged helper flow

The helper task under `tasks/` still stages the same payload from pre-built
binaries when you want to inspect the snap root or produce local artifacts.

For that flow, install `mise` and build:

- `openshell`
- `openshell-gateway`
- `openshell-sandbox`

## Build helper binaries

Build the release binaries used by the staged helper flow:

```shell
mise run build:rust:snap
```

This convenience target builds the CLI with `bundled-z3`, the gateway, and
`openshell-sandbox` for the Docker driver to bind-mount into sandbox containers.

## Pack the snap

Run the packaging hook through mise:

```shell
VERSION="$(uv run python tasks/scripts/release.py get-version --snap)"

OPENSHELL_CLI_BINARY="$PWD/target/release/openshell" \
OPENSHELL_GATEWAY_BINARY="$PWD/target/release/openshell-gateway" \
OPENSHELL_DOCKER_SUPERVISOR_BINARY="$PWD/target/release/openshell-sandbox" \
OPENSHELL_SNAP_VERSION="$VERSION" \
OPENSHELL_OUTPUT_DIR=artifacts \
mise run package:snap
```

The artifact is written to `artifacts/openshell_${VERSION}_${ARCH}.snap`. The
packaging hook fails before `snap pack` if `openshell-sandbox` is missing or not
executable.

## Stage without packing

To inspect the snap root without running `snap pack`:

```shell
VERSION="$(uv run python tasks/scripts/release.py get-version --snap)"

OPENSHELL_CLI_BINARY="$PWD/target/release/openshell" \
OPENSHELL_GATEWAY_BINARY="$PWD/target/release/openshell-gateway" \
OPENSHELL_DOCKER_SUPERVISOR_BINARY="$PWD/target/release/openshell-sandbox" \
OPENSHELL_SNAP_VERSION="$VERSION" \
mise run package:snap:stage
```

The staged root is written to `artifacts/snap-root`.

## Commands in the snap

The snap exposes the CLI:

- `openshell`

It also defines a system service running the gateway with the Docker driver.

- `openshell.gateway`

The gateway service uses `refresh-mode: endure` so snap refreshes do not restart
it while sandboxes are active. Restart the service manually when you are ready
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 passes it to the in-process Docker driver
through `OPENSHELL_DOCKER_SUPERVISOR_BIN=$SNAP/bin/openshell-sandbox`. The
service stores its gateway database under `$SNAP_COMMON`.

## Interfaces

The `openshell` CLI app plugs:

- `home`
- `network`
- `ssh-keys`
- `system-observe`

The `openshell.gateway` service plugs:

- `docker`
- `log-observe`
- `network`
- `network-bind`
- `ssh-keys`
- `system-observe`

## Start a Docker gateway from the snap

The snapped gateway talks to Docker through the Docker snap's
`docker:docker-daemon` slot. The snap declares `default-provider: docker` on
its Docker plug so snapd can install the Docker snap when OpenShell is
installed. Connect the interface before using the Docker driver:

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

The gateway uses Docker's default Unix socket location. The Docker snap exposes
that socket through the connected `docker` interface, so no `DOCKER_HOST`
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:

```shell
openshell.gateway \
--drivers docker \
--disable-tls \
--port 17670 \
--db-url "sqlite:$SNAP_COMMON/gateway.db?mode=rwc" \
--docker-supervisor-bin "$SNAP/bin/openshell-sandbox" \
--docker-network-name openshell-snap \
--sandbox-namespace docker-snap \
--sandbox-image ghcr.io/nvidia/openshell-community/sandboxes/base:latest \
--sandbox-image-pull-policy IfNotPresent \
--grpc-endpoint http://host.openshell.internal:17670
```

This stores the gateway SQLite database at
`/var/snap/openshell/common/gateway.db`.

## Connect with the OpenShell CLI

Register the snap-run gateway as a local plaintext gateway:

```shell
openshell gateway add http://127.0.0.1:17670 --local --name snap-docker
openshell gateway select snap-docker
openshell status
```

Then use normal sandbox commands:

```shell
openshell sandbox create --name demo
openshell sandbox connect demo
```

To avoid changing the default gateway, pass the gateway name per command:

```shell
openshell --gateway snap-docker status
openshell --gateway snap-docker sandbox create --name demo
```
66 changes: 66 additions & 0 deletions deploy/snap/meta/snap.yaml.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

name: openshell
title: OpenShell
version: "@VERSION@"
summary: Safe, sandboxed runtimes for autonomous AI agents
description: |
OpenShell provides safe, sandboxed runtimes for autonomous AI agents.
It offers a CLI for managing gateways, sandboxes, and providers with
policy-enforced egress routing, credential proxying, and privacy-aware
LLM inference routing.

base: "@BASE@"
grade: "@GRADE@"
confinement: strict
license: Apache-2.0
website: https://docs.nvidia.com/openshell/latest/index.html
source-code: https://github.com/NVIDIA/OpenShell
issues: https://github.com/NVIDIA/OpenShell/issues
contact: https://github.com/NVIDIA/OpenShell/security/policy
architectures:
- "@ARCH@"

apps:
openshell:
command: bin/openshell
plugs:
- home
- network
- ssh-keys
- system-observe
gateway:
command: bin/openshell-gateway
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_GRPC_ENDPOINT: http://host.openshell.internal:17670
OPENSHELL_DISABLE_TLS: true
OPENSHELL_DRIVERS: docker
OPENSHELL_DOCKER_SUPERVISOR_BIN: "$SNAP/bin/openshell-sandbox"
OPENSHELL_DOCKER_NETWORK_NAME: openshell-snap
OPENSHELL_SANDBOX_IMAGE: ghcr.io/nvidia/openshell-community/sandboxes/base:latest
OPENSHELL_SANDBOX_IMAGE_PULL_POLICY: IfNotPresent
OPENSHELL_SANDBOX_SSH_PORT: 2222
OPENSHELL_SSH_GATEWAY_HOST: 127.0.0.1
OPENSHELL_SSH_GATEWAY_PORT: 8080
XDG_DATA_HOME: "$SNAP_COMMON"
# Used for creating and locating certain sockets.
XDG_RUNTIME_DIR: "$SNAP_COMMON"

plugs:
- docker
- log-observe
- network
- network-bind
- ssh-keys
- system-observe

plugs:
docker:
interface: docker
default-provider: docker
102 changes: 102 additions & 0 deletions snapcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

name: openshell
title: OpenShell
adopt-info: openshell
summary: Safe, sandboxed runtimes for autonomous AI agents
description: |
OpenShell provides safe, sandboxed runtimes for autonomous AI agents.
It offers a CLI for managing gateways, sandboxes, and providers with
policy-enforced egress routing, credential proxying, and privacy-aware
LLM inference routing.

base: core24
# TODO: this prevents publishing a snap to latest/stable.
grade: devel
confinement: strict
license: Apache-2.0
website: https://docs.nvidia.com/openshell/latest/index.html
source-code: https://github.com/NVIDIA/OpenShell
issues: https://github.com/NVIDIA/OpenShell/issues
contact: https://github.com/NVIDIA/OpenShell/security/policy
platforms:
amd64:
build-on: [amd64]
build-for: [amd64]
arm64:
build-on: [arm64]
build-for: [arm64]

apps:
openshell:
command: bin/openshell
plugs:
- home
- network
- ssh-keys
- system-observe
gateway:
command: bin/openshell-gateway
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_GRPC_ENDPOINT: http://host.openshell.internal:17670
OPENSHELL_DISABLE_TLS: "true"
OPENSHELL_DRIVERS: docker
OPENSHELL_DOCKER_SUPERVISOR_BIN: "$SNAP/bin/openshell-sandbox"
OPENSHELL_DOCKER_NETWORK_NAME: openshell-snap
OPENSHELL_SANDBOX_IMAGE: ghcr.io/nvidia/openshell-community/sandboxes/base:latest
OPENSHELL_SANDBOX_IMAGE_PULL_POLICY: IfNotPresent
OPENSHELL_SANDBOX_SSH_PORT: 2222
OPENSHELL_SSH_GATEWAY_HOST: 127.0.0.1
OPENSHELL_SSH_GATEWAY_PORT: 8080
XDG_DATA_HOME: "$SNAP_COMMON"
XDG_RUNTIME_DIR: "$SNAP_COMMON"
plugs:
- docker
- log-observe
- network
- network-bind
- ssh-keys
- system-observe

parts:
openshell:
plugin: rust
rust-channel: "1.95.0"
source: .
build-packages:
- build-essential
- ca-certificates
- clang
- cmake
- git
- libclang-dev
- libssl-dev
- libz3-dev
- pkg-config
- python3
override-pull: |
craftctl default
craftctl set version="$(python3 "$CRAFT_PROJECT_DIR/tasks/scripts/release.py" get-version --snap)"
override-build: |
set -euo pipefail

cargo build --release --locked -p openshell-cli --features bundled-z3
cargo build --release --locked -p openshell-server --bin openshell-gateway
cargo build --release --locked -p openshell-sandbox --bin openshell-sandbox

install -D -m 0755 "$CRAFT_PROJECT_DIR/target/release/openshell" \
"$CRAFT_PART_INSTALL/bin/openshell"
install -D -m 0755 "$CRAFT_PROJECT_DIR/target/release/openshell-gateway" \
"$CRAFT_PART_INSTALL/bin/openshell-gateway"
install -D -m 0755 "$CRAFT_PROJECT_DIR/target/release/openshell-sandbox" \
"$CRAFT_PART_INSTALL/bin/openshell-sandbox"
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" \
"$CRAFT_PART_INSTALL/usr/share/doc/openshell/README.md"
8 changes: 8 additions & 0 deletions tasks/ci.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ description = "Build all Rust crates in release mode"
run = "cargo build --workspace --release"
hide = true

["build:rust:snap"]
description = "Build release Rust binaries consumed by the hand-staged snap"
run = [
"cargo build --release -p openshell-cli --features bundled-z3",
"cargo build --release -p openshell-server",
"cargo build --release -p openshell-sandbox",
]

[check]
description = "Run fast compile and type checks"
depends = ["rust:check", "python:typecheck"]
Expand Down
Loading
Loading