GitHub has a special talent for making me reopen the same browser tab. A pull request needs a review, a check turns red, somebody mentions me, or a release lands in a repository I follow. None of those events deserves my full attention all day, but checking whether one happened still breaks whatever I was doing. Email arrives late and mixes the useful signal with everything else. A pinned tab has the complete picture, but only after I go looking for it.
PullBar showed me how useful that always-visible surface could be, but it is deliberately focused on pull requests. My GitHub day does not stop there. It also includes issues, the full notification inbox, workflow runs, releases, saved searches, multiple accounts, Enterprise, and the small actions that clear the queue. That gap became gbar, a native GitHub menu bar companion for macOS that keeps the important state one glance away and leaves the actual deep work on GitHub.
A general GitHub bar, not a tiny GitHub
The name is the product brief. gbar is meant to be a general GitHub bar, not a complete desktop client squeezed into 400 pixels. Its job is to answer two questions quickly: what needs me, and what can I finish without opening another window? That produces a deliberately broad but shallow surface:
- Pull requests and issues. Both show age, author, repository, and why they need attention. Pull requests add approvals and diff size.
- Checks and quick actions. Each PR exposes its individual CI checks, plus approve, merge, and open-in-browser actions when the current state allows them.
- A real notification inbox. Notifications can be filtered by why they arrived and marked read individually or all at once.
- Watchlists and saved queries. Selected repositories feed Actions and Releases tabs, while any GitHub pull-request or issue search query can become its own section.
- More than one GitHub. Multiple accounts, organisations, and GitHub Enterprise instances can share one menu, with the source account kept visible.
The menu-bar badge is the smallest version of that idea. It can show the number of PRs waiting for my review without opening anything. Clicking expands the context; Approve opens an inline composer, while Merge opens a method picker before anything is submitted. If I need to read a discussion, inspect a diff, or debug a workflow, gbar gets out of the way and opens the exact GitHub page. The boundary is intentional.
That also keeps the product honest. The repository and the app are source-available, but gbar is not trying to reproduce GitHub's entire interface or invent a second place where work lives. It is a status surface with a few sharp actions. The best result is often that I look at it for two seconds and do nothing.
The menu bar fought back
The first version was the obvious stack: Swift 6, SwiftUI, macOS 14, Tuist, and MenuBarExtra. The app is an LSUIElement agent, so it has no Dock icon and no normal main window. SwiftUI made the rows, tabs, filters, and settings pleasant to build. Then the edges of a real menu-bar app appeared. I wanted a right-click Quit menu, exact control over dismissal, and a popover that opened on the display whose menu bar I had actually clicked.
The current implementation therefore owns an AppKit NSStatusItem and an NSPopover, then hosts the SwiftUI menu inside it. Left-click toggles the popover; right-click opens a transient menu. Global and local event monitors close it when I click away, while an app-resign observer catches Command-Tab and Spaces changes. It is more code than MenuBarExtra, but the behavior is deterministic instead of almost right.
Settings needed another bridge. A menu-bar agent has no ordinary window from which SwiftUI can resolve openWindow, so gbar keeps a one-pixel hidden scene alive as an opener. It briefly promotes the app to a regular activation policy when Settings appears, then drops back to an accessory when the last visible window closes. This is the kind of machinery that nobody should notice. If it works, gbar simply feels like it belongs in the menu bar.
GitHub state is not a list
Fetching a search result is easy. Keeping fifty pull requests accurate without wasting the API budget is not. gbar first runs GitHub search queries for the sections a user enabled. Pull requests then need more detail: reviews, merge rules, the head commit, and its individual check runs. A naive REST implementation turns that into several requests per row on every poll, exactly the kind of invisible fan-out that works during development and falls apart with a busy account.
The current client batches pull-request hydration through GitHub GraphQL. Its query builder aliases as many as 25 repository and PR nodes into one request, fetches review and check-rollup data, and maps the response back into the same models the REST path already uses. The important part is the fallback: if an Enterprise server lacks a requested GraphQL field or a node cannot be resolved, gbar returns to REST for that account or PR instead of making the entire menu fail.
1static let graphQLBatchSize = 2523for chunk in refs.chunked(into: Self.graphQLBatchSize) {4 let payload = GitHubGraphQL.batchQuery(for: chunk)5 let request = try makeGraphQLRequest(url: endpoint, body: payload)6 let data = try await execute(request)7 let bundles = try GitHubGraphQL.decodeBatch(data, for: chunk)8 result.merge(bundles) { _, new in new }9}Polling still needs restraint. A 16 MiB memory-only URL cache revalidates unchanged REST responses with ETags, so idle data can return 304 Not Modified without spending the normal GitHub rate-limit budget. Nothing from private repositories is cached to disk. If GitHub responds with a primary or secondary rate limit, the next poll waits until the server's reset or retry time instead of hammering the same endpoint on the configured cadence.
On the REST fallback path, the store also remembers which PR state it has already hydrated. If GitHub's search timestamp has not changed, the next wave can refresh only the check runs and reuse the gate and head commit. Mergeable PRs are deliberately excluded from that shortcut because a base-branch change can make them stale without updating the PR itself. The optimization is useful only while the Merge button remains truthful.
The button must tell the truth
Approve and Merge are small buttons with large consequences. gbar derives an action gate from the PR state, the viewer's latest review, repository permissions, allowed merge strategies, and GitHub's mergeability result. Only definitively invalid actions are hidden; missing or still-hydrating signals remain available until GitHub provides a firm answer. The awkward case begins after an approval: GitHub recalculates branch-protection state asynchronously, so the first refetch can still say blocked even when the approval was the final requirement.
Instead of forcing a full refresh or making the user reopen the menu, gbar starts a short background readiness poll with capped backoff. It rechecks the gate for roughly eighteen seconds and reveals Merge when GitHub catches up. The PR stays in place even if it would disappear from a review-requested search after approval, which lets the whole review-and-merge sequence finish where it started.
That poll exposed a more interesting bug: an older full hydration wave could finish after the targeted poll and overwrite its newer gate. The fix is an issue-time write clock. Every gate fetch receives a sequence before it goes to the network, and only a newer sequence may replace live state. A menu that looks like a list is actually coordinating searches, GraphQL batches, REST fallbacks, CI updates, and user mutations that can all finish in a different order.
No backend, by design
gbar talks directly to the GitHub API. The primary sign-in path uses GitHub's OAuth device-flow implementation, which needs a public client ID but no client secret or callback server. A personal access token remains available for self-hosted or unusual setups, and the API base URL can point at GitHub Enterprise. There is no separate gbar account between the app and GitHub, and no backend collecting a second copy of the data.
Tokens live in the macOS Keychain. A properly signed build uses the data-protection keychain and an access group, avoiding the repeated permission prompts that ad-hoc signatures can trigger after rebuilds. Teamless source builds cannot carry that entitlement, so they transparently fall back to the file-based Keychain. Logout removes both possible copies so a token written by an older build cannot survive unnoticed.
This is also why the free build is not a cut-down edition. There are no paid feature gates, outbound product telemetry, or separate hosted gbar account to pay for. Users can download the signed app, bring their GitHub identity, and use every feature. The architecture is simpler because the privacy promise and the product model point in the same direction.
Shipping a tiny app is not tiny
The app may live behind an 18-pixel status item, but distributing a native macOS binary still means signing identities, entitlements, provisioning profiles, hardened runtime, notarisation, stapling, a DMG, and updateable installation paths. gbar's release workflow promotes stage to main, tags the version from the Tuist manifest, builds the app, signs and notarises the DMG, uploads a stable download, and updates the Homebrew cask.
- No third-party app dependencies. The Tuist app target depends only on Apple's frameworks; the GitHub client, store, Keychain layer, design system, and menu behavior live in the repository.
- Twenty-five PRs per GraphQL batch. The cap keeps each response below GitHub's node limits while collapsing the REST request fan-out.
- Fourteen GitHub releases in six days. Release entries from
v0.1.0throughv0.6.0were published between July 1 and July 6, 2026, while the signing and product edges were still being hardened.
Even the icon influenced CI. gbar uses Apple's newer Icon Composer bundle rather than a conventional asset catalog. Xcode 16 copied that bundle as a raw resource and produced an app without the intended icon, so the release job moved to macOS 26 with Xcode 26, where actool understands the format. A square image can reach surprisingly far into a build pipeline.
The visible result is pleasantly boring: download a signed and notarised DMG from GitHub Releases, or install the cask with one command, and macOS opens it without a Gatekeeper detour. Getting there required several release fixes around signing, Keychain entitlements, credentials, and the branded DMG. Shipping was not the line after building the app. It was another feature.
Source-available on purpose
The entire app can be read, built, self-hosted, modified, and redistributed for noncompeting purposes under the PolyForm Shield License. That makes gbar source-available, not open source in the OSI sense. The distinction is deliberate: people can inspect what touches their GitHub token and adapt the tool, but they cannot turn the same code into a competing product or service.
I prefer stating that boundary plainly over using the friendlier label and hiding the exception. Trust matters more for a developer tool with access to private repositories than an ambiguous badge does. The source shows the direct API calls, the Keychain handling, and the absence of telemetry. The license says what can be done with that source. Both are part of the product contract.
One glance, then back to work
gbar does not make GitHub smaller. It makes the part that changes between visits visible sooner. Reviews, failed checks, mentions, and releases can sit in the menu bar without becoming a permanent browser ritual. When an item needs real attention, one click still lands on GitHub with all its context. That limit is not unfinished scope; it is what keeps the menu useful.
There are still edges to improve. Polling cannot be as immediate as webhooks, Enterprise installations vary, and every new quick action adds another state that must stay honest under concurrency. Those are better problems than pretending a menu can replace a full collaboration platform. The test for each feature remains simple: does it reduce a context switch without creating a second system to manage?
1brew install --cask jaylann/tap/gbarThe app lives at gbar.lanfermann.dev, and the complete source and issue tracker are in the GitHub repository. The latest signed build is available as a direct DMG download or under Releases. Once installed, it should spend most of its life doing exactly what the title promises: waiting quietly until one glance is enough.

