Skip to content

fix(macos): allow terminal tools to request audio input - #1485

Closed
mao13811364454 wants to merge 1 commit into
xiaolai:mainfrom
mao13811364454:fix/macos-terminal-audio-input
Closed

mao13811364454 wants to merge 1 commit into
xiaolai:mainfrom
mao13811364454:fix/macos-terminal-audio-input

Conversation

@mao13811364454

Copy link
Copy Markdown
Contributor

Summary

Audio capture launched from VMark's integrated terminal is denied by macOS TCC because the responsible app is missing the Hardened Runtime audio-input entitlement. FFmpeg can keep writing all-zero samples without a permission prompt.

Add the entitlement and microphone usage description so terminal recording tools can request user consent. Document device routing and short-recording verification. A locally ad-hoc-signed release build requested permission and captured the known BlackHole test signal after the user allowed access.

Policy Gates (Required)

Linked Issue

Fixes #1483

Type of Change

  • Bug fix
  • Feature
  • Docs
  • Refactor
  • Test-only
  • Other

What Changed

  • Add com.apple.security.device.audio-input to the existing signing entitlement file.
  • Add NSMicrophoneUsageDescription explaining terminal-initiated recording.
  • Explain macOS permission and routing in the integrated terminal guide.

The entitlement file is currently used for both app and MCP sidecar signing. This candidate retains that arrangement, so the new declaration is shared. No signing protection or consent requirement is disabled. No new recorder, transcription service, dependency, or startup permission request is added.

Validation

  • plutil -lint src-tauri/Info.plist src-tauri/sidecar-entitlements.plist
  • git diff --check
  • pnpm check:predelta — 46/46 passed, Node 22.23.3 / pnpm 10.33.0, full Git history and tokei installed.
  • pnpm check:all — exit 0 on upstream 4b1641c; 100 gate files, 1,830 app files, and both server suites passed. App coverage: statements 95.46%, branches 91.85%, functions 94.53%, lines 96.40%.
  • pnpm --dir website build
  • Inspect the built app's entitlements and merged Info.plist; audio-input=true and the usage string are present. Hardened Runtime retained; codesign --verify --deep --strict passed.
  • Independently launch a release-style build with an isolated bundle identifier; user granted consent and a known 880 Hz tone was captured from BlackHole in both channels. FFmpeg exited 0. Physical microphone input was not tested.
  • With microphone permission switched off and the test app restarted, TCC denied its microphone requests and FFmpeg could not open BlackHole. No audio file was created; the probe stopped FFmpeg after its timeout. The user restored permission afterward.
  • A separate launch-only observation for absence of a permission prompt was not recorded; this patch adds no startup permission-request code.
  • The bundled MCP sidecar --health-check returned status=ok, version=0.9.87, toolCount=9. This is a startup self-check, not a full editor/MCP integration test.

The patch is now based on upstream 4b1641c, with all 46 pre-delta gates passing there. The signed-build and allowed-input capture evidence was collected on the earlier 241a253 baseline; none of the three patched files changed in the upstream update. This test build is ad-hoc signed with identifier app.vmark.audio-test, not notarized or signed with the maintainer identity. The original installed app was untouched. The final check:all passed on 2026-09-30. No tests or gates were weakened.

UI Evidence (if applicable)

No application UI change. Permission-prompt behavior and known-signal capture were verified locally. No screenshot is attached.

PR Checklist

Daily-use acceptance

After restoring permission, the reporter ran the usual local recording script from the test app’s integrated terminal, recorded a spoken-audio segment, and completed local Whisper transcription. The WAV contained 107.5 seconds of audio and 1,474,263 nonzero samples. The reporter checked the transcript and confirmed its opening and ending matched the played segment. This verifies the recording-to-transcription workflow; it does not claim error-free transcription. No course audio or transcript is attached.

Add the audio-input entitlement and microphone usage description for
recording tools launched from the integrated terminal. Document user
consent, virtual-input routing, and short-recording verification.
@xiaolai

xiaolai commented Sep 30, 2026

Copy link
Copy Markdown
Owner

Thank you, this was an excellent diagnosis. The TCC log, the signed-build check, and testing both the allowed and denied cases made the cause unambiguous.

Your commit landed in #1487 with your authorship kept, extended in a few ways:

  • Camera and Apple Events (osascript) are declared too, since they fail the same way in the terminal.
  • The MCP sidecar gets none of these entitlements. Tauri re-signs every externalBin with the app's entitlements, so the macOS release now ships the sidecar with its own signature, and a check on the built app enforces it before each release.
  • The permission prompts are translated into all ten app languages.
  • The terminal guide covers all three permissions in every language.

It ships in the next release (0.9.89). I'm closing this PR because its change is merged through #1487.

非常感谢!问题定位非常清楚:TCC 日志、签名构建的检查、以及允许和拒绝两种情况的实测,让原因一目了然。你的提交已通过 #1487 合入并保留了你的作者署名,同时补充了摄像头和 Apple 事件(osascript)权限、让 MCP 辅助程序不带这些权限、把权限提示翻译成全部十种语言,并更新了各语言的终端文档。将随下一个版本(0.9.89)发布。

newhdr pushed a commit to newhdr/vmark that referenced this pull request Oct 5, 2026
…e Events

macOS attributes programs run in VMark's integrated terminal to VMark, so
under the Hardened Runtime they can only ask for a protected resource the app
declares. VMark declared none: FFmpeg recorded all-zero audio with no prompt
(xiaolai#1483), and the camera and osascript failed the same way.

- src-tauri/app-entitlements.plist: the app bundle's own entitlements, adding
  audio-input, camera and automation.apple-events to the existing runtime
  exceptions. Info.plist gains the three usage descriptions, translated for
  the ten app locales in macos-l10n/<lang>.lproj/InfoPlist.strings.
- The Tauri bundler re-signs every externalBin with the app's entitlements but
  copies bundle.macOS.files unsigned. The macOS release therefore places the
  MCP sidecar through tauri.macos-release.conf.json, keeping the signature
  release.yml gives it with sidecar-entitlements.plist (JIT exceptions only).
  The layout is read from the checked-out tree, so a manual release of an
  older tag still builds as before.
- scripts/verify-macos-bundle-entitlements.sh checks the signed .app: in
  release.yml before publishing, and in release-smoke.yml on the published
  DMG from v0.9.89.
- scripts/check-macos-tcc-entitlements.test.mjs pins the plists, the
  translations and the release wiring.

Builds on the entitlement change from xiaolai#1485.
newhdr pushed a commit to newhdr/vmark that referenced this pull request Oct 5, 2026
…n macOS

Replaces the audio-only section from xiaolai#1485 with one covering all three
resources, where to change the permission, and what a silent denial looks
like, in English and the nine translated guides.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] macOS integrated terminal audio capture is denied because the app lacks audio-input entitlement

2 participants