Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
465287d
feat: add RPM packaging with packit and COPR integration
maxamillion Apr 28, 2026
6604104
fix(rpm): harden gateway systemd units and sysconfig defaults
maxamillion Apr 28, 2026
eb837bc
fix(rpm): auto-generate SSH handshake secret for user unit
maxamillion Apr 28, 2026
842afab
fix(rpm): depend on podman.service instead of podman.socket
maxamillion Apr 28, 2026
20f3e4b
feat(ci): add supervisor-sideload as distinct build and release target
maxamillion Apr 28, 2026
df4d13e
feat(server): make gateway bind address configurable via OPENSHELL_BI…
maxamillion Apr 29, 2026
a2c7436
fix(cli): gateway destroy on non-Docker systems fails looking for doc…
maxamillion Apr 29, 2026
bcdac04
refactor(docker): consolidate supervisor and supervisor-sideload into…
maxamillion Apr 30, 2026
7ac190c
feat(podman): enable mTLS by default for RPM gateway packaging
maxamillion Apr 30, 2026
02bd258
refactor(podman): rename tls_ca/cert/key to guest_tls_ca/cert/key
maxamillion Apr 30, 2026
5fb3a9b
fix(cli): skip cert extraction when TLS certs already exist on disk
maxamillion Apr 30, 2026
af13b9a
fix(cli): warn on http:// registration when mTLS certs exist and vali…
maxamillion Apr 30, 2026
d46c5b7
fix(cli): resolve loopback gateway name for mTLS cert detection
maxamillion Apr 30, 2026
c3ae4b9
fix(podman): resolve mTLS cert path mismatch and SELinux bind-mount d…
maxamillion May 1, 2026
fec4ccb
feat(rpm): overhaul packaging and documentation for Podman/systemd de…
maxamillion May 1, 2026
e7c4151
fix: resolve auto-merge compilation errors from rebase
maxamillion May 1, 2026
051c31c
feat(rpm): add cargo-rpm-macros for bundled crate provides
maxamillion May 1, 2026
dccc44b
feat(ci): add RPM package build and publish via Packit CLI
maxamillion May 1, 2026
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
31 changes: 26 additions & 5 deletions .github/workflows/release-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -636,12 +636,20 @@ jobs:
checkout-ref: ${{ github.sha }}
secrets: inherit

build-rpm:
name: Build RPM Packages
needs: [compute-versions]
uses: ./.github/workflows/rpm-package.yml
with:
checkout-ref: ${{ github.sha }}
secrets: inherit

# ---------------------------------------------------------------------------
# Create / update the dev GitHub Release with CLI binaries and wheels
# ---------------------------------------------------------------------------
release-dev:
name: Release Dev
needs: [compute-versions, build-cli-linux, build-cli-macos, build-gateway-binary-linux, build-gateway-binary-macos, build-supervisor-binary-linux, build-python-wheels-linux, build-python-wheel-macos, build-deb]
needs: [compute-versions, build-cli-linux, build-cli-macos, build-gateway-binary-linux, build-gateway-binary-macos, build-supervisor-binary-linux, build-python-wheels-linux, build-python-wheel-macos, build-deb, build-rpm]
runs-on: build-amd64
timeout-minutes: 10
outputs:
Expand Down Expand Up @@ -684,6 +692,13 @@ jobs:
path: release/
merge-multiple: true

- name: Download RPM package artifacts
uses: actions/download-artifact@v4
with:
pattern: rpm-linux-*
path: release/
merge-multiple: true

- name: Capture wheel filenames
id: wheel_filenames
run: |
Expand All @@ -701,6 +716,7 @@ jobs:
openshell-aarch64-unknown-linux-musl.tar.gz \
openshell-aarch64-apple-darwin.tar.gz \
openshell_*.deb \
openshell-*.rpm \
*.whl > openshell-checksums-sha256.txt
cat openshell-checksums-sha256.txt
sha256sum \
Expand All @@ -713,7 +729,7 @@ jobs:
openshell-sandbox-aarch64-unknown-linux-gnu.tar.gz > openshell-sandbox-checksums-sha256.txt
cat openshell-sandbox-checksums-sha256.txt

- name: Prune stale wheel and deb assets from dev release
- name: Prune stale wheel, deb, and rpm assets from dev release
uses: actions/github-script@v7
env:
WHEEL_VERSION: ${{ needs.compute-versions.outputs.python_version }}
Expand Down Expand Up @@ -745,13 +761,17 @@ jobs:
core.info(` ${String(a.id).padStart(12)} ${a.name}`);
}

// Delete stale wheels
let kept = 0, deleted = 0, debDeleted = 0;
// Delete stale wheels, debs, and rpms
let kept = 0, deleted = 0, debDeleted = 0, rpmDeleted = 0;
for (const asset of assets) {
if (asset.name.endsWith('.deb')) {
core.info(`Deleting stale deb package: ${asset.name} (id=${asset.id})`);
await github.rest.repos.deleteReleaseAsset({ owner, repo, asset_id: asset.id });
debDeleted++;
} else if (asset.name.endsWith('.rpm')) {
core.info(`Deleting stale rpm package: ${asset.name} (id=${asset.id})`);
await github.rest.repos.deleteReleaseAsset({ owner, repo, asset_id: asset.id });
rpmDeleted++;
} else if (asset.name.endsWith('.whl') && asset.name.startsWith(currentPrefix)) {
core.info(`Keeping current wheel: ${asset.name}`);
kept++;
Expand All @@ -761,7 +781,7 @@ jobs:
deleted++;
}
}
core.info(`Summary: kept_wheels=${kept}, deleted_wheels=${deleted}, deleted_debs=${debDeleted}`);
core.info(`Summary: kept_wheels=${kept}, deleted_wheels=${deleted}, deleted_debs=${debDeleted}, deleted_rpms=${rpmDeleted}`);

- name: Move dev tag
run: |
Expand Down Expand Up @@ -793,6 +813,7 @@ jobs:
release/openshell-aarch64-unknown-linux-musl.tar.gz
release/openshell-aarch64-apple-darwin.tar.gz
release/openshell_*.deb
release/openshell-*.rpm
release/openshell-gateway-x86_64-unknown-linux-gnu.tar.gz
release/openshell-gateway-aarch64-unknown-linux-gnu.tar.gz
release/openshell-gateway-aarch64-apple-darwin.tar.gz
Expand Down
19 changes: 18 additions & 1 deletion .github/workflows/release-tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -663,12 +663,20 @@ jobs:
checkout-ref: ${{ inputs.tag || github.ref }}
secrets: inherit

build-rpm:
name: Build RPM Packages
needs: [compute-versions]
uses: ./.github/workflows/rpm-package.yml
with:
checkout-ref: ${{ inputs.tag || github.ref }}
secrets: inherit

# ---------------------------------------------------------------------------
# Create a tagged GitHub Release with CLI binaries and wheels
# ---------------------------------------------------------------------------
release:
name: Release
needs: [compute-versions, build-cli-linux, build-cli-macos, build-gateway-binary-linux, build-gateway-binary-macos, build-supervisor-binary-linux, build-python-wheels-linux, build-python-wheel-macos, tag-ghcr-release, build-deb]
needs: [compute-versions, build-cli-linux, build-cli-macos, build-gateway-binary-linux, build-gateway-binary-macos, build-supervisor-binary-linux, build-python-wheels-linux, build-python-wheel-macos, tag-ghcr-release, build-deb, build-rpm]
runs-on: build-amd64
timeout-minutes: 10
outputs:
Expand Down Expand Up @@ -713,6 +721,13 @@ jobs:
path: release/
merge-multiple: true

- name: Download RPM package artifacts
uses: actions/download-artifact@v4
with:
pattern: rpm-linux-*
path: release/
merge-multiple: true

- name: Capture wheel filenames
id: wheel_filenames
run: |
Expand All @@ -730,6 +745,7 @@ jobs:
openshell-aarch64-unknown-linux-musl.tar.gz \
openshell-aarch64-apple-darwin.tar.gz \
openshell_*.deb \
openshell-*.rpm \
*.whl > openshell-checksums-sha256.txt
cat openshell-checksums-sha256.txt
sha256sum \
Expand Down Expand Up @@ -763,6 +779,7 @@ jobs:
release/openshell-aarch64-unknown-linux-musl.tar.gz
release/openshell-aarch64-apple-darwin.tar.gz
release/openshell_*.deb
release/openshell-*.rpm
release/openshell-gateway-x86_64-unknown-linux-gnu.tar.gz
release/openshell-gateway-aarch64-unknown-linux-gnu.tar.gz
release/openshell-gateway-aarch64-apple-darwin.tar.gz
Expand Down
71 changes: 71 additions & 0 deletions .github/workflows/rpm-package.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

name: RPM Package

on:
workflow_call:
inputs:
checkout-ref:
required: true
type: string

permissions:
contents: read

defaults:
run:
shell: bash

jobs:
build-rpm-linux:
name: Build RPM Package (Linux ${{ matrix.arch }})
strategy:
matrix:
include:
- arch: x86_64
runner: build-amd64
- arch: aarch64
runner: build-arm64
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
container:
image: fedora:latest
steps:
- name: Install build dependencies
run: |
dnf install -y \
packit rpm-build \
rust cargo gcc gcc-c++ make cmake pkg-config \
clang-devel z3-devel systemd-rpm-macros \
pandoc python3-devel git-core \
cargo-rpm-macros

- uses: actions/checkout@v6
with:
ref: ${{ inputs.checkout-ref }}
fetch-depth: 0

- name: Mark workspace safe for git
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Fetch tags
run: git fetch --tags --force

- name: Build RPMs via Packit
run: packit build locally

- name: Collect RPM artifacts
run: |
set -euo pipefail
mkdir -p artifacts
find ~/rpmbuild/RPMS/ -name '*.rpm' -exec cp {} artifacts/ \;
echo "=== Built RPMs ==="
ls -lah artifacts/

- name: Upload RPM artifacts
uses: actions/upload-artifact@v4
with:
name: rpm-linux-${{ matrix.arch }}
path: artifacts/*.rpm
retention-days: 5
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -206,5 +206,11 @@ rfc.md
.worktrees
.z3-trace

# RPM build artifacts
*.src.rpm
*.tar.gz
*.tar.xz
*.tar.bz2

# Markdown/mermaid lint tooling deps
scripts/lint-mermaid/node_modules/
6 changes: 5 additions & 1 deletion .markdownlint-cli2.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
".opencode/**",
".github/**",
"THIRD-PARTY-NOTICES/**",
"CLAUDE.md"
"CLAUDE.md",
// Man page sources use pandoc markdown with multiple H1 sections
// (NAME, SYNOPSIS, DESCRIPTION, etc.) which is standard for man
// pages but violates MD025.
"deploy/man/**"
],
"config": {
"default": true,
Expand Down
77 changes: 77 additions & 0 deletions .packit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# Packit configuration for OpenShell RPM builds via Fedora COPR.
# See https://packit.dev/docs/configuration for full reference.

upstream_tag_template: "v{version}"
upstream_package_name: openshell
downstream_package_name: openshell
specfile_path: openshell.spec

# Packages needed in the SRPM build environment to create vendor tarball
srpm_build_deps:
- rust
- cargo
- git-core

actions:
get-current-version:
# Derive version from the latest upstream tag on the current branch.
- 'bash -c "git describe --tags --match ''v*'' --abbrev=0 HEAD | sed ''s/^v//''"'

create-archive:
# Step 1: Create source tarball from git working tree.
# Uses git ls-files + tar instead of git archive so the tarball
# reflects any patching that Packit may have done (e.g. version bumps).
- 'bash -c "VERSION=${PACKIT_PROJECT_VERSION} && TMPDIR=$(mktemp -d) && DIR=openshell-${VERSION} && mkdir -p ${TMPDIR}/${DIR} && git ls-files -z | xargs -0 tar cf - | tar xf - -C ${TMPDIR}/${DIR}/ && tar -czf openshell-${VERSION}.tar.gz -C ${TMPDIR} ${DIR} && rm -rf ${TMPDIR}"'
# Step 2: Create vendored Cargo dependencies tarball for offline RPM build.
- 'bash -c "VERSION=${PACKIT_PROJECT_VERSION} && cargo vendor --quiet && tar -cJf openshell-${VERSION}-vendor.tar.xz vendor/ && rm -rf vendor/"'
# Step 3: Return BOTH archive names. Packit maps each line to Source0, Source1, etc.
- 'bash -c "echo openshell-${PACKIT_PROJECT_VERSION}.tar.gz && echo openshell-${PACKIT_PROJECT_VERSION}-vendor.tar.xz"'

fix-spec-file:
# Update Source0 to the generated tarball name
- 'bash -c "sed -i \"s|^Source0:.*|Source0: openshell-${PACKIT_PROJECT_VERSION}.tar.gz|\" openshell.spec"'
# Update Source1 to the generated vendor tarball name
- 'bash -c "sed -i \"s|^Source1:.*|Source1: openshell-${PACKIT_PROJECT_VERSION}-vendor.tar.xz|\" openshell.spec"'
# Update Version
- 'bash -c "sed -i -r \"s/^Version:(\\s*)\\S+/Version:\\1${PACKIT_RPMSPEC_VERSION}/\" openshell.spec"'
# Update Release
- 'bash -c "sed -i -r \"s/^Release:(\\s*)\\S+/Release:\\1${PACKIT_RPMSPEC_RELEASE}%{?dist}/\" openshell.spec"'

jobs:
# Build on every pull request targeting main for CI validation
- job: copr_build
trigger: pull_request
branch: main
identifier: main-pr
targets:
- fedora-all
- epel-10

# Build into maxamillion/openshell on every commit to main
# for continuous development and testing builds.
- job: copr_build
trigger: commit
branch: main
owner: "maxamillion"
project: "openshell"
identifier: main-commit
targets:
- fedora-all
- epel-10
preserve_project: true
list_on_homepage: true

# Build on GitHub releases for publishable RPMs.
# See: https://packit.dev/docs/configuration/upstream/copr_build#using-a-custom-copr-project
- job: copr_build
trigger: release
owner: "maxamillion"
project: "openshell"
targets:
- fedora-all
- epel-10
preserve_project: true
list_on_homepage: true
14 changes: 13 additions & 1 deletion architecture/podman-driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,19 @@ sequenceDiagram
C->>C: entrypoint: /opt/openshell/bin/openshell-sandbox
```

The supervisor image is a `FROM scratch` image containing only the prebuilt `openshell-sandbox` binary. It is built by the `supervisor-output` target in `deploy/docker/Dockerfile.images`. The `image_volumes` field in the container spec mounts this image's filesystem at `/opt/openshell/bin` with `rw: false`, making it a read-only overlay that the sandbox cannot tamper with.
The supervisor image is a `FROM scratch` image containing only the prebuilt `openshell-sandbox` binary. It is built by the `supervisor` target in `deploy/docker/Dockerfile.images`. The `image_volumes` field in the container spec mounts this image's filesystem at `/opt/openshell/bin` with `rw: false`, making it a read-only overlay that the sandbox cannot tamper with.

## TLS

When the Podman driver's TLS configuration is set (`tls_ca`, `tls_cert`, `tls_key` in `PodmanComputeConfig`), the driver:

1. Switches the auto-detected endpoint scheme from `http://` to `https://`
2. Bind-mounts the client cert files (read-only) into the container at `/etc/openshell/tls/client/`
3. Sets `OPENSHELL_TLS_CA`, `OPENSHELL_TLS_CERT`, `OPENSHELL_TLS_KEY` env vars pointing to the container-side paths

The supervisor reads these env vars and uses them to establish an mTLS connection back to the gateway.

The RPM packaging auto-generates a self-signed PKI on first start via `init-pki.sh`. Client certs are placed in the CLI auto-discovery directory (`~/.config/openshell/gateways/openshell/mtls/`) so the CLI connects with mTLS without manual configuration. See `deploy/rpm/CONFIGURATION.md` for the full RPM configuration reference and `deploy/rpm/QUICKSTART.md` for the quick start guide.

## Network Model

Expand Down
Loading
Loading