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
111 changes: 111 additions & 0 deletions .github/workflows/release-flatpak.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
name: Flatpak release
# ShadowCode_<version>_x86_64.flatpak and ShadowCode_<version>_aarch64.flatpak
# (GNOME Platform, bundled llama.cpp with the Vulkan module), built from the
# release tag inside the SDK on each architecture, installed and started, then
# attached to the published GitHub release with
# ShadowCode_<version>_flatpak.sha256. Pull requests already build the
# Flatpak in ci.yml; this runs only for releases.
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Release tag to build and attach (for example v2.2.0)'
required: true
permissions:
contents: read
concurrency:
group: release-flatpak-${{ github.event.release.tag_name || inputs.tag }}
cancel-in-progress: false
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-24.04
arch: x86_64
- runner: ubuntu-24.04-arm
arch: aarch64
runs-on: ${{ matrix.runner }}
timeout-minutes: 240
env:
RUSTUP_TOOLCHAIN: '1.95.0'
TAG: ${{ github.event.release.tag_name || inputs.tag }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
ref: ${{ env.TAG }}
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '22'
- name: Require the tag to match the app version
run: |
version="$(node -p "require('./src-tauri/tauri.conf.json').version")"
test "v$version" = "$TAG" || { echo "Tag $TAG does not match app version $version" >&2; exit 1; }
- name: Install the pinned Rust toolchain
run: rustup toolchain install 1.95.0 --profile minimal
- name: Flatpak tools and Flathub
run: |
sudo apt-get update
sudo apt-get install -y flatpak flatpak-builder dbus-daemon
# flatpak-builder's bubblewrap needs unprivileged user namespaces,
# which Ubuntu 24.04 restricts through AppArmor by default.
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
- name: Build the Flatpak with the GNOME 50 SDK (Vulkan + CPU llama.cpp)
env:
SHADOWCODE_COMMIT: ${{ github.sha }}
run: python3 scripts/build-flatpak.py --install-deps-from=flathub --state-dir ~/.flatpak-builder
- name: Install and start the bundle
run: |
version="${TAG#v}"
bundle="target/flatpak/ShadowCode_${version}_${{ matrix.arch }}.flatpak"
test -f "$bundle"
flatpak install --user --noninteractive "$bundle"
# `flatpak run` needs a session bus; the runner has no desktop session.
dbus-run-session -- flatpak run com.shadowfetch.shadowcode --version | grep -Fx "ShadowCode $version"
dbus-run-session -- flatpak run --command=/app/lib/shadowcode/llama-server com.shadowfetch.shadowcode --version
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: flatpak-${{ matrix.arch }}
path: target/flatpak/ShadowCode_*_${{ matrix.arch }}.flatpak
if-no-files-found: error
compression-level: 0
retention-days: 7
attach:
needs: build
runs-on: ubuntu-24.04
timeout-minutes: 20
permissions:
contents: write
env:
TAG: ${{ github.event.release.tag_name || inputs.tag }}
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
pattern: flatpak-*
path: flatpak
merge-multiple: true
- name: Attach the bundles and their checksums without replacing anything
run: |
version="${TAG#v}"
cd flatpak
ls
test -f "ShadowCode_${version}_x86_64.flatpak"
test -f "ShadowCode_${version}_aarch64.flatpak"
sha256sum "ShadowCode_${version}_x86_64.flatpak" "ShadowCode_${version}_aarch64.flatpak" > "ShadowCode_${version}_flatpak.sha256"
cat "ShadowCode_${version}_flatpak.sha256"
existing="$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json assets -q '.assets[].name')"
for file in "ShadowCode_${version}_x86_64.flatpak" "ShadowCode_${version}_aarch64.flatpak" "ShadowCode_${version}_flatpak.sha256"; do
if grep -Fxq "$file" <<<"$existing"; then
# Never overwrite a published file: an existing one must match.
gh release download "$TAG" --repo "$GITHUB_REPOSITORY" --pattern "$file" --output "existing-$file"
cmp -s "$file" "existing-$file" || { echo "$file on the release differs from this build; not replacing it" >&2; exit 1; }
else
gh release upload "$TAG" "$file" --repo "$GITHUB_REPOSITORY"
fi
done
32 changes: 30 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,35 @@
# Changelog

## 2.2.0 (unreleased)

## 2.2.0: macOS, Flatpak downloads and your own model servers

- **macOS.** ShadowCode builds and runs as a Mac app: `ShadowCode.app` in
`ShadowCode_VERSION_aarch64.dmg` (Apple silicon) and `_x64.dmg` (Intel),
macOS 12 or later, ad-hoc signed and not notarized (GitHub is the channel;
`scripts/install-macos.sh` installs from a release after checking
`ShadowCode_VERSION_macos.sha256`). Settings and history live in
`~/Library/Application Support/ShadowCode`; opened from Finder the app
adopts the login shell's `PATH`; agent shell commands run under Seatbelt
(`sandbox-exec`) mirroring bubblewrap: hidden home and other volumes, an
empty `HOME` with read-only toolchains, writes only in the project and
scratch, network off, on or limited to the allow-list proxy; API keys can
live in the login Keychain; terminals, the dev-server preview, worktree
cleanup and SQLite inspection work without `/proc`; helpers die with the
app through a kqueue watcher; llama.cpp is built with Metal and
precompiled shaders. `.github/workflows/release-macos.yml` attaches the
images to each published release. Details: `docs/MACOS_PORT_ESTIMATE.md`.
- **Flatpak bundles on every release.** `.github/workflows/release-flatpak.yml`
builds `ShadowCode_VERSION_x86_64.flatpak` and `_aarch64.flatpak` (Vulkan
runtime) from the release tag, installs and starts them, and attaches them
with `ShadowCode_VERSION_flatpak.sha256`. The publisher leaves these
later attachments alone when it re-checks a published release.
- **Paths on approval cards.** The high-entropy secret rule judges a path
under a well-known root (`/Users`, `/Volumes`, `/home`, ...) one segment at a
time, so a command's folder is no longer shown as `[redacted secret]`; a
secret that is itself a segment is still hidden.
- **Free memory.** Local models that run in RAM (or Apple silicon's shared
memory) note when they need more memory than is free right now
(`MemAvailable` on Linux, free and reclaimable pages on macOS);
`hardware.available_bytes` reports it.
- **Your own model servers.** **Settings › Accounts › Your own endpoints**
connects Ollama, LM Studio, a llama.cpp or vLLM server, xAI, OpenAI or any
OpenAI-compatible API. **Find models** lists what the server offers; the
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ exclude = ["vendor/glib-0.18.5"]
resolver = "2"

[workspace.package]
version = "2.1.0"
version = "2.2.0"
edition = "2021"
rust-version = "1.95"
license = "Apache-2.0"
Expand Down
34 changes: 19 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,27 +70,31 @@ The code is open source; the Shadowfetch and ShadowCode product identity is cove
own terminal, go from commit to pull request, preview the app you're
building, dictate with your voice, and follow along from your phone.

ShadowCode **2.1.0** is available in [GitHub releases](https://github.com/ShadowfetchStudio/ShadowCode/releases/tag/v2.1.0) for x86_64 and, for the first time, 64-bit ARM, and from the signed APT repository below.
See the [2.1.0 release notes](docs/RELEASE_NOTES.md) for changes and qualification
ShadowCode **2.2.0** is available in [GitHub releases](https://github.com/ShadowfetchStudio/ShadowCode/releases/tag/v2.2.0) for x86_64 and 64-bit ARM Linux, from the signed APT repository below and, for the first time, for macOS (Apple silicon and Intel).
See the [2.2.0 release notes](docs/RELEASE_NOTES.md) for changes and qualification
limits, and the [release history](CHANGELOG.md).

## Install

Releases target x86_64 Linux with glibc 2.39 or newer (Ubuntu 24.04 or later).
From 2.1.0, aarch64 (64-bit ARM) packages are built and qualified too:
`ShadowCode_VERSION_aarch64.AppImage`, `ShadowCode_VERSION_arm64.deb` and
`ShadowCode_VERSION_aarch64.flatpak`. Each is signed for its own target and
`ShadowCode_VERSION_aarch64.AppImage` and `ShadowCode_VERSION_arm64.deb`.
Each is signed for its own target and
published next to the x86_64 files, with its signature files named
`RELEASE-AUTH-aarch64`, `RELEASE-AUTH-aarch64.sig`,
`RELEASE-MANIFEST-aarch64.json` and `SHA256SUMS-aarch64`; download those with
the package and use the same installer and verifier commands with the aarch64
file name. See
[Distributing](docs/DISTRIBUTING.md#arm64-aarch64).
From 2.2.0 each release also carries Flatpak bundles for both architectures
(`ShadowCode_VERSION_x86_64.flatpak`, `ShadowCode_VERSION_aarch64.flatpak`,
checked with `ShadowCode_VERSION_flatpak.sha256`; see [Flatpak](docs/FLATPAK.md))
and macOS disk images (see [macOS](#macos)).
Check [GitHub releases](https://github.com/ShadowfetchStudio/ShadowCode/releases)
for published versions. The 2.1.0 x86_64 packages and checksums are:
for published versions. The 2.2.0 x86_64 packages and checksums are:

- `ShadowCode_2.1.0_amd64.AppImage`
- `ShadowCode_2.1.0_amd64.deb`
- `ShadowCode_2.2.0_amd64.AppImage`
- `ShadowCode_2.2.0_amd64.deb`
- `SHA256SUMS`, `RELEASE-MANIFEST.json`, `RELEASE-AUTH` and
`RELEASE-AUTH.sig` (download these beside the package; the installer checks
the publisher's signature before running anything)
Expand Down Expand Up @@ -129,7 +133,7 @@ assets are checksum-only and cannot be installed by this new entry point; use
the instructions shipped with that released version for those existing assets.
The 0.33.0 release attempt failed before signing and publication. Its tag stays
unchanged. Version 0.33.1 was the first signed release; the public trust policy
in `release/trust` now authorizes 0.33.0 through 2.1.0.
in `release/trust` now authorizes 0.33.0 through 2.2.0.
For development, use [Build from source](#build-from-source).

Obtain an independently authenticated installer bundle as described below,
Expand All @@ -143,7 +147,7 @@ bundle and bootstrap requirements.
From that independently authenticated installer bundle, run:

```bash
bash /path/to/trusted-bundle/scripts/install-appimage.sh /path/to/downloads/ShadowCode_2.1.0_amd64.AppImage
bash /path/to/trusted-bundle/scripts/install-appimage.sh /path/to/downloads/ShadowCode_2.2.0_amd64.AppImage
```

The installer takes the AppImage path (or `--recover`), resolves its own trusted
Expand Down Expand Up @@ -189,7 +193,7 @@ as `--trust-dir` are not installer options.
cannot corrupt it.

After obtaining and verifying the intended package, run the AppImage without installing it:
`./ShadowCode_2.1.0_amd64.AppImage --appimage-extract-and-run`. FUSE is not
`./ShadowCode_2.2.0_amd64.AppImage --appimage-extract-and-run`. FUSE is not
required.

### Debian package
Expand Down Expand Up @@ -237,16 +241,16 @@ installed. The repository is published by the release workflow only; nothing
in ShadowCode adds or contacts it by itself.

To install one release by hand instead, verify the Debian artifact using the independently trusted verifier bundle.
`verified-deb-2.1.0` must not already exist; install the verified snapshot:
`verified-deb-2.2.0` must not already exist; install the verified snapshot:

```bash
bash /path/to/trusted-bundle/scripts/verify-native-release.sh \
--bundle-dir /path/to/downloads \
--trust-dir /path/to/trusted-bundle/release/trust \
--artifact ShadowCode_2.1.0_amd64.deb \
--stage-dir ./verified-deb-2.1.0 \
--expect-version 2.1.0
sudo apt install ./verified-deb-2.1.0/ShadowCode_2.1.0_amd64.deb
--artifact ShadowCode_2.2.0_amd64.deb \
--stage-dir ./verified-deb-2.2.0 \
--expect-version 2.2.0
sudo apt install ./verified-deb-2.2.0/ShadowCode_2.2.0_amd64.deb
```

A `.deb` installed by hand this way gets no updates and none of the APT
Expand Down
5 changes: 5 additions & 0 deletions docs/DISTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ bash /path/to/trusted-bundle/scripts/verify-native-release.sh \
--stage-dir ./verified-deb-VERSION
```

The Flatpak bundles are attached after publication by
`.github/workflows/release-flatpak.yml` (from 2.2.0), with SHA-256 checksums
in `ShadowCode_VERSION_flatpak.sha256` rather than publisher signatures; the
macOS disk images likewise (`release-macos.yml`, `ShadowCode_VERSION_macos.sha256`).

Releases before 2.1.0 have no aarch64 packages; build them from source (below)
on an aarch64 machine. Cross-building is not supported: each architecture is built
on its own hardware.
Expand Down
20 changes: 14 additions & 6 deletions docs/FLATPAK.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
# ShadowCode 2.1 Flatpak
# ShadowCode 2.2 Flatpak

The Flatpak runs the native GTK/WebKit desktop, application engine, SQLite
profile, and bundled CPU/Vulkan llama.cpp runtime on GNOME Platform 50. It is
an x86_64 or aarch64 package built from source inside the SDK on a host of
the same architecture. Version 2.1.0 is a local
build until a publisher signs and publishes it; it is not listed on Flathub.
the same architecture. From 2.2.0 every GitHub release carries both bundles
(`.github/workflows/release-flatpak.yml` builds, installs and starts them
from the release tag) with their SHA-256 checksums in
`ShadowCode_VERSION_flatpak.sha256`. They are checked by that checksum, not
by the publisher signature the AppImage and deb carry. It is not listed on
Flathub.

## Install the local bundle
## Install the bundle

Download the bundle for your computer and the checksum file from the
release, then:

```sh
flatpak install --user ./ShadowCode_2.1.0_x86_64.flatpak
sha256sum --check --ignore-missing ShadowCode_2.2.0_flatpak.sha256
flatpak install --user ./ShadowCode_2.2.0_x86_64.flatpak
flatpak run com.shadowfetch.shadowcode
```

On 64-bit ARM, install `ShadowCode_VERSION_aarch64.flatpak` instead.
On 64-bit ARM, install `ShadowCode_2.2.0_aarch64.flatpak` instead.

The bundle points to Flathub for the GNOME runtime. Installation exports the
application launcher and icons into the desktop's Applications menu.
Expand Down
6 changes: 3 additions & 3 deletions docs/RELEASE_AUTHENTICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ Each key has an inclusive version range and exactly one epoch. Unknown keys, dif

For a reviewed rotation, distribute a new authenticated trust bundle with the new public key and higher minimum epoch/version. Retain the old public key with its historical version range only when needed to verify an existing accepted-state receipt. The internal history check may ignore current minimum floors, but it still verifies the old signature, identity, key epoch and historical key range. This exception is not exposed as an install-verification API. Deleting an old key outright can make old state unverifiable; the installer must then fail pending explicit migration, not erase the state or assume a fresh install.

The tracked `release/trust` bundle contains the public Ed25519 key with ID `f0c60ff8228314616985712f8918c9798c0ecf72b7271e19343a041c25663b7f`. Its epoch is 1, minimum version is 0.33.0, and its inclusive key range is 0.33.0–2.1.0. It authorizes no later version without a reviewed policy update. The private key is not distributed in the repository or installer bundle. Fixture keys remain temporary and separate. Public fingerprint distribution, custody and future rotation/revocation remain owner responsibilities.
The tracked `release/trust` bundle contains the public Ed25519 key with ID `f0c60ff8228314616985712f8918c9798c0ecf72b7271e19343a041c25663b7f`. Its epoch is 1, minimum version is 0.33.0, and its inclusive key range is 0.33.0–2.2.0. It authorizes no later version without a reviewed policy update. The private key is not distributed in the repository or installer bundle. Fixture keys remain temporary and separate. Public fingerprint distribution, custody and future rotation/revocation remain owner responsibilities.

## Downgrade, replay and accepted state

Expand Down Expand Up @@ -149,8 +149,8 @@ A separate publisher runner receives the signing job's immutable artifact ID and
The workflow deliberately refuses missing owner configuration. The following records the 0.33.1 setup; recheck it before dispatching a release:

- The tracked `release/trust` public policy and key bundle must be included in the reviewed tooling commit and distributed through an authenticated channel. It contains public material only; no default fixture key is trusted.
- Repository variable `NATIVE_RELEASE_TOOLING_COMMIT` must pin the exact 40-character reviewed and pushed commit containing the current gate definitions, signing/publisher code, public trust and release-appropriate notes. The reviewed pin for 0.33.1 was `e15c4480e65db5650af012bb2a9773dbe89acf84`; for 2.1.0 it is the tagged `v2.1.0` release commit (`v1.0.1` for 1.0.1, `v1.0.0` for 1.0.0, `v0.34.2` for 0.34.2). Never infer this pin from an unreviewed tag.
- Repository variable `NATIVE_RELEASE_SIGNING_ENVIRONMENT` is set to `release-signing`. Its verified configuration requires the `ShadowfetchStudio` User reviewer (ID `209457103`, unchanged after the account rename), allows self-review and retains GitHub's default administrator override. Its sole deployment rule allows the tag being released (`v2.1.0` for 2.1.0; `v1.0.1`, `v1.0.0`, `v0.34.2` and `v0.33.1` before). This is an owner-reviewed gate, not a two-person approval guarantee; a later release needs an explicit rule/policy update.
- Repository variable `NATIVE_RELEASE_TOOLING_COMMIT` must pin the exact 40-character reviewed and pushed commit containing the current gate definitions, signing/publisher code, public trust and release-appropriate notes. The reviewed pin for 0.33.1 was `e15c4480e65db5650af012bb2a9773dbe89acf84`; for 2.2.0 it is the tagged `v2.2.0` release commit (`v2.1.0` for 2.1.0, `v1.0.1` for 1.0.1, `v1.0.0` for 1.0.0, `v0.34.2` for 0.34.2). Never infer this pin from an unreviewed tag.
- Repository variable `NATIVE_RELEASE_SIGNING_ENVIRONMENT` is set to `release-signing`. Its verified configuration requires the `ShadowfetchStudio` User reviewer (ID `209457103`, unchanged after the account rename), allows self-review and retains GitHub's default administrator override. Its sole deployment rule allows the tag being released (`v2.2.0` for 2.2.0; `v2.1.0`, `v1.0.1`, `v1.0.0`, `v0.34.2` and `v0.33.1` before). This is an owner-reviewed gate, not a two-person approval guarantee; a later release needs an explicit rule/policy update.
- Environment secret `NATIVE_RELEASE_SIGNING_KEY_PEM` has been uploaded to `release-signing`. It is scoped to the signing environment, not repository-wide secrets or the build job. Its presence does not prove that a candidate has been signed.

Owner review of the release workflow/source and repository protections remains necessary: another authorized workflow could request the same environment secret. Local gate receipts are validated assertions, not independent attestations that an adversarial producer executed the commands. Workflow dependencies and exact artifact IDs bind the proposed job flow; local fixtures do not prove GitHub control-plane behavior. Remote checks are observations, not atomic compare-and-swap against independently authorized concurrent changes.
Expand Down
Loading
Loading