0039: Pair runs in a helper inside the app

On this page

Status: accepted

Context

A call needs to:

  • capture the screen;
  • use the microphone and speakers;
  • inject input;
  • open network sockets;
  • draw cursors over other apps.

On macOS each of these is a privacy permission that belongs to an app the person can see. Apple attributes a helper's requests to the app that started it ("responsible process"), and a nested .app doesn't get an identity of its own. leashd is a launch agent; it holds no window and isn't exempt from Local Network privacy. The Tauri shell holds no domain logic (ADR 0001).

Decision

  • The helper. Calls run in a sidecar binary, leash-pair, inside Short Leash's bundle: Contents/MacOS on macOS, next to the app on Windows.

    • The app starts it for each session and stops it when the session ends.
    • It talks to leashd over leashd's local API (unix socket or named pipe), with the ipc types.
    • It never disclaims responsibility, so macOS attributes every request to Short Leash.app.
  • The screen on macOS.

    • On macOS 14 and later, the screen comes from the system's screen picker (SCContentSharingPicker, through libwebrtc's capturer). That needs no Screen Recording permission and has no monthly reminder.
    • On macOS 13, Short Leash falls back to Screen Recording.
  • Permissions on macOS, each asked when first needed, not at install:

    Permission Needed for Asked when
    Microphone voice the first session
    Accessibility posting a guest's input the first time the host gives control; Pair also offers a button straight to the right pane, because macOS doesn't ask twice
    Local Network a peer on the same network the first time a call reaches one

    Short Leash.app's Info.plist declares NSMicrophoneUsageDescription and NSLocalNetworkUsageDescription, and its entitlements add com.apple.security.device.audio-input for the hardened runtime.

  • The screen on Windows: libwebrtc's capturer. No permission is needed for capture or SendInput.

  • The UI: overlays (guest cursors, the control frame) are click-through windows kept out of the captured image. The in-call bar, waiting room and viewer chrome are ordinary Short Leash panels (ADR 0031).

Consequences

  • One identity: people see one app asking for permissions, Short Leash, with Short Leash's words.
  • Grants survive updates only with a stable signature. Releases are Developer ID–signed and notarised; ad-hoc local builds lose them on every rebuild.
  • To confirm in the Phase 2 preview build: grants surviving an update, the Local Network prompt, and that prompts name Short Leash.
  • Smaller blast radius: a crash in capture or WebRTC takes down the helper, not leashd or the window. The helper is also the only process that opens call sockets (ADR 0037).