## Summary The Buzz 1.0 desktop app now lives in [block/buzz-app](https://github.com/block/buzz-app). This adds a banner to the 0.X desktop app that tells users, and lets them download the new app's installer without leaving the app. - A full-width banner sits under the top chrome: "A new Buzz app is here! This version is being retired, so grab the new one whenever you get a chance." - **Upgrade** downloads the installer for this machine inside the app. A dialog shows the progress, then the saved file's name and folder with **Show in Finder** (worded per platform) and **Done**. The file goes to the Downloads folder and is revealed in the file manager. It is never opened, mounted or run. - **Cancel**, Escape or the dialog's close button stops a running download and removes the partial file. - When no installer can be resolved for this machine (unsupported platform, or the manifest fetch fails or is slow), **Upgrade** opens `https://buzz.xyz` in the browser instead. - **More info** opens the [upgrade guide](https://github.com/block/buzz-app/blob/main/docs/upgrading.md) in the browser. - Dismissing (×, labelled "Remind me later") stores a timestamp in `localStorage`. The banner is read once per mount, so it comes back on the next launch (or community switch) once 24h have passed. - It is hidden in the huddle room window. ## Demo https://github.com/user-attachments/assets/1f1916e8-f32d-4ec0-8e60-fe7c85dd4a57 Recorded on macOS (Apple Silicon) from a native debug build with the real Rust backend: a full download, **Done**, then a second download cancelled part-way. - The network is throttled through a local proxy so the progress stays visible. Unthrottled, the installer arrives in about two seconds. - The app was launched with a scratch home folder, so the path in the dialog is a scratch path, and the Finder window that the reveal opens is left out of the recording. - Presses are accessibility presses, marked with an orange ring. The OS cursor is not shown. ## How the download works - **Which installer:** a Tauri command reports this install's key (`macos-arm64`, `macos-x64`, `windows-x64`, `linux-appimage` or `linux-deb`). The webview reads the matching entry from the [buzz-app download manifest](https://raw.githubusercontent.com/block/buzz-app/download-feed/downloads.json) once per banner mount. - **Validation:** Rust re-validates the URL on every call. It must be `https://github.com/block/buzz-app/releases/download/<tag>/<file>`, with a restricted tag and file-name character set and the extension this machine installs. The webview names a URL and nothing else. - **Redirects:** a dedicated HTTPS-only client follows at most 5 redirects, and only to `github.com`, `objects.githubusercontent.com` or `release-assets.githubusercontent.com`. - **Limits:** 512 MiB, 15 s to connect, 30 s for headers, 30 s without a byte, 60 min in total. One download at a time. - **Saving:** the body is written to `<name>.<random>.part` in the Downloads folder, then renamed to `<name>` (or `<name> (1)` and so on) without ever replacing an existing file. The partial file is removed on failure, on cancel and at app exit. A partial left by a crash or force-quit is removed on the next launch. - **Downloaded-from-the-internet marking:** macOS gets the `com.apple.quarantine` attribute a browser would write, and Windows gets the Mark of the Web (`Zone.Identifier`). If the mark cannot be written, the download fails and the file is not kept. - **Failures:** the dialog says what went wrong in plain words. Transient failures lead with **Try again**. Failures a retry cannot fix lead with **Download in browser**, which opens the validated installer URL. ## Testing - Unit: `deprecationBannerVisibility.test.mjs`, `newAppInstaller.test.mjs` (manifest and URL rules), `newAppInstallerDownload.test.mjs` (dialog state), and a jsdom test of the download hook. - Rust: `new_app_installer_tests.rs` covers URL validation, the redirect policy, limits, naming without overwrite, cancel and shutdown cleanup, launch cleanup, and the quarantine and Mark of the Web values. CI runs this crate's tests on Linux and on `windows-latest`, so the Mark of the Web test writes a real alternate data stream there. The quarantine test runs on macOS only, which CI builds but does not test. - Smoke: `tests/e2e/deprecation-banner.spec.ts` drives the banner, the download dialog, each failure's actions, cancel and the keyboard paths against the mock bridge. - Native, macOS arm64 debug build: the download was saved with `com.apple.quarantine` set and revealed in Finder; cancel removed the partial; the download kept running while the window was hidden; SIGTERM and a menu quit mid-download removed the partial; after a force kill the next launch removed it. Not verified by hand: - The Windows and Linux flows in a running app, including SmartScreen on Windows. - The Gatekeeper prompt when opening the downloaded DMG, and the macOS Downloads-folder consent prompt, which need a signed build. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: Kalvin Chau <kalvin@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Matt Toohey <contact@matttoohey.com>
Buzz
Important
This is the deprecated Buzz 0.X desktop app. Buzz 1.0 lives in block/buzz-app. Build new desktop features there. Change this directory only to fix a 0.X bug, or to keep it compiling after a shared crate change.
Desktop chat shell with:
- Tauri + React + TypeScript + Vite
- Tailwind CSS
- shadcn/ui-ready shared components
- Biome (lint/format/check)
- Feature-driven frontend structure
Scripts
pnpm dev- run the web frontendpnpm tauri dev- run the desktop apppnpm build- typecheck and build frontendpnpm typecheck- TypeScript checkspnpm lint- Biome lintpnpm format- Biome format (write)pnpm check- Biome check
Structure
src/shared- reusable app-wide code (ui,lib,styles)src/features- feature modules (vertical slices)src/app- top-level app composition
Required-community guidance in the legacy app
The legacy desktop reads public NIP-11 metadata before each WebSocket connection.
Only requires_nip_fi: true shows the upgrade card. Missing/false requirements
keep the existing connection flow. Discovery failures retain an unknown-policy
state and continue the existing socket/authentication path; the relay enforces
admission. Query/fragment URLs keep their existing socket behavior; the native
discovery reader rejects them before making an HTTP request. Credentials never
reach a fallback dial. Only explicit required policy opens the upgrade modal. Switching
communities invalidates pending checks. The card does not
reset keys or remove saved communities.
The upgrade card directs public users to the fixed https://buzz.xyz URL to
download an up-to-date Buzz client on every supported desktop platform and for
every required community.
If the browser cannot open, the card shows the public URL for manual navigation.
Discovery never supplies a trusted download or sign-in origin.