Skip to content

Commit dfd4768

Browse files
authored
(feat) early snap support (#1238)
1 parent 977be31 commit dfd4768

8 files changed

Lines changed: 616 additions & 0 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,5 +212,9 @@ rfc.md
212212
*.tar.xz
213213
*.tar.bz2
214214

215+
# Snap build artifacts
216+
*.snap
217+
*.comp
218+
215219
# Markdown/mermaid lint tooling deps
216220
scripts/lint-mermaid/node_modules/

deploy/snap/README.md

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
# Building a snap package
2+
3+
OpenShell snap packages are defined by the root `snapcraft.yaml` and built with
4+
Snapcraft from source.
5+
6+
The helper task under `tasks/` still stages the same payload from pre-built
7+
binaries when you want to inspect the snap root or produce local artifacts.
8+
9+
## Prerequisites
10+
11+
- Linux on `amd64` or `arm64`
12+
- `snap` from `snapd`
13+
- `snapcraft`
14+
- Docker from the Docker snap (`sudo snap install docker`)
15+
16+
## Build with Snapcraft
17+
18+
Build the snap from source with the root manifest:
19+
20+
```shell
21+
snapcraft pack
22+
```
23+
24+
The manifest builds the Rust binaries inside Snapcraft, installs the CLI,
25+
gateway, and sandbox supervisor into the snap, and keeps the same runtime
26+
environment as the current deployment logic.
27+
28+
## Staged helper flow
29+
30+
The helper task under `tasks/` still stages the same payload from pre-built
31+
binaries when you want to inspect the snap root or produce local artifacts.
32+
33+
For that flow, install `mise` and build:
34+
35+
- `openshell`
36+
- `openshell-gateway`
37+
- `openshell-sandbox`
38+
39+
## Build helper binaries
40+
41+
Build the release binaries used by the staged helper flow:
42+
43+
```shell
44+
mise run build:rust:snap
45+
```
46+
47+
This convenience target builds the CLI with `bundled-z3`, the gateway, and
48+
`openshell-sandbox` for the Docker driver to bind-mount into sandbox containers.
49+
50+
## Pack the snap
51+
52+
Run the packaging hook through mise:
53+
54+
```shell
55+
VERSION="$(uv run python tasks/scripts/release.py get-version --snap)"
56+
57+
OPENSHELL_CLI_BINARY="$PWD/target/release/openshell" \
58+
OPENSHELL_GATEWAY_BINARY="$PWD/target/release/openshell-gateway" \
59+
OPENSHELL_DOCKER_SUPERVISOR_BINARY="$PWD/target/release/openshell-sandbox" \
60+
OPENSHELL_SNAP_VERSION="$VERSION" \
61+
OPENSHELL_OUTPUT_DIR=artifacts \
62+
mise run package:snap
63+
```
64+
65+
The artifact is written to `artifacts/openshell_${VERSION}_${ARCH}.snap`. The
66+
packaging hook fails before `snap pack` if `openshell-sandbox` is missing or not
67+
executable.
68+
69+
## Stage without packing
70+
71+
To inspect the snap root without running `snap pack`:
72+
73+
```shell
74+
VERSION="$(uv run python tasks/scripts/release.py get-version --snap)"
75+
76+
OPENSHELL_CLI_BINARY="$PWD/target/release/openshell" \
77+
OPENSHELL_GATEWAY_BINARY="$PWD/target/release/openshell-gateway" \
78+
OPENSHELL_DOCKER_SUPERVISOR_BINARY="$PWD/target/release/openshell-sandbox" \
79+
OPENSHELL_SNAP_VERSION="$VERSION" \
80+
mise run package:snap:stage
81+
```
82+
83+
The staged root is written to `artifacts/snap-root`.
84+
85+
## Commands in the snap
86+
87+
The snap exposes the CLI:
88+
89+
- `openshell`
90+
91+
It also defines a system service running the gateway with the Docker driver.
92+
93+
- `openshell.gateway`
94+
95+
The gateway service uses `refresh-mode: endure` so snap refreshes do not restart
96+
it while sandboxes are active. Restart the service manually when you are ready
97+
to move the gateway to the refreshed snap revision.
98+
99+
`openshell-sandbox` is staged next to `openshell-gateway` as the Docker
100+
supervisor binary. The gateway app passes it to the in-process Docker driver
101+
through `OPENSHELL_DOCKER_SUPERVISOR_BIN=$SNAP/bin/openshell-sandbox`. The
102+
service stores its gateway database under `$SNAP_COMMON`.
103+
104+
## Interfaces
105+
106+
The `openshell` CLI app plugs:
107+
108+
- `home`
109+
- `network`
110+
- `ssh-keys`
111+
- `system-observe`
112+
113+
The `openshell.gateway` service plugs:
114+
115+
- `docker`
116+
- `log-observe`
117+
- `network`
118+
- `network-bind`
119+
- `ssh-keys`
120+
- `system-observe`
121+
122+
## Start a Docker gateway from the snap
123+
124+
The snapped gateway talks to Docker through the Docker snap's
125+
`docker:docker-daemon` slot. The snap declares `default-provider: docker` on
126+
its Docker plug so snapd can install the Docker snap when OpenShell is
127+
installed. Connect the interface before using the Docker driver:
128+
129+
```shell
130+
sudo snap connect openshell:docker docker:docker-daemon
131+
sudo snap connect openshell:log-observe
132+
sudo snap connect openshell:system-observe
133+
sudo snap connect openshell:ssh-keys
134+
```
135+
136+
The gateway uses Docker's default Unix socket location. The Docker snap exposes
137+
that socket through the connected `docker` interface, so no `DOCKER_HOST`
138+
override is required. The OpenShell snap still requires the Docker snap because
139+
it relies on the `docker:docker-daemon` slot; it does not work with Docker
140+
installed from a Debian package or Docker's upstream packages.
141+
142+
The service runs the gateway with the Docker driver enabled:
143+
144+
```shell
145+
openshell.gateway \
146+
--drivers docker \
147+
--disable-tls \
148+
--port 17670 \
149+
--db-url "sqlite:$SNAP_COMMON/gateway.db?mode=rwc" \
150+
--docker-supervisor-bin "$SNAP/bin/openshell-sandbox" \
151+
--docker-network-name openshell-snap \
152+
--sandbox-namespace docker-snap \
153+
--sandbox-image ghcr.io/nvidia/openshell-community/sandboxes/base:latest \
154+
--sandbox-image-pull-policy IfNotPresent \
155+
--grpc-endpoint http://host.openshell.internal:17670
156+
```
157+
158+
This stores the gateway SQLite database at
159+
`/var/snap/openshell/common/gateway.db`.
160+
161+
## Connect with the OpenShell CLI
162+
163+
Register the snap-run gateway as a local plaintext gateway:
164+
165+
```shell
166+
openshell gateway add http://127.0.0.1:17670 --local --name snap-docker
167+
openshell gateway select snap-docker
168+
openshell status
169+
```
170+
171+
Then use normal sandbox commands:
172+
173+
```shell
174+
openshell sandbox create --name demo
175+
openshell sandbox connect demo
176+
```
177+
178+
To avoid changing the default gateway, pass the gateway name per command:
179+
180+
```shell
181+
openshell --gateway snap-docker status
182+
openshell --gateway snap-docker sandbox create --name demo
183+
```

deploy/snap/meta/snap.yaml.in

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
name: openshell
5+
title: OpenShell
6+
version: "@VERSION@"
7+
summary: Safe, sandboxed runtimes for autonomous AI agents
8+
description: |
9+
OpenShell provides safe, sandboxed runtimes for autonomous AI agents.
10+
It offers a CLI for managing gateways, sandboxes, and providers with
11+
policy-enforced egress routing, credential proxying, and privacy-aware
12+
LLM inference routing.
13+
14+
base: "@BASE@"
15+
grade: "@GRADE@"
16+
confinement: strict
17+
license: Apache-2.0
18+
website: https://docs.nvidia.com/openshell/latest/index.html
19+
source-code: https://github.com/NVIDIA/OpenShell
20+
issues: https://github.com/NVIDIA/OpenShell/issues
21+
contact: https://github.com/NVIDIA/OpenShell/security/policy
22+
architectures:
23+
- "@ARCH@"
24+
25+
apps:
26+
openshell:
27+
command: bin/openshell
28+
plugs:
29+
- home
30+
- network
31+
- ssh-keys
32+
- system-observe
33+
gateway:
34+
command: bin/openshell-gateway
35+
daemon: simple
36+
refresh-mode: endure
37+
environment:
38+
OPENSHELL_BIND_ADDRESS: 127.0.0.1
39+
OPENSHELL_SERVER_PORT: 17670
40+
OPENSHELL_DB_URL: "sqlite:$SNAP_COMMON/gateway.db?mode=rwc"
41+
OPENSHELL_GRPC_ENDPOINT: http://host.openshell.internal:17670
42+
OPENSHELL_DISABLE_TLS: true
43+
OPENSHELL_DRIVERS: docker
44+
OPENSHELL_DOCKER_SUPERVISOR_BIN: "$SNAP/bin/openshell-sandbox"
45+
OPENSHELL_DOCKER_NETWORK_NAME: openshell-snap
46+
OPENSHELL_SANDBOX_IMAGE: ghcr.io/nvidia/openshell-community/sandboxes/base:latest
47+
OPENSHELL_SANDBOX_IMAGE_PULL_POLICY: IfNotPresent
48+
OPENSHELL_SANDBOX_SSH_PORT: 2222
49+
OPENSHELL_SSH_GATEWAY_HOST: 127.0.0.1
50+
OPENSHELL_SSH_GATEWAY_PORT: 8080
51+
XDG_DATA_HOME: "$SNAP_COMMON"
52+
# Used for creating and locating certain sockets.
53+
XDG_RUNTIME_DIR: "$SNAP_COMMON"
54+
55+
plugs:
56+
- docker
57+
- log-observe
58+
- network
59+
- network-bind
60+
- ssh-keys
61+
- system-observe
62+
63+
plugs:
64+
docker:
65+
interface: docker
66+
default-provider: docker

snapcraft.yaml

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
name: openshell
5+
title: OpenShell
6+
adopt-info: openshell
7+
summary: Safe, sandboxed runtimes for autonomous AI agents
8+
description: |
9+
OpenShell provides safe, sandboxed runtimes for autonomous AI agents.
10+
It offers a CLI for managing gateways, sandboxes, and providers with
11+
policy-enforced egress routing, credential proxying, and privacy-aware
12+
LLM inference routing.
13+
14+
base: core24
15+
# TODO: this prevents publishing a snap to latest/stable.
16+
grade: devel
17+
confinement: strict
18+
license: Apache-2.0
19+
website: https://docs.nvidia.com/openshell/latest/index.html
20+
source-code: https://github.com/NVIDIA/OpenShell
21+
issues: https://github.com/NVIDIA/OpenShell/issues
22+
contact: https://github.com/NVIDIA/OpenShell/security/policy
23+
platforms:
24+
amd64:
25+
build-on: [amd64]
26+
build-for: [amd64]
27+
arm64:
28+
build-on: [arm64]
29+
build-for: [arm64]
30+
31+
apps:
32+
openshell:
33+
command: bin/openshell
34+
plugs:
35+
- home
36+
- network
37+
- ssh-keys
38+
- system-observe
39+
gateway:
40+
command: bin/openshell-gateway
41+
daemon: simple
42+
refresh-mode: endure
43+
environment:
44+
OPENSHELL_BIND_ADDRESS: 127.0.0.1
45+
OPENSHELL_SERVER_PORT: 17670
46+
OPENSHELL_DB_URL: "sqlite:$SNAP_COMMON/gateway.db?mode=rwc"
47+
OPENSHELL_GRPC_ENDPOINT: http://host.openshell.internal:17670
48+
OPENSHELL_DISABLE_TLS: "true"
49+
OPENSHELL_DRIVERS: docker
50+
OPENSHELL_DOCKER_SUPERVISOR_BIN: "$SNAP/bin/openshell-sandbox"
51+
OPENSHELL_DOCKER_NETWORK_NAME: openshell-snap
52+
OPENSHELL_SANDBOX_IMAGE: ghcr.io/nvidia/openshell-community/sandboxes/base:latest
53+
OPENSHELL_SANDBOX_IMAGE_PULL_POLICY: IfNotPresent
54+
OPENSHELL_SANDBOX_SSH_PORT: 2222
55+
OPENSHELL_SSH_GATEWAY_HOST: 127.0.0.1
56+
OPENSHELL_SSH_GATEWAY_PORT: 8080
57+
XDG_DATA_HOME: "$SNAP_COMMON"
58+
XDG_RUNTIME_DIR: "$SNAP_COMMON"
59+
plugs:
60+
- docker
61+
- log-observe
62+
- network
63+
- network-bind
64+
- ssh-keys
65+
- system-observe
66+
67+
parts:
68+
openshell:
69+
plugin: rust
70+
rust-channel: "1.95.0"
71+
source: .
72+
build-packages:
73+
- build-essential
74+
- ca-certificates
75+
- clang
76+
- cmake
77+
- git
78+
- libclang-dev
79+
- libssl-dev
80+
- libz3-dev
81+
- pkg-config
82+
- python3
83+
override-pull: |
84+
craftctl default
85+
craftctl set version="$(python3 "$CRAFT_PROJECT_DIR/tasks/scripts/release.py" get-version --snap)"
86+
override-build: |
87+
set -euo pipefail
88+
89+
cargo build --release --locked -p openshell-cli --features bundled-z3
90+
cargo build --release --locked -p openshell-server --bin openshell-gateway
91+
cargo build --release --locked -p openshell-sandbox --bin openshell-sandbox
92+
93+
install -D -m 0755 "$CRAFT_PROJECT_DIR/target/release/openshell" \
94+
"$CRAFT_PART_INSTALL/bin/openshell"
95+
install -D -m 0755 "$CRAFT_PROJECT_DIR/target/release/openshell-gateway" \
96+
"$CRAFT_PART_INSTALL/bin/openshell-gateway"
97+
install -D -m 0755 "$CRAFT_PROJECT_DIR/target/release/openshell-sandbox" \
98+
"$CRAFT_PART_INSTALL/bin/openshell-sandbox"
99+
install -D -m 0644 "$CRAFT_PROJECT_DIR/LICENSE" \
100+
"$CRAFT_PART_INSTALL/usr/share/doc/openshell/LICENSE"
101+
install -D -m 0644 "$CRAFT_PROJECT_DIR/README.md" \
102+
"$CRAFT_PART_INSTALL/usr/share/doc/openshell/README.md"

tasks/ci.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ description = "Build all Rust crates in release mode"
2727
run = "cargo build --workspace --release"
2828
hide = true
2929

30+
["build:rust:snap"]
31+
description = "Build release Rust binaries consumed by the hand-staged snap"
32+
run = [
33+
"cargo build --release -p openshell-cli --features bundled-z3",
34+
"cargo build --release -p openshell-server",
35+
"cargo build --release -p openshell-sandbox",
36+
]
37+
3038
[check]
3139
description = "Run fast compile and type checks"
3240
depends = ["rust:check", "python:typecheck"]

0 commit comments

Comments
 (0)