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.1my-app/v2.0.0These 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.