9 GitHub releases in 13 daysgithub.com/mediar-ai/fazmLive appcast polls GitHub every 60s

Open source AI projects with GitHub releases in the past day, and what actually happens after the tag is published

Everyone ranks projects by stars, tags, and recency. Almost nobody shows you what happens between the GitHub release page and the user's Applications folder. This guide walks the full path using one real open-source AI project I can point at line-by-line: Fazm, a macOS AI agent whose entire release pipeline (appcast, EdDSA signing, channel routing, crash-loop rollback) is readable source code in its public repo.

F
Fazm
12 min read
4.9from 200+
Every anchor fact maps to a specific file and line in the public repo
9 real GitHub releases, April 3 to April 16, 2026 (from CHANGELOG.json)
Appcast polls api.github.com on every request with a 60s CDN cache

Fazm GitHub releases, past 13 days

v2.3.22026-04-16v2.2.12026-04-12v2.2.02026-04-11v2.1.32026-04-09v2.1.22026-04-07v2.0.92026-04-05v2.0.72026-04-05v2.0.62026-04-04v2.0.12026-04-03

The cadence

0 real GitHub releases in 0 days

Every list for this query answers "what is new in the past 24 hours." A stable-enough answer is: a project with a high release cadence almost always has a release in the past day. Fazm shipped nine versions between April 3 and April 16, 2026. Every one of them exists on github.com/mediar-ai/fazm/releases. Every one of them is reflected in CHANGELOG.json in the repo root.

v2.3.2
2026-04-16
Privacy language tightened. Pop-out chat stream crosstalk fix.
v2.2.1
2026-04-12
Duplicate AI response bug in pop-out and floating bar, fixed.
v2.2.0
2026-04-11
Custom API endpoint setting added. Global new-chat shortcut. Founder chat rate-limit crash loop fix.
v2.1.3
2026-04-09
Manage Subscription button. Pop-out chat error surfacing.
v2.1.2
2026-04-07
ACP v0.25.0 with credit-exhaustion and rate-limit handling.
v2.0.9
2026-04-05
Chat credit exhaustion surfaced. Scroll-to-bottom button.
v2.0.7
2026-04-05
Auto-fallback to built-in account when personal Claude lacks model access.
v2.0.6
2026-04-04
Smart/Fast model toggle. Floating bar and detached window reliability.
v2.0.1
2026-04-03
Pop-out chat window. 30% wider default chat. 19 other fixes.

The full path

From `git push --tags` to `/Applications/Fazm.app`

The loop on the left is the release pipeline. The loop on the right is what Sparkle does on an existing install. The backend in the middle is the thing that no listicle ever explains: it polls GitHub on every request and turns a GitHub release into a Sparkle appcast on the fly.

GitHub release -> Sparkle appcast -> user Mac

release.shgithub.comfazm-backendSparkleUser Macpush v2.3.2-macos tag + Fazm.ziprelease body: edSignature: MEUCIQD...GET /appcast.xml (every 10 min)GET /repos/mediar-ai/fazm/releases (60s cache)JSON array of 20 latest releasesRSS with edSignature + url + channel tagsGET releases/download/v2.3.2-macos/Fazm.zipverify EdDSA against SUPublicEDKeybackup current .app via /usr/bin/ditto, install new3 launches in 60s window -> crash loop?

The anchor fact (uncopyable part)

150 lines of Rust turn GitHub releases into a Sparkle feed

This is the entire mechanism. On every request to `/appcast.xml`, the backend hits `api.github.com/repos/mediar-ai/fazm/releases?per_page=20`, maps each release to a Sparkle item, and extracts the EdDSA signature from the release body with a regex. No database of releases. No cron job. The source of truth is GitHub, not a pipeline's scratch bucket.

Backend/src/routes/appcast.rs

What comes back when you curl the live endpoint

Terminal, talking to fazm-backend.run.app

One pattern any open-source AI Mac project can copy

The same three-piece pipeline (GitHub Release on the left, a backend appcast in the middle, Sparkle on the right) works for any open-source desktop AI project. The only project-specific part is the repo name and the EdDSA public key.

GitHub releases -> dynamic appcast -> end-user binary

v2.3.2-macos tag
Fazm.zip asset
edSignature in body
Firestore channel
/appcast.xml
Sparkle check
EdDSA verify
ditto backup
Crash-loop guard

What happens when a new Fazm GitHub release lands

1

release.sh pushes a tag like v2.3.2-macos

release.sh in the repo root drives Codemagic to build, sign with the Developer ID, notarize via apple-notarize-worker, and attach Fazm.zip + the EdDSA signature to the GitHub release body. The tag format is enforced by the regex in appcast.rs at line 99.

2

Within 60 seconds, /appcast.xml reflects the new release

appcast.rs line 47 sets `Cache-Control: public, max-age=60`. The next time any client (or CDN) requests /appcast.xml, the backend calls api.github.com/repos/mediar-ai/fazm/releases?per_page=20 and parses the 20 most recent releases. The JSON -> XML translation is line 81-212.

3

Sparkle on the client checks every 10 minutes

UpdaterViewModel.swift line 361 sets `updateCheckInterval = 600`. When a newer shortVersionString is found, the delegate fires didFindValidUpdate (line 62-83). If the user was below MIN_SUPPORTED_VERSION = 2.1.0, the criticalUpdate flag makes the prompt non-skippable.

4

Sparkle downloads the .zip and verifies the EdDSA signature

Against SUPublicEDKey in Info.plist. If verification fails, the install is aborted and nothing is written to /Applications/. This is the per-release integrity check that lets the backend trust the GitHub release body contents.

5

UpdateRollbackManager.backupCurrentApp() runs willInstallUpdate

/usr/bin/ditto copies the current /Applications/Fazm.app into ~/Library/Application Support/Fazm/UpdateBackup/Fazm.app. This preserves code signature, xattrs, and resource forks. Backup happens before Sparkle overwrites the app, not after.

6

On relaunch, the rollback manager samples the launch pattern

Two UserDefaults keys, rollback_launchCount and rollback_launchTimestamp, track how many launches happened in the last 60 seconds. If launchCount reaches 3 within that window, the new version is declared a crash-loop and rolled back.

7

Rollback restores the backup and blocks the bad version

ditto again, this time from UpdateBackup/Fazm.app back to /Applications/Fazm.app. rollback_blockedVersion is written with the crashing shortVersionString. Sparkle's shouldProceedWithUpdate delegate (UpdaterViewModel.swift lines 92-101) then refuses that version on future checks.

The other uncopyable part

3 crashes, 60 seconds, rollback

The most interesting line is the timestamp threshold. Not a feature flag. Not a config value. A compiled constant. Changing it requires a release, which has to survive the rollback it configures, which is the kind of self-referential pressure that keeps the threshold sane.

Desktop/Sources/UpdateRollbackManager.swift

What the log file looks like during a rollback

~/Library/Logs/Fazm/fazm.log during a bad v2.2.0 install
60s cache

const GITHUB_REPO = "mediar-ai/fazm"; one line of Rust that turns every new GitHub release into a Sparkle feed item, on every request, no cron.

Backend/src/routes/appcast.rs, line 12

0GitHub releases, Apr 3 to Apr 16
0seconds of appcast CDN cache
0crashes in 60s triggers rollback
0minimum supported version floor

Standard listicles vs. this guide

Same query, two very different answers. One ranks tag names. The other explains the path between a tag and a running binary.

FeatureStandard listiclesThis guide (Fazm)
Answers 'what tagged in the past 24h'Yes, usually a table of tag namesYes, with 9 dated entries
Walks the full release pipeline line by lineStops at the tag nameSeven timeline steps tied to source files
Quotes the exact Rust/Swift source that runsNoneappcast.rs + UpdateRollbackManager.swift
Shows the real curl output of the live appcastNoneFull terminal trace of /appcast.xml
Explains EdDSA signing and verificationNot mentionedExtraction regex + SUPublicEDKey flow
Documents the crash-loop rollback thresholdsNot mentioned3 crashes in 60s, compiled constants
Links to a single OSI-licensed repo that has everythingLinks out to many repos, no depth on anygithub.com/mediar-ai/fazm

Enforcement in a const

const MIN_SUPPORTED_VERSION: &str = "2.1.0";

Line 19 of appcast.rs. Any client running below 2.1.0 sees the latest update marked with Sparkle's criticalUpdate tag, which disables the 'remind me later' option and re-prompts aggressively on relaunch. This is how the Fazm team forces a minimum version floor without a migration script.

The floor was raised to 2.1.0 after the v2.1.0 release shipped the paywall and an auth change that must not be bypassable by running an older binary. Raising the floor is a one-line change in appcast.rs and a backend redeploy, same blast radius as changing the paywall logic itself.

What every other past-day-releases listicle skips

The plumbing between a tag and a running binary

  • How a GitHub release body's edSignature line becomes the Sparkle sparkle:edSignature attribute
  • Why the appcast polls GitHub live on every request with a 60s CDN cache, instead of running a cron
  • How channel routing works when Firestore and GitHub's isPrerelease flag disagree
  • What ditto does before Sparkle overwrites the .app (preserves code signature, xattrs, resource forks)
  • How two UserDefaults keys (launchCount, launchTimestamp) detect a crash loop without Sentry or Firebase
  • Why MIN_SUPPORTED_VERSION is a compiled const and what changing it actually costs

Frequently asked questions

Frequently asked questions

What qualifies as an 'open source AI project with a GitHub release in the past day'?

For this guide the bar is: (1) source code publicly available on GitHub under an OSI-approved license, (2) a GitHub Release (not just a commit or a tag) published within the past 24-48 hours, (3) the release ships a usable binary or package, not just a version bump. A lot of lists that surface for this query skip the binary requirement. The interesting projects are the ones that actually deliver something a user can run, not just projects that cut a tag and walk away.

Why is this page anchored on one specific Mac app instead of listing many projects?

Because the listicles already list the projects. What the listicles never explain is what happens after the tag is published: how the signed zip moves from github.com/owner/repo/releases/download/... to the user's /Applications/ directory, how signatures are verified, how the channel routing works, and what happens when a release crashes on boot. Fazm is a useful case because its release pipeline is entirely in the open repo and every step of 'what happens when a new GitHub release drops' is readable source, not a black box.

What is the anchor fact for this guide? Where can I verify it?

Two source files. Backend/src/routes/appcast.rs lines 12-19 declare `const GITHUB_REPO: &str = "mediar-ai/fazm";`, `const GITHUB_API: &str = "https://api.github.com";`, and `const MIN_SUPPORTED_VERSION: &str = "2.1.0";`. Lines 239-255 are the GitHub releases fetch. Line 47 sets `public, max-age=60` on the appcast response. Line 151 is the regex that extracts the EdDSA signature from the GitHub release body. Desktop/Sources/UpdateRollbackManager.swift lines 25-27 set `crashThreshold = 3` and `rapidCrashWindow = 60`. All four facts come from github.com/mediar-ai/fazm, open source.

How many releases did Fazm actually ship in the past 13 days?

Nine. From CHANGELOG.json: v2.0.1 (April 3), v2.0.6 (April 4), v2.0.7 (April 5), v2.0.9 (April 5), v2.1.2 (April 7), v2.1.3 (April 9), v2.2.0 (April 11), v2.2.1 (April 12), and v2.3.2 (April 16). One 'past day' release is common. Nine in 13 days means there is almost always one within the past 24-48 hours. The cadence is not accidental: the appcast backend polls GitHub releases on every request with a 60-second cache, so the moment a new tag with a .zip asset is published, every existing Fazm install will discover it within 10 minutes (the Sparkle `updateCheckInterval`, UpdaterViewModel.swift line 361).

How does the backend actually turn a GitHub release into a Sparkle appcast?

appcast.rs at generate_appcast() does it in one pass. It calls `api.github.com/repos/mediar-ai/fazm/releases?per_page=20` in parallel with a Firestore fetch for channel routing, finds the release asset whose name ends in .zip and does not contain 'appcast', parses the tag via the regex `v?(\d+\.\d+\.\d+)(?:\+(\d+))?(?:-macos)?(?:-(staging|beta))?`, pulls the EdDSA signature out of the release body with another regex, converts the 'What's New' section of the release body into HTML list items, and emits one <item> per (release, channel) pair. No cron job, no webhook. Every appcast.xml request does the live fetch with a 60-second CDN cache.

What does 'channel routing via Firestore' mean and why does it exist alongside GitHub's isPrerelease flag?

Firestore holds a `desktop_releases` collection where each document is a tag-to-channel assignment (stable, beta, staging). On every appcast request, the backend fetches that map in parallel with GitHub and uses Firestore as the authoritative source. If Firestore is unreachable, it falls back to the tag suffix heuristic: `-staging` suffix means staging channel, otherwise stable. The reason Firestore exists on top of GitHub's isPrerelease flag is that Fazm supports three channels (stable / beta / staging), not two, and Fazm promotes releases between channels after the tag has already been published. Changing a channel should not require re-cutting a release.

What is the crash-loop rollback protection and how do the thresholds work?

UpdateRollbackManager.swift ships three phases. Phase 1 fires inside Sparkle's willInstallUpdate delegate: it runs /usr/bin/ditto to copy the current /Applications/Fazm.app into ~/Library/Application Support/Fazm/UpdateBackup/. Phase 2 runs on every launch: it reads two UserDefaults keys, `rollback_launchCount` and `rollback_launchTimestamp`. If three launches happen with less than 60 seconds between each, we are in a crash loop. Phase 3 restores the backup over the broken app, writes `rollback_blockedVersion = <crashing version>`, and Sparkle's shouldProceedWithUpdate delegate then refuses that version (UpdaterViewModel.swift lines 92-101). The two thresholds are literal values in the source: `crashThreshold = 3` and `rapidCrashWindow = 60`, lines 25-27.

Why does the appcast force `sparkle:criticalUpdate sparkle:version="2.1.0"` on every item?

Because the Fazm team needed to enforce a hard floor for a paywall and auth change that shipped in v2.1.0. Sparkle's criticalUpdate tag tells any client below that version to show a non-skippable prompt on the next check. Every version below 2.1.0 effectively has to update. This is the reason `MIN_SUPPORTED_VERSION` is a const in appcast.rs rather than a config value: changing the floor requires a backend deploy, which is the same blast radius as changing the paywall logic itself.

Is there a way to verify the EdDSA signature that ends up in the appcast?

Yes. The release notes include a line of the form `edSignature: MEUCIQD...` or similar. The backend regex at appcast.rs line 151 extracts any Base64 string of 40 or more characters that follows the literal `edSignature`. Sparkle on the client then verifies that signature against the Fazm EdDSA public key baked into Info.plist before extracting the zip. If the signature is missing, malformed, or does not verify, Sparkle refuses to install. verify-release.sh in the repo root runs this end-to-end: it downloads the appcast, extracts the edSignature and download URL, fetches the zip, and runs the sign_update --verify tool from Sparkle's bin/ directory. No install, just verification.

How does Fazm compare to screenshot-based AI agents on the 'past-day release' angle?

Screenshot-based agents have a very different release surface: they ship model weights and a thin client. Fazm ships a binary that reads the macOS accessibility tree directly via AXUIElementCreateApplication and AXUIElementCopyAttributeValue (AppState.swift line 439 area). The update path has to ship the binary, not just model weights, so the infrastructure described in this guide (Sparkle, EdDSA, ditto backup, crash-loop rollback) is required. A screenshot-based cloud agent can roll out a new model with a Firestore feature flag and no client release. Different shape of past-day release.

Can I build the same thing for my own open-source AI project?

Yes. The three load-bearing pieces are: (1) a small HTTP endpoint that polls api.github.com/repos/OWNER/REPO/releases and emits a Sparkle appcast (150 lines of Rust, or ~200 of Go/Node), (2) Sparkle framework on the client with EdDSA signing (generate_keys once, sign_update per release, store the public key in Info.plist's SUPublicEDKey), (3) a rollback manager on the client that tracks launch timestamps in UserDefaults and keeps a backup of the current .app bundle. Every file in Fazm's repo that implements this is open-source: Backend/src/routes/appcast.rs, Desktop/Sources/UpdateRollbackManager.swift, Desktop/Sources/UpdaterViewModel.swift, release.sh, verify-release.sh.

Try Fazm, then read its release pipeline in the open

Download the Mac app, watch Sparkle pull the latest appcast on first launch, then open Backend/src/routes/appcast.rs in the repo. Every part of the pipeline described on this page is open source at github.com/mediar-ai/fazm.

Download Fazm
fazm.AI Computer Agent for macOS
© 2026 fazm. All rights reserved.

How did this page land for you?

Comments

Public and anonymous. No signup.

Loading…