Skip to content

Updates

Shipping updates in Electrobun doesn’t require an update server — updates are just static files. Hutch creates the update metadata, compressed full bundle, and an optional delta patch at build time; the application-side Updater checks, downloads, and installs them. Anything that serves files over HTTPS works as the host: R2, S3, GitHub Releases, or a plain static server.

This page covers the publishing side — where artifacts go and how to automate building them for every platform. The application-side API lives in the Updater reference.

Configure The Release Host

Point release.baseUrl at the directory where every target’s artifacts will be uploaded:

import type { ElectrobunConfig } from "electrobun";
export default {
app: {
name: "My App",
identifier: "com.example.my-app",
version: "2.0.0",
},
release: {
baseUrl: "https://downloads.example.com/my-app",
},
} satisfies ElectrobunConfig;

A stable macOS ARM64 build checks stable-macos-arm64-update.json; a canary Windows x64 build checks canary-win-x64-update.json. Upload artifacts/ without renaming its files.

The metadata names the exact .tar.zst artifact. When Hutch can reach the previous release it also creates a patch named after that release’s bundle hash. The updater starts with its retained current tar, applies <channel>-<os>-<arch>-<current-hash>.patch, reads the next hash from the result, and follows the chain until it reaches the hash in update.json. A missing or failed edge discards the attempted chain and downloads the latest full archive instead. Hosts should retain old hash-named patches and return a normal 404 for unavailable ones.

The bundle hash is a release-routing identifier, not a code-signing or content-authentication mechanism. Serve updates over HTTPS and sign distributed applications with the platform’s normal code-signing system when authenticity is required.

The identity fields used by Electrobun 1.x (version, hash, platform, and arch) remain at the top level, and stable remains the main release channel. That lets Electrobun v1.18.1 and later consume a 2.0 release directly. For the bridge release, keep app.name and app.identifier unchanged and publish it at the release.baseUrl embedded in the installed 1.x app. Older prereleases used updater or payload layouts that are not universally bridgeable; install the 2.0 release normally for those users.

On its first 2.0 launch, an app delivered by the v1 updater creates the new update/uninstall manager metadata in its existing stable root. Its user data, browser profiles, update state, and future update URLs therefore stay on the same channel without a translation layer or reinstall.

Release Tags

Use an application prefix so unrelated repository tags do not trigger a deployment:

my-app/v2.0.0-canary.1
my-app/v2.0.0

These are independent releases. A stable artifact is built from its own tag; a canary artifact is never promoted into stable. Keep the version in electrobun.config.ts aligned with the tag.

GitHub Actions Matrix

This workflow builds all supported targets first, then creates one GitHub Release only after every build succeeds:

name: Release My App
on:
push:
tags:
- "my-app/v*"
permissions:
contents: read
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- runner: macos-14
target: macos-arm64
- runner: ubuntu-24.04
target: linux-x64
- runner: ubuntu-24.04-arm
target: linux-arm64
- runner: windows-2025
target: win-x64
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v6
- name: Install Hutch on macOS or Linux
if: runner.os != 'Windows'
shell: bash
run: |
installer="$RUNNER_TEMP/install-hutch.sh"
curl --proto '=https' --tlsv1.2 --fail --silent --show-error --location \
https://hutch.blackboard.sh/hutch/install.sh --output "$installer"
sh "$installer" --channel production --no-modify-path
echo "$HOME/.hutch/bin" >> "$GITHUB_PATH"
- name: Install Hutch on Windows
if: runner.os == 'Windows'
shell: pwsh
run: |
$script = irm https://hutch.blackboard.sh/hutch/install.ps1
& ([scriptblock]::Create($script)) -Channel production
"$HOME\.hutch\bin" | Out-File $env:GITHUB_PATH -Encoding utf8 -Append
- name: Select release channel
id: channel
shell: bash
run: |
if [[ "${{ github.ref_name }}" == *"-canary."* ]]; then
echo "value=canary" >> "$GITHUB_OUTPUT"
else
echo "value=stable" >> "$GITHUB_OUTPUT"
fi
- name: Install dependencies
run: npm ci
- name: Build release
env:
ELECTROBUN_DEVELOPER_ID: ${{ secrets.ELECTROBUN_DEVELOPER_ID }}
ELECTROBUN_APPLEID: ${{ secrets.ELECTROBUN_APPLEID }}
ELECTROBUN_APPLEIDPASS: ${{ secrets.ELECTROBUN_APPLEIDPASS }}
ELECTROBUN_TEAMID: ${{ secrets.ELECTROBUN_TEAMID }}
run: hutch electrobun build --env=${{ steps.channel.outputs.value }}
- name: Stage target artifacts
uses: actions/upload-artifact@v6
with:
name: release-${{ matrix.target }}
path: artifacts/*
if-no-files-found: error
publish:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Collect target artifacts
uses: actions/download-artifact@v7
with:
pattern: release-*
path: release-artifacts
merge-multiple: true
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: release-artifacts/*
generate_release_notes: true
prerelease: ${{ contains(github.ref_name, '-canary.') }}

The macOS job needs the signing and notarization secrets only when the config enables those features. See Code Signing for their values.

GitHub Releases As The Host

For stable-only updates, release.baseUrl can use GitHub’s latest release redirect:

import type { ElectrobunConfig } from "electrobun";
export default {
app: {
name: "My App",
identifier: "com.example.my-app",
version: "2.0.0",
},
release: {
baseUrl: "https://github.com/YOUR_ORG/YOUR_REPO/releases/latest/download",
},
} satisfies ElectrobunConfig;

GitHub excludes prereleases from /releases/latest, so that URL cannot track canary builds. Use a static host with a stable base URL when canary clients must update automatically.

See Updater for the application-side API and Bundling and Distribution for the complete artifact names.