Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,10 @@ trust the three BarKeep commands. Codex and ChatGPT desktop share these hooks.

| Feature | Permission | Why |
|---|---|---|
| Busy Bar connection | Local Network | Required for the bar's local HTTP API over both its USB network interface and Wi-Fi. BarKeep asks on first launch. |
| Notification forwarding | Full Disk Access | macOS stores delivered notifications in a TCC-protected SQLite DB (`~/Library/Group Containers/group.com.apple.usernoted/db2/db`). BarKeep polls it read-only; only titles/bodies matching your app filter are read, and they go straight to the bar over your local USB or Wi-Fi connection. |
| Calendar auto-busy | Calendar (full access) | To know when you're in an event. |
| Everything else | none | Mic detection reads CoreAudio device state, not audio. |
| Microphone detection | None | Mic detection reads CoreAudio device state, not audio. |

Grant Full Disk Access in System Settings → Privacy & Security → Full Disk
Access → add the installed `BarKeep.app`. Official release builds use a stable
Expand Down
7 changes: 7 additions & 0 deletions Sources/BarKeep/AppState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ final class AppState {
var calendarAccessGranted: Bool { calendarMonitor.accessGranted }

let client: BusyBarClient
private let localNetworkPermissionTrigger = LocalNetworkPermissionTrigger()
private let micMonitor = MicMonitor()
private let notificationWatcher = NotificationWatcher()
private let calendarMonitor = CalendarMonitor()
Expand Down Expand Up @@ -205,6 +206,12 @@ final class AppState {
self.pingHost = defaults.string(forKey: "pingHost") ?? "1.1.1.1"
self.weatherCelsius = defaults.bool(forKey: "weatherCelsius")
self.client = BusyBarClient(host: host, token: token)
localNetworkPermissionTrigger.onAccessAvailable = { [weak self] in
Task { @MainActor [weak self] in
await self?.refreshDeviceStatus()
}
}
localNetworkPermissionTrigger.requestAccess()
slackSync.token = self.slackToken
syncPingLoop()
syncWeatherLoop()
Expand Down
40 changes: 40 additions & 0 deletions Sources/BarKeep/LocalNetworkPermissionTrigger.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Foundation
import Network

/// Performs an explicit local-network operation so macOS presents its privacy
/// prompt before the Busy Bar client begins making direct-IP requests.
final class LocalNetworkPermissionTrigger: @unchecked Sendable {
private let queue = DispatchQueue(label: "dev.barkeep.local-network-permission")
private var browser: NWBrowser?
var onAccessAvailable: (@Sendable () -> Void)?

func requestAccess() {
guard browser == nil else { return }

let parameters = NWParameters()
parameters.includePeerToPeer = true

let browser = NWBrowser(
for: .bonjour(type: "_barkeep._tcp", domain: nil),
using: parameters
)
browser.stateUpdateHandler = { [weak self] state in
switch state {
case .ready:
self?.onAccessAvailable?()
self?.stop()
case .failed, .cancelled:
self?.stop()
default:
break
}
}
browser.start(queue: queue)
self.browser = browser
}

private func stop() {
browser?.cancel()
browser = nil
}
}
8 changes: 7 additions & 1 deletion packaging/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,18 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.8</string>
<string>1.0.9</string>
<key>LSMinimumSystemVersion</key>
<string>14.0</string>
<key>LSUIElement</key>
<true/>
<key>NSCalendarsFullAccessUsageDescription</key>
<string>BarKeep sets your Busy Bar to busy during calendar events and shows a countdown to your next meeting.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>BarKeep connects directly to your Busy Bar over USB or Wi-Fi on your local network.</string>
<key>NSBonjourServices</key>
<array>
<string>_barkeep._tcp</string>
</array>
</dict>
</plist>
Loading