Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2a3de17
docs: drop the beta notice ahead of v1.0.0
eraflo Oct 8, 2026
8dd9155
feat: sampling presets, advanced chat options and model loading settings
eraflo Oct 8, 2026
732574c
feat(chat): export a conversation as Markdown
eraflo Oct 8, 2026
914e8ea
feat(server): LangChain, Continue and Open WebUI setup in Connect an app
eraflo Oct 8, 2026
666e228
feat(chat): choose where vision models encode images
eraflo Oct 8, 2026
57e37c3
feat: import a model from the command palette
eraflo Oct 8, 2026
b007ce3
feat(chat): preload the model before the first message
eraflo Oct 8, 2026
c07b3a3
feat: Ollama-compatible API, optionally on port 11434
eraflo Oct 8, 2026
44b0746
feat: Benchmark page with Qualcomm's geniex-bench
eraflo Oct 8, 2026
fe27313
feat!: install GenieX on first launch, privacy policy and code signin…
eraflo Oct 8, 2026
cd33afa
feat: speculative decoding in the Chat and the Benchmark
eraflo Oct 8, 2026
60f1198
feat: structured output on every model, the NPU included
eraflo Oct 8, 2026
4060676
feat: tool_choice on every model, the NPU included
eraflo Oct 9, 2026
c72d6d9
feat: long conversations on the NPU
eraflo Oct 9, 2026
71fcc06
fix(chat): keep the header inside a narrow chat column
eraflo Oct 9, 2026
68d7a55
feat(chat): hide the settings panel by default on small windows
eraflo Oct 9, 2026
527801d
feat(chat): summarize the messages the model forgets
eraflo Oct 9, 2026
878b22a
feat: live power and energy per token on Snapdragon X
eraflo Oct 9, 2026
a2ce233
feat: answer other devices on the local network, over HTTPS
eraflo Oct 9, 2026
b43a42d
feat: calcine-cli, the API without the window
eraflo Oct 9, 2026
8be7a41
feat: put calcine-cli on the PATH
eraflo Oct 9, 2026
eb8f0af
fix(gateway): free the network port before listening again
eraflo Oct 9, 2026
c9f88cc
feat: Linux ARM64
eraflo Oct 9, 2026
e681cd0
ci: link the code signing policy from release notes
eraflo Oct 9, 2026
4d69ea5
docs: bring the README, site and policies up to date
eraflo Oct 9, 2026
39bf9d5
chore(release): v1.0.0
eraflo Oct 9, 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
65 changes: 65 additions & 0 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Build Linux package

# Called by the Release and Prerelease workflows: the Linux ARM64 `.deb`
# (Snapdragon X laptops running Ubuntu) with its update signature, uploaded
# as the `linux-deb` artifact for them to publish.

on:
workflow_call:
inputs:
beta-build:
description: Stamp the next beta version for this build number (prereleases).
type: string
default: ""
secrets:
TAURI_SIGNING_PRIVATE_KEY:
required: true
TAURI_SIGNING_PRIVATE_KEY_PASSWORD:
required: true

permissions:
contents: read

env:
CARGO_TERM_COLOR: always
# Keep in sync with rust-toolchain.toml.
RUST_TOOLCHAIN: "1.99.0"

jobs:
deb:
name: Package (Linux ARM64)
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Tauri system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: package.json
- uses: dtolnay/rust-toolchain@7e38f4b43b4db5c8dd498af069a4f6196df1d067 # master
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- run: bun install --frozen-lockfile

- name: Stamp beta version
if: inputs.beta-build != ''
env:
BUILD: ${{ inputs.beta-build }}
run: bun scripts/version.ts beta "$BUILD"

- name: Build the package (update signature)
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: bun run build:release --bundles deb

- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: linux-deb
path: |
target/release/bundle/deb/*.deb
target/release/bundle/deb/*.deb.sig
if-no-files-found: error
26 changes: 25 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ jobs:
with:
bun-version-file: package.json
- name: Pinned GenieX installer is published
run: bun scripts/fetch-geniex.ts --check
run: bun scripts/check-geniex.ts

# The real target, built only for `main` and pull requests to it (release
# PRs); `dev` relies on the checks above.
Expand All @@ -103,3 +103,27 @@ jobs:
- run: bun install --frozen-lockfile
- name: Build the app (no installer)
run: bun run tauri build --no-bundle

build-linux-arm64:
name: Build (Linux ARM64)
if: github.ref == 'refs/heads/main' || github.base_ref == 'main'
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Tauri system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: package.json
- uses: dtolnay/rust-toolchain@7e38f4b43b4db5c8dd498af069a4f6196df1d067 # master
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- run: bun install --frozen-lockfile
# The Rust job runs on x86_64; the app ships for ARM64.
- name: Test on ARM64
run: mkdir -p dist && cargo test --workspace --locked
- name: Build the package
run: bun run tauri build --bundles deb
8 changes: 4 additions & 4 deletions .github/workflows/geniex-bump.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: GenieX bump

# Daily: when Qualcomm publishes a new stable GenieX, verify the installer
# (SHA-256, then install it and run `geniex version`) and open a PR to `dev`
# that bundles it in Calcine's installer.
# that pins it: Calcine installs that version on first launch.

on:
schedule:
Expand Down Expand Up @@ -53,10 +53,10 @@ jobs:
base: dev
branch: chore/geniex-${{ steps.bump.outputs.version }}
add-paths: runtime/geniex.json
commit-message: "chore(runtime): bundle GenieX ${{ steps.bump.outputs.version }}"
title: "chore(runtime): bundle GenieX ${{ steps.bump.outputs.version }}"
commit-message: "chore(runtime): pin GenieX ${{ steps.bump.outputs.version }}"
title: "chore(runtime): pin GenieX ${{ steps.bump.outputs.version }}"
body: |
Qualcomm published GenieX ${{ steps.bump.outputs.version }}. This pins it in `runtime/geniex.json`, so the next Calcine installers bundle it.
Qualcomm published GenieX ${{ steps.bump.outputs.version }}. This pins it in `runtime/geniex.json`, so the next Calcine release installs it on first launch.

Checked by this workflow: the installer matches the official manifest (size and SHA-256), installs silently on Windows ARM64, and `geniex version` reports ${{ steps.bump.outputs.version }}.

Expand Down
35 changes: 27 additions & 8 deletions .github/workflows/prerelease.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
name: Prerelease

# On demand (Actions > Prerelease > Run workflow, on `dev`): publish an
# installable beta, Windows ARM64 NSIS installer with GenieX bundled, as a
# GitHub pre-release, and point the Beta update channel at it. Pushes to
# installable beta, Windows ARM64 NSIS installer (Calcine only) and Linux
# ARM64 package, as a GitHub pre-release, and point the Beta update channel
# at it. Pushes to
# `dev` don't build installers; only `main` releases do.

on:
Expand All @@ -21,8 +22,18 @@ env:
RUST_TOOLCHAIN: "1.99.0"

jobs:
linux:
name: Linux
uses: ./.github/workflows/build-linux.yml
with:
beta-build: ${{ github.run_number }}
secrets:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}

beta:
name: Beta (Windows ARM64)
name: Beta
needs: linux
runs-on: windows-11-arm
permissions:
contents: write
Expand All @@ -44,20 +55,25 @@ jobs:
id: version
run: echo "version=$(bun scripts/version.ts beta ${{ github.run_number }})" >> "$GITHUB_OUTPUT"

- name: Build installer (GenieX bundled, update signature)
- name: Build installer (update signature)
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: bun run build:release

- name: Collect the Linux package
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: linux-deb
path: linux-release
- name: Collect artifacts
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
mkdir -p dist-release
cp target/release/bundle/nsis/*-setup.exe dist-release/
cp target/release/bundle/nsis/*-setup.exe linux-release/*.deb dist-release/
printf 'Beta from `dev` (%s).\n' "${GITHUB_SHA::7}" > notes.md
bun scripts/updater-manifest.ts "$VERSION" latest-beta.json notes.md
bun scripts/updater-manifest.ts "$VERSION" latest-beta.json notes.md linux-release
cd dist-release && sha256sum -- * > SHA256SUMS

- name: Publish pre-release
Expand All @@ -70,9 +86,12 @@ jobs:
--prerelease \
--target "$GITHUB_SHA" \
--title "Calcine $VERSION" \
--notes "Automated beta from \`dev\` (${GITHUB_SHA::7}), bundling GenieX $geniex.
--notes "Automated beta from \`dev\` (${GITHUB_SHA::7}). Installs GenieX $geniex on first launch.

- Windows 11 on Snapdragon: the \`-setup.exe\`. This build isn't code-signed yet, so Windows SmartScreen will warn before installing.
- Linux on ARM64 (preview): the \`.deb\` (\`sudo apt install ./<file>.deb\`).

This build isn't code-signed yet, so Windows SmartScreen will warn before installing."
Checksums in \`SHA256SUMS\`. [Code signing policy](https://eraflo.github.io/Calcine/#code-signing) · [Privacy policy](https://github.com/eraflo/Calcine/blob/main/PRIVACY.md)"

# The Beta channel (Settings > Calcine updates) reads latest-beta.json
# from a fixed release. It's a pre-release so it never becomes "latest".
Expand Down
35 changes: 27 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ name: Release

# When `main` carries a version without a tag yet (after merging the release
# PR prepared by `bun scripts/release.ts`), build and publish it:
# signed installer with GenieX bundled, updater manifest, checksums, SBOM and
# build provenance. See docs/RELEASING.md.
# signed Windows installer (Calcine only), Linux package, updater manifest,
# checksums, SBOM and build provenance. See docs/RELEASING.md.

on:
push:
Expand Down Expand Up @@ -52,10 +52,19 @@ jobs:
echo "release=true" >> "$GITHUB_OUTPUT"
fi

release:
name: Release v${{ needs.check.outputs.version }} (Windows ARM64)
linux:
name: Linux
needs: check
if: needs.check.outputs.release == 'true'
uses: ./.github/workflows/build-linux.yml
secrets:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}

release:
name: Release v${{ needs.check.outputs.version }}
needs: [check, linux]
if: needs.check.outputs.release == 'true'
runs-on: windows-11-arm
permissions:
contents: write
Expand All @@ -79,7 +88,7 @@ jobs:
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- run: bun install --frozen-lockfile

- name: Build installer (GenieX bundled, update signature)
- name: Build installer (update signature)
run: bun run build:release

- name: Collect the installer
Expand All @@ -89,6 +98,12 @@ jobs:
installer="$(ls target/release/bundle/nsis/*-setup.exe)"
cp "$installer" dist-release/
echo "path=dist-release/$(basename "$installer")" >> "$GITHUB_OUTPUT"
- name: Collect the Linux package
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: linux-deb
path: linux-release
- run: cp linux-release/*.deb dist-release/

# Authenticode signing through the SignPath Foundation, once the project
# is approved: set the repository variables SIGNPATH_ORGANIZATION_ID and
Expand Down Expand Up @@ -122,8 +137,8 @@ jobs:
run: |
awk -v v="## $VERSION " 'index($0, v) == 1 {found=1; next} found && /^## / {exit} found' CHANGELOG.md > notes.md
[ -s notes.md ] || echo "Calcine $VERSION" > notes.md
bun scripts/updater-manifest.ts "$VERSION" dist-release/latest.json notes.md
cd dist-release && sha256sum -- *.exe > SHA256SUMS
bun scripts/updater-manifest.ts "$VERSION" dist-release/latest.json notes.md linux-release
cd dist-release && sha256sum -- *.exe *.deb > SHA256SUMS

- name: SBOM (CycloneDX)
uses: anchore/sbom-action@66cbf4bc1f1c0d2edc94016e65bc221b6bb0ad6c # v0.24.3
Expand All @@ -137,12 +152,16 @@ jobs:
- name: Build provenance
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: ${{ steps.installer.outputs.path }}
subject-path: |
${{ steps.installer.outputs.path }}
dist-release/*.deb

- name: Publish the release
env:
GH_TOKEN: ${{ github.token }}
run: |
# SignPath asks for the code signing policy on download pages.
printf '\n---\n\nWindows 11 on Snapdragon: the `-setup.exe`. Linux on ARM64 (preview): the `.deb`. Checksums in `SHA256SUMS`.\n\n[Code signing policy](https://eraflo.github.io/Calcine/#code-signing) · [Privacy policy](https://github.com/eraflo/Calcine/blob/main/PRIVACY.md)\n' >> notes.md
gh release create "v$VERSION" dist-release/* \
--target "$GITHUB_SHA" \
--title "Calcine $VERSION" \
Expand Down
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
# --- Tauri ---
src-tauri/gen/schemas/
src-tauri/WixTools/
# GenieX installer fetched at build time (pinned in runtime/geniex.json)
src-tauri/resources/geniex/
# Downloaded by scripts/geniex-bump.ts to test a new GenieX
/geniex-cli-setup.exe

Expand Down
50 changes: 50 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Changelog

## 1.0.0 (2026-10-09)

The first stable release: a desktop app and a secure, OpenAI-compatible local API for
Qualcomm GenieX on Snapdragon, with the NPU first.

### Models and chat

- Manage GenieX models from the app: Qualcomm AI Hub catalog for your chipset, Hugging Face
search with precisions, sizes and memory checks, links from ModelScope and Docker Hub, local
import, remove and clean (9155f29, 9f13225, 57e37c3)
- Streaming chat with reasoning, sampling presets, advanced options, images, voice, model
preloading and Markdown export (0a1f0d8, 8dd9155, b007ce3, 666e228, 732574c)
- Structured output (`response_format`) and tool calls (`tools`, `tool_choice`) on every
model, the NPU included: Calcine checks each reply and has the model fix it (60f1198, 4060676)
- Long conversations on the NPU: context counted with each model's tokenizer, the oldest
messages forgotten in steps that keep GenieX's cache useful, and summarized (c72d6d9, 527801d)
- Speculative decoding in the Chat and the Benchmark (cd33afa)

### API

- Secured gateway on `127.0.0.1:18181`: per-app keys, Host and Origin checks, body checks, a
request queue and a log without prompts (0a1f0d8)
- Ollama-compatible API, optionally on port 11434 (c07b3a3)
- Other devices on the local network, over HTTPS with network keys and private addresses only,
off by default (a2ce233)
- `calcine-cli`: the same API without the window, on the `PATH` (b43a42d, 8be7a41)
- Setup snippets for LangChain, Continue and Open WebUI (914e8ea)

### Hardware

- Live NPU, GPU, CPU and memory load, and a self-test per compute unit (9f13225)
- Benchmark page with Qualcomm's `geniex-bench` (44b0746)
- Live power and energy per token on Snapdragon X (878b22a)

### Install and updates

- GenieX downloaded from Qualcomm on first launch and checked against a pinned SHA-256; update,
roll back and repair it from the app (fe27313, acb05b2)
- Signed Calcine updates on Stable and Beta channels, start with the session, configurable API
port and allowed web origins (6223b34)
- Linux on ARM64, as a preview: a `.deb` package, GenieX's Linux archive, `calcine-cli geniex`
for machines without a screen (c9f88cc)
- English and French, light and dark themes, command palette and tray (44a3a1f, 9f13225)

### Fixes

- The network port is free again before it listens anew after a settings change (eb8f0af)
- The chat header stays inside a narrow chat column (71fcc06)
10 changes: 6 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ Common scopes: `app`, `ui`, `chat`, `models`, `discover`, `server`, `gateway`,

## Local setup

Requirements: Windows 11 ARM64 on a Snapdragon device (for real inference),
[rustup](https://rustup.rs) (the pinned toolchain in `rust-toolchain.toml` is
installed automatically), the Visual Studio C++ build tools, and
[bun](https://bun.sh).
Requirements: a Snapdragon device for real inference, running Windows 11 ARM64
(with the Visual Studio C++ build tools) or Linux ARM64 (with
[Tauri's system dependencies](https://v2.tauri.app/start/prerequisites/#linux)
and `ocl-icd-libopencl1`), [rustup](https://rustup.rs) (the pinned toolchain in
`rust-toolchain.toml` is installed automatically) and [bun](https://bun.sh).
The mock (`bun run app:mock`) runs anywhere.

```bash
bun install
Expand Down
Loading
Loading