Skip to content

feat(desktop): run the macOS Host under its own identity - #6587

Merged
M4n5ter merged 6 commits into
rust-previewfrom
feat/cua-host-identity
Nov 3, 2026
Merged

M4n5ter merged 6 commits into
rust-previewfrom
feat/cua-host-identity

Conversation

@UncertaintyDeterminesYou4ndMe

@UncertaintyDeterminesYou4ndMe UncertaintyDeterminesYou4ndMe commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #6582.

macOS grants privacy permissions such as Accessibility and Screen Recording to the application responsible for a process. The Host belonged to whichever application started it first: Maka, or the terminal a CLI ran in. So users had to find and allow that application, and a grant could follow the wrong one. Allowing a terminal also gives the permissions to everything else that terminal runs.

Maka.app now carries Contents/Helpers/Maka Host.app. Its executable starts itself again with responsibility disclaimed (responsibility_spawnattrs_setdisclaim), then runs the Host. It passes stdio through, forwards SIGTERM/SIGINT/SIGHUP and exits with the Host's status. Interactive, activation and launchd starts run the Host through it, so the request dialog and System Settings name "Maka Host". Without a helper, the Host runs directly as before.

Choices the diff does not explain on its own:

  • The helper is installed once per account. macOS ties a grant to the bytes of the helper's executable (ad-hoc and linker signatures pin the cdhash), and every package lives in a directory of its own. So the helper is copied to ~/Library/Application Support/Maka/Maka Host.app and replaced, atomically with RENAME_SWAP, only when a package carries a higher CFBundleVersion. A new build of the same version keeps the copy and its grants; moving a byte-identical bundle keeps them too (measured on macOS 26.6). Bumping hostApp.version in scripts/rust/build-cli.mjs makes every user grant again. A launchd service installs the helper only when it starts, not when it is observed, stopped or removed.
  • Every TCC service now attributes to "Maka Host". Not only Accessibility and Screen Recording: Files & Folders, Full Disk Access and Automation prompts raised by the Host also name "Maka Host", and existing grants given to Maka or a terminal no longer apply to the Host.
  • The helper starts only the Host. It accepts only a program named maka with host candidate or host service-run, so it is not a general way to borrow its grants. A process of the same account can still replace that maka, so the grants protect no more than the account does; Developer ID signing would not change that.
  • Signals and lifetime. The shells block termination signals until the child is known, so a signal that arrives during startup reaches the Host instead of orphaning it. If a shell is killed outright, the Host still ends with its owner: an interactive Host when its owner closes stdin, a launchd service with the process group launchd removes.
  • Package format. The helper is a required member of darwin packages, like DISCLAIMER in fix(release): ship the standard disclaimer in Rust distributions #6500, so an older CLI rejects a package that carries it.

Review follow-up

The branch includes the updated permission-probe implementation and latest rust-preview. Packaging tests still validate the helper identity, executable and permissions, without pinning its bundle version to the current value.

AI use

  • Generative tooling made a substantive contribution

Tool(s) and scope: Claude Code wrote the change and this description, and ran the macOS permission experiments; the commit carries Co-Authored-By: Claude Opus 5.5. Codex implemented the review follow-up fixes.

Native Computer Use on macOS needs Accessibility and Screen Recording,
which macOS grants to the application that started the Host. Without
them the first AX call failed with an opaque -25211 deep inside a tool.

Check both grants before native work, ask macOS to show its request
the first time one is missing, and fail with a single
permission_required: line naming the application to allow. The check
is a real call (an AX query to this user's Dock, a ScreenCaptureKit
listing), because AXIsProcessTrusted and CGPreflightScreenCaptureAccess
keep their first answer in a Host without a run loop; a grant the user
turns on is seen by the next call without restarting the Host. A
confirmed grant is remembered, and failures that a missing grant
explains (AX -25211, a refused ScreenCaptureKit listing) check again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
macOS grants privacy permissions such as Accessibility and Screen
Recording to the application responsible for a process. The Host
belonged to whichever application started it first: Maka, or the
terminal a CLI ran in. Users had to find and allow that application,
and a grant could follow the wrong one.

Maka.app now carries Helpers/Maka Host.app. Its executable starts
itself again with responsibility disclaimed and runs the Host, passing
stdio through, forwarding termination signals and returning the Host's
status. Interactive, activation and launchd starts run the Host through
it, so its grants belong to "Maka Host".

macOS ties a grant to the bytes of the helper's executable, and each
package lives in a directory of its own. The helper is therefore copied
once into the account data directory and replaced only when a package
carries a higher bundle version. Without a helper, the Host runs
directly as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@M4n5ter M4n5ter left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

English

The cleanup is pushed in d1744d200. This branch now includes the #6582 fix and the latest rust-preview, with no merge conflicts.

The separate Host helper has a concrete purpose: keeping the macOS permission identity stable across launch paths. I kept that design and the packaging checks for its identity, executable contents and executable permissions. I removed the assertions that pinned both helper version fields to 1; those assertions only repeated the current constant and would need changing during a legitimate helper upgrade.

Validation: the related packaging/build/launcher tests passed 9/9 locally. The actual macOS helper launch and TCC attribution were not exercised on this WSL machine. The new GitHub CI run is currently queued.

简体中文

已在 d1744d200 完成精简。该分支已包含 #6582 的修复和最新 rust-preview,没有合并冲突。

独立 Host helper 有明确用途:让不同启动路径使用稳定的 macOS 授权身份。因此保留了这套设计,以及对 helper 身份、可执行文件内容和执行权限的打包验证。删除了将两个 helper 版本字段写死为 1 的断言;它们只是在复述当前常量,正常升级 helper 时也会要求改测试。

验证:相关打包、构建和启动器测试本地 9/9 通过。WSL 下未实测 macOS helper 启动和 TCC 授权归属。新的 GitHub CI 目前仍在排队。

@github-actions github-actions Bot added the effort/XL Under 2500 readable lines label Nov 3, 2026
@M4n5ter
M4n5ter merged commit 8a83fcf into rust-preview Nov 3, 2026
43 checks passed
@M4n5ter
M4n5ter deleted the feat/cua-host-identity branch November 3, 2026 04:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/XL Under 2500 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants