Minibar 能力矩阵与权限模型

状态 v0.3 · 面向插件作者与审核员 · 英文版 capabilities.en.md · 接口细节见 03-plugin-api.md

Minibar 是一个基座:它向 macOS 申请得到的每一项能力,都应该以可声明、可授权、可撤销的方式开放给插件;反过来,插件没有声明的能力,基座绝不会替它向系统申请。本文是这两句话的完整展开——能力清单、每项能力对应的权限与系统授权、基座如何落实最小权限、哪些能力明确不做、以及后续计划。


1. 五条原则

#原则落实方式
P1声明即边界插件能用什么,全部写在 manifest.json:permissions、allowedHosts、allowedSchemes。没声明的调用一律 permission_denied / host_not_allowed / invalid_argument,运行时与 App 进程各检查一次(双重强制)。
P2最小权限,懒触发基座启动、安装插件、引导流程都不会向 macOS 申请任何系统权限。系统授权弹窗只在"已安装 + 用户已授予该插件 + 插件第一次真正调用"时出现。没有任何插件声明某项权限,这台 Mac 上就永远不会出现对应的系统弹窗。entitlements 是静态最小集,由 CI 测试锁死(§5)。
P3副作用只响应用户点击打开链接/App、写剪贴板、释放内存、运行快捷指令、保持唤醒——所有"对 Mac 做点什么"的调用只能在 action(用户点了按钮)里执行,render 里调用抛 not_in_action。杜绝"每次刷新偷偷做事"。
P4逐项、可撤销、即时生效安装弹窗逐条解释每项权限;设置页可单独关闭任意一项;撤销后插件下一次调用立即被拒,并释放它持有的资源(如唤醒断言)。
P5可审计源码可在安装前查看;安装弹窗列出能访问的域名与 URL scheme;每次能力调用写入插件日志;诊断导出不含插件密钥。

2. 能力矩阵

状态:✅ 已实现(v0.3)· 🗓 计划(标注里程碑)· ⛔ 不做(见 §4)

能力ctx 接口权限系统授权 / entitlement阶段状态
时间、语言、宿主版本ctx.now() ctx.locale ctx.host无—任意✅
日志ctx.log() console.*无—任意✅
持久存储(1 MB)ctx.storage.*无沙箱容器文件任意✅
用户设置(含 secret)ctx.settings.*无—任意✅
系统指标:CPU、内存、磁盘、负载、网络速率、电池ctx.system()无—任意✅
硬件信息:机型、芯片、核心布局、内存总量ctx.system().hardware无—任意✅
内存压力、交换空间ctx.system().memory.pressure .swapUsed无—任意✅
已授予的权限ctx.permissions.granted(name) .list()无—任意✅
哈希、HMAC、Base64、UUIDctx.crypto.*无—任意✅
HTTPS 请求(白名单域名)ctx.http.*networknetwork.client任意✅
打开 http / https / mailto 链接ctx.openURL()、button.url无—action✅
打开其他 App 的 URL scheme(things:、obsidian:…)ctx.openURL()apps + allowedSchemes—action✅
打开 / 激活某个 Appctx.apps.open(bundleID)apps—action✅
正在运行的 App 列表、前台 Appctx.apps.running()apps—任意✅
通知ctx.notify()notificationsUNUserNotificationCenter 授权(首次发送时)任意(每分钟 1 条)✅
读剪贴板ctx.clipboard() / .read()clipboard每次 App 会话首次读取再确认任意✅
写剪贴板ctx.clipboard.write(text)clipboard—action✅
日历(读 + 新建日程)ctx.calendar.upcoming() ctx.calendar.add()calendarTCC 日历(首次调用时)· personal-information.calendars读任意;add 仅 action✅
提醒事项(读 + 新建 + 完成)ctx.reminders.incomplete() .add() .complete()remindersTCC 提醒事项(首次调用时)· 同上 entitlement读任意;add / complete 仅 action✅
定位(约 1 km)ctx.location()locationTCC 定位(首次调用时)· personal-information.location任意✅
释放内存ctx.freeMemory()system—action✅
运行用户的快捷指令ctx.runShortcut(name, input?)system—action✅
阻止系统休眠(保持唤醒)ctx.keepAwake(enabled)systemIOPMAssertionaction✅
自定义 Web 卡片(React 等任意框架)web 节点无WebKit(仅插件包内文件,无网络)任意✅
用户选择的文件/文件夹(只读)ctx.files.pick() .list() .entries() .read() .forget()filesNSOpenPanel(用户亲自选)+ security-scoped bookmark · files.user-selected.read-only + files.bookmarks.app-scopepick / forget 仅 action✅
联系人(生日、即将到来)ctx.contacts.*contactsTCC 通讯录 · personal-information.addressbook任意⏸ 暂不做:需求弱、敏感度最高,且多一个 entitlement 要向审核解释;生日提醒可由用户在日历/提醒事项里订阅后经 calendar / reminders 读到
把插件动作暴露给快捷指令 / SiriApp Intents———🗓 M5(非权限项,不影响最小权限集)
音量 / 输出设备ctx.audio.*systemCoreAudioaction🗓 M5(评估)
Shell、脚本、任意文件、AppleScript、屏幕、键鼠、摄像头、麦克风、网络监听————⛔

3. 各权限详解

network

clipboard

notifications

calendar / reminders

location

system

apps

files


4. 明确不做的能力

能力原因
Shell / 子进程 / 脚本执行沙箱下不可行;一个 shell 插件就能毁掉整个市场的信任
任意文件读写、用户目录遍历沙箱 + 审核;只有用户在对话框里亲手选的文件以 files 权限只读开放
AppleScript / Apple Events需要 temporary-exception entitlement,审核高风险;快捷指令是 Apple 认可的自动化通道
屏幕录制、辅助功能(键鼠控制)、输入监听隐私与审核;菜单栏信息面板不需要
摄像头、麦克风同上
网络监听 / 本地服务器插件没有后台执行,也不该让别的进程连进来
读其他 App 的数据、Keychain沙箱隔离;插件密钥只存在自己的 secret 设置中
原生代码、动态库2.5.2 / 4.7;插件永远是 JavaScript + 声明式 UI(或 Web 卡片)

5. 基座如何落实最小权限

5.1 entitlements(静态最小集,CI 锁定)

文件键对应能力
Minibar.entitlementsapp-sandbox全部
network.clientregistry、插件更新;network 权限的请求实际在 XPC 服务里发出
personal-information.calendarscalendar / reminders
personal-information.locationlocation
files.user-selected.read-only本地安装、链接文件夹、files 权限的文件对话框
files.bookmarks.app-scope跨启动记住链接文件夹与 files 权限选中的项
MinibarRuntime.entitlementsapp-sandbox、network.client、cs.allow-jit插件代码与 http 的执行环境

Tests/MinibarCoreTests/EntitlementsTests.swift 断言这两个文件的键恰好是上面这些;新增能力必须同时改测试和本表。

5.2 系统授权只在首次调用时触发

系统权限触发点绝不触发的时机
日历 / 提醒事项已授权插件第一次调用 ctx.calendar / ctx.reminders启动、安装、引导、授予权限的那一刻
定位第一次调用 ctx.location()同上(CLLocationManager 创建不弹窗)
通知第一次 ctx.notify()同上

引导流程只写入插件级授权,不碰系统弹窗;引导结束页明确告知"插件首次使用时 macOS 可能询问一次"。

5.3 撤销与清理

撤销某项权限后:运行时与 App 进程的检查立即生效(无需重启);system 被撤销或插件停用/卸载时释放唤醒断言;clipboard 撤销后会话确认同时清除。卸载插件删除其存储与设置。

5.4 审核员视角


6. 插件作者指南

  1. 只声明用得到的权限,并在 description 里说清楚用途——安装弹窗会原样展示。
  2. 优雅降级:没授权时也要能渲染。用 ctx.permissions.granted('location') 决定是否走定位分支;catch permission_denied 并给一个 $allow:<permission> 按钮,用户一点即授予。
  3. 系统弹窗期间(os_access_pending)不要重试死循环,基座会在用户回答后自动重渲染。
  4. 副作用放 action:按钮 → action → ctx.openURL / apps.open / clipboard.write / freeMemory / runShortcut / keepAwake。
  5. URL scheme 必须写进 allowedSchemes,并申请 apps。
  6. 不要把密钥写进代码:用 secret 类设置,ctx.settings.get() 读取;基座不会把它写进日志。

7. 路线图

里程碑能力备注
M3(本轮)system、apps、clipboard.write、keepAwake、crypto、permissions、Web 卡片已实现,待真机验收
M4✅ files(用户选择的文件/文件夹,security-scoped bookmark);✅ reminders.add / reminders.complete / calendar.add;⏸ contacts(暂不做,见 §2)每项都走本文的声明 → 懒授权 → action 流程
M5App Intents(把插件动作暴露给快捷指令 / Siri);audio(音量、输出设备);插件间消息(ctx.bus)评估需求后再定