Minibar Capabilities and Permissions

Status v0.3 · for plugin authors and reviewers · Chinese original: 06-capabilities.md · API details: plugin-guide.en.md

Minibar is a platform. Every capability the host can obtain from macOS is meant to be available to plugins in a declared, granted and revocable form; conversely, the host never asks the system for anything no plugin has declared. This document spells both sentences out: the capability catalog, the permission and OS authorization behind each entry, how the host enforces least privilege, what is deliberately out of scope, and what comes next.


1. Five principles

#PrincipleHow it is enforced
P1Declared is the boundaryEverything a plugin may use is in manifest.json: permissions, allowedHosts, allowedSchemes. Anything undeclared fails with permission_denied / host_not_allowed / invalid_argument, checked once in the runtime and once more in the app process.
P2Least privilege, lazilyLaunch, installing a plugin and onboarding never ask macOS for a system permission. A system prompt appears only when a plugin is installed, the user granted it that permission, and it calls the API for the first time. If no installed plugin declares a permission, the corresponding prompt never appears on this Mac. Entitlements are a static minimum locked by a CI test (§5).
P3Side effects only on a tapOpening links or apps, writing the clipboard, freeing memory, running a Shortcut, keeping the Mac awake: all of it is callable only from an action (the user tapped a button); from render it throws not_in_action. Nothing happens quietly on refresh.
P4Per item, revocable, immediateThe install prompt explains every permission; Settings turns any single one off; after a revocation the next call is refused at once and held resources (such as a wake assertion) are released.
P5AuditableSource is viewable before installing; the install prompt lists hosts and URL schemes; every capability call is logged for the plugin; diagnostics exports never include plugin secrets.

2. Capability matrix

Status: ✅ shipped (v0.3) · 🗓 planned (milestone) · ⛔ never (§4)

Capabilityctx APIPermissionOS authorization / entitlementPhaseStatus
Time, locale, host versionctx.now() ctx.locale ctx.hostnone—any✅
Loggingctx.log() console.*none—any✅
Persistent storage (1 MB)ctx.storage.*nonesandbox container fileany✅
User settings (incl. secret)ctx.settings.*none—any✅
System metrics: CPU, memory, disk, load, network rate, batteryctx.system()none—any✅
Hardware: model, chip, core layout, memoryctx.system().hardwarenone—any✅
Memory pressure, swapctx.system().memory.pressure .swapUsednone—any✅
Granted permissionsctx.permissions.granted(name) .list()none—any✅
Hash, HMAC, Base64, UUIDctx.crypto.*none—any✅
HTTPS requests (allowlisted hosts)ctx.http.*networknetwork.clientany✅
Open http / https / mailto linksctx.openURL(), button.urlnone—action✅
Open another app's URL scheme (things:, obsidian:…)ctx.openURL()apps + allowedSchemes—action✅
Open / activate an appctx.apps.open(bundleID)apps—action✅
Running apps, frontmost appctx.apps.running()apps—any✅
Notificationsctx.notify()notificationsUNUserNotificationCenter (on first post)any (1 per minute)✅
Read clipboardctx.clipboard() / .read()clipboardper-session confirmation on the cardany✅
Write clipboardctx.clipboard.write(text)clipboard—action✅
Calendar (read + create events)ctx.calendar.upcoming() ctx.calendar.add()calendarTCC Calendars (on first call) · personal-information.calendarsread: any; add: action✅
Reminders (read + create + complete)ctx.reminders.incomplete() .add() .complete()remindersTCC Reminders (on first call) · same entitlementread: any; add / complete: action✅
Location (~1 km)ctx.location()locationTCC Location (on first call) · personal-information.locationany✅
Free up memoryctx.freeMemory()system—action✅
Run one of the user's Shortcutsctx.runShortcut(name, input?)system—action✅
Keep the Mac awakectx.keepAwake(enabled)systemIOPMAssertionaction✅
Custom web card (React or any framework)web nodenoneWebKit (package files only, no network)any✅
User-picked files / folders (read-only)ctx.files.pick() .list() .entries() .read() .forget()filesNSOpenPanel (the user chooses) + security-scoped bookmark · files.user-selected.read-only + files.bookmarks.app-scopepick / forget: action✅
Contacts (birthdays, upcoming)ctx.contacts.*contactsTCC Contacts · personal-information.addressbookany⏸ Not for now: little demand, the most sensitive data, and one more entitlement to justify in review; birthday reminders reach plugins through calendar / reminders once the user subscribes to them there
Expose plugin actions to Shortcuts / SiriApp Intents———🗓 M5 (not a permission; leaves the entitlement set unchanged)
Volume / output devicectx.audio.*systemCoreAudioaction🗓 M5 (evaluate)
Shell, scripts, arbitrary files, AppleScript, screen, keyboard/mouse, camera, microphone, listening sockets————⛔

3. Permissions in detail

network

clipboard

notifications

calendar / reminders

location

system

apps

files


4. Deliberately out of scope

CapabilityWhy
Shell / child processes / script executionImpossible in the sandbox, and one shell plugin would destroy trust in the whole market
Arbitrary file access, home directory listingSandbox and review; only files the user picks by hand in a dialog are readable, behind the files permission
AppleScript / Apple EventsNeeds a temporary-exception entitlement, high review risk; Shortcuts is Apple's sanctioned automation channel
Screen recording, Accessibility (keyboard/mouse control), input monitoringPrivacy and review; a menu bar dashboard does not need them
Camera, microphoneSame
Listening sockets / local serversPlugins have no background execution and nothing should connect into them
Other apps' data, KeychainSandbox isolation; a plugin's secrets live only in its own secret settings
Native code, dynamic librariesGuidelines 2.5.2 / 4.7; plugins are always JavaScript plus declarative UI (or a web card)

5. How the host enforces least privilege

5.1 Entitlements (static minimum, locked by CI)

FileKeyCapability
Minibar.entitlementsapp-sandboxall
network.clientregistry and plugin updates; network requests are made by the XPC service
personal-information.calendarscalendar / reminders
personal-information.locationlocation
files.user-selected.read-onlylocal install, linked folders, the files permission's file dialog
files.bookmarks.app-scoperemembering linked folders and files picks across launches
MinibarRuntime.entitlementsapp-sandbox, network.client, cs.allow-jitwhere plugin code and http run

Tests/MinibarCoreTests/EntitlementsTests.swift asserts that these two files contain exactly these keys; a new capability must change the test and this table together.

5.2 System prompts only on first use

System permissionTriggered byNever triggered by
Calendars / Remindersa granted plugin's first ctx.calendar / ctx.reminders calllaunch, install, onboarding, the moment a grant is given
Locationthe first ctx.location() callsame (creating CLLocationManager does not prompt)
Notificationsthe first ctx.notify()same

Onboarding only records plugin-level grants; its last page says that macOS may ask once when a plugin first uses a capability.

5.3 Revocation and cleanup

After a permission is revoked both checks apply immediately (no restart); revoking system or disabling / uninstalling a plugin releases its wake assertion; revoking clipboard also clears the session confirmation. Uninstalling removes the plugin's storage and settings.

5.4 For reviewers


6. Guidance for plugin authors

  1. Declare only what you use and explain it in description; the install prompt shows it verbatim.
  2. Degrade gracefully: render something without grants. Use ctx.permissions.granted('location') to choose a branch; catch permission_denied and offer a $allow:<permission> button so one tap grants it.
  3. While a system prompt is up (os_access_pending) do not retry in a loop; the host re-renders when the user answers.
  4. Side effects go in actions: button → action → ctx.openURL / apps.open / clipboard.write / freeMemory / runShortcut / keepAwake.
  5. URL schemes must be listed in allowedSchemes and need the apps permission.
  6. No secrets in code: use a secret setting and ctx.settings.get(); the host keeps it out of logs.

7. Roadmap

MilestoneCapabilitiesNotes
M3 (this round)system, apps, clipboard.write, keepAwake, crypto, permissions, web cardimplemented, awaiting on-device acceptance
M4✅ files (user-picked files/folders via security-scoped bookmarks); ✅ reminders.add / reminders.complete / calendar.add; ⏸ contacts (not for now, see §2)each follows declare → lazy prompt → action
M5App Intents (expose plugin actions to Shortcuts / Siri); audio (volume, output device); plugin-to-plugin messages (ctx.bus)decided after a demand check