# How Meetily Handles Audio Permissions on macOS, Windows, and Linux

> Learn how Meetily manages audio permissions on macOS, Windows, and Linux. Meetily uses a centralized Rust module to ensure user consent for microphone and screen recording.

- Repository: [Zackriya Solutions/meetily](https://github.com/Zackriya-Solutions/meetily)
- Tags: how-to-guide
- Published: 2026-08-01

---

**Meetily automatically triggers platform-specific permission requests before any audio capture begins, using a centralized Rust module that orchestrates microphone and screen-recording consent across all supported operating systems.**

Meetily is an open-source AI meeting assistant built with Tauri. Before it can record meetings, it must obtain system-level audio permissions—requirements vary significantly by platform. This article examines how Meetily's permission architecture works, tracing the flow from JavaScript invocation through Rust platform abstractions to native OS APIs.

## Permission Architecture Overview

Meetily's permission system follows a **layered design** that keeps platform complexity isolated while ensuring consistent behavior:

- **Entry point**: [`frontend/src-tauri/src/audio/permissions.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/permissions.rs) — central orchestrator
- **Platform modules**: `audio/devices/platform/{macos,windows,linux}.rs` — OS-specific implementations
- **Native helpers**: Swift scripts for macOS screen-recording prompts
- **Frontend integration**: [`usePermissionCheck.ts`](https://github.com/Zackriya-Solutions/meetily/blob/main/usePermissionCheck.ts) hook for UI coordination

The permission flow executes automatically when any recording command is invoked, preventing audio capture until all required consents are granted.

## macOS: Microphone and Screen Recording Permissions

macOS requires **two distinct permissions** for full meeting capture: microphone access and screen recording (for system audio). Meetily handles these sequentially in [`permissions.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/permissions.rs), calling native Swift code for the screen-recording check.

### Microphone Permission

The macOS platform module uses `AVAudioSession` to request microphone access. This triggers the standard macOS permission dialog with the application's `NSMicrophoneUsageDescription` string.

### Screen Recording Permission

System audio capture requires **ScreenCaptureKit** entitlement, which needs explicit user approval. Meetily delegates this to a Swift helper:

```swift
// frontend/src-tauri/check_screen_permission.swift
import Cocoa

let options = [kAXTrustedCheckOptionPrompt.takeRetainedValue() as String: true] as CFDictionary
let trusted = AXIsProcessTrustedWithOptions(options)
exit(trusted ? 0 : 1)

```

The Rust caller runs this script and parses the exit code to determine whether recording can proceed.

### macOS Implementation Files

| File | Purpose |
|------|---------|
| [`frontend/src-tauri/src/audio/devices/platform/macos.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/platform/macos.rs) | Device enumeration and native API calls |
| [`frontend/src-tauri/check_screen_permission.swift`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/check_screen_permission.swift) | Screen-recording permission prompt |

## Windows: WASAPI Access and Device Permissions

Windows handles audio permissions through the **WASAPI** (Windows Audio Session API) framework. Unlike macOS, Windows typically does not show a permission dialog for microphone access—access is granted implicitly when the application first opens an audio stream.

Meetily's Windows implementation in [`audio/devices/platform/windows.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/audio/devices/platform/windows.rs) uses `IMMDeviceEnumerator` to discover and activate capture devices. If the OS denies access (for example, due to privacy settings or corporate policy), the error propagates back to the frontend:

```rust
// Conceptual flow in windows.rs
let device = device_enumerator
    .GetDefaultAudioEndpoint(eCapture, eConsole, &device)
    .map_err(|e| format!("WASAPI access denied: {}", e))?;

```

The frontend receives this error through the Tauri command layer and displays an actionable message to the user.

## Linux: ALSA/PulseAudio Group Membership

Linux audio permissions are **implicit rather than interactive**. Meetily attempts to open the default ALSA or PulseAudio device; success depends on the user belonging to the `audio` group and having appropriate udev rules.

The [`audio/devices/platform/linux.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/audio/devices/platform/linux.rs) module tries PulseAudio first, falling back to ALSA if unavailable. Permission failures surface as device-open errors:

```rust
// Simplified from linux.rs
pub async fn ensure_alsa_or_pulse_access() -> Result<(), String> {
    match pulse::connect() {
        Ok(_) => Ok(()),
        Err(_) => alsa::open_default()
            .map_err(|e| format!("Audio access failed: {}. Ensure user is in 'audio' group.", e)),
    }
}

```

No OS dialog appears—administrators must configure access through system groups and permissions.

## Frontend Integration: The Permission Hook

Meetily's React frontend uses a custom hook to coordinate permission state with the Rust backend. The [`usePermissionCheck.ts`](https://github.com/Zackriya-Solutions/meetily/blob/main/usePermissionCheck.ts) hook provides:

- `hasPermission`: Boolean indicating current consent status
- `requestPermission()`: Triggers the platform-specific flow
- Error state propagation for UI feedback

```tsx
// Frontend usage example
import { invoke } from '@tauri-apps/api/tauri';
import { usePermissionCheck } from '@/hooks/usePermissionCheck';

const MeetingRecorder = () => {
  const { hasPermission, requestPermission } = usePermissionCheck();

  const startRecording = async () => {
    if (!hasPermission) {
      const granted = await requestPermission();
      if (!granted) return; // User denied
    }

    try {
      await invoke('start_recording', {
        mic_device_name: 'Built-in Microphone',
        system_device_name: 'BlackHole 2ch',
        meeting_name: 'Team Sync',
      });
    } catch (e) {
      console.error('Recording failed:', e);
    }
  };

  return <button onClick={startRecording}>Start Recording</button>;
};

```

The hook internally calls Tauri commands that delegate to [`permissions.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/permissions.rs), ensuring consistent behavior across all entry points.

## Complete Permission Flow

When a user clicks "Start Recording," this sequence executes:

1. **JavaScript** invokes `start_recording` command via Tauri API
2. **[`recording_commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/recording_commands.rs)** receives the call and invokes `permissions::ensure_permissions()`
3. **[`permissions.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/permissions.rs)** selects platform module based on `cfg!` macros
4. **Platform module** executes native permission requests:
   - macOS: `request_microphone_permission()` → Swift screen-recording check
   - Windows: `ensure_wasapi_access()` → Verify device activation succeeds
   - Linux: `ensure_alsa_or_pulse_access()` → Test device open
5. **Result propagation**: Success allows pipeline initialization; failure returns descriptive error
6. **Frontend** displays error or proceeds to recording UI

This flow guarantees **fail-fast behavior**—permissions are verified before any audio pipeline resources are allocated.

## Key Implementation Files

| File Path | Role |
|-----------|------|
| [[`frontend/src-tauri/src/audio/permissions.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/permissions.rs)](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/permissions.rs) | Central permission orchestrator with platform dispatch |
| [[`frontend/src-tauri/check_screen_permission.swift`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/check_screen_permission.swift)](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/check_screen_permission.swift) | macOS screen-recording prompt via `AXIsProcessTrustedWithOptions` |
| [[`frontend/src-tauri/src/audio/devices/platform/macos.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/platform/macos.rs)](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/platform/macos.rs) | macOS audio device access and `AVAudioSession` integration |
| [[`frontend/src-tauri/src/audio/devices/platform/windows.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/platform/windows.rs)](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/platform/windows.rs) | Windows WASAPI device enumeration and access validation |
| [[`frontend/src-tauri/src/audio/devices/platform/linux.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/platform/linux.rs)](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/platform/linux.rs) | Linux ALSA/PulseAudio device access with group validation |
| [[`frontend/src/hooks/usePermissionCheck.ts`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src/hooks/usePermissionCheck.ts)](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src/hooks/usePermissionCheck.ts) | React hook for permission state and user-initiated requests |
| [[`frontend/src-tauri/src/audio/recording_commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/recording_commands.rs)](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/recording_commands.rs) | Tauri command handlers that enforce permission checks |

## Design Patterns and Best Practices

Meetily's permission handling demonstrates several robust patterns:

- **Platform abstraction trait**: Common interface across macOS, Windows, and Linux implementations
- **Early validation**: Permissions checked before resource allocation prevents partial initialization states
- **User-transparent flow**: Permission requests emerge from normal app interaction, not preemptive prompts
- **Clear error messages**: Platform-specific failures include actionable guidance (e.g., "Ensure user is in 'audio' group")

The architecture also simplifies testing: platform modules can be mocked, and the permission layer supports conditional compilation for different target environments.

## Summary

- **Centralized control**: [`permissions.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/permissions.rs) gates all audio capture through `ensure_permissions()`
- **macOS complexity**: Requires both microphone and screen-recording permissions, with Swift helper for the latter
- **Windows simplicity**: Implicit WASAPI access, with graceful handling of enterprise policy restrictions
- **Linux system dependency**: Relies on group membership and udev configuration rather than interactive prompts
- **Frontend coordination**: [`usePermissionCheck.ts`](https://github.com/Zackriya-Solutions/meetily/blob/main/usePermissionCheck.ts) hook bridges Rust permission state with React UI state
- **Fail-fast safety**: No audio pipeline initializes until all platform requirements are satisfied

## Frequently Asked Questions

### How does Meetily request microphone permission on macOS?

Meetily's macOS platform module uses `AVAudioSession` to trigger the standard system permission dialog. This occurs in [`audio/devices/platform/macos.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/audio/devices/platform/macos.rs) when `request_microphone_permission()` is called from the central [`permissions.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/permissions.rs) orchestrator. The user sees the application-specific usage description defined in `Info.plist`.

### Why does macOS require screen recording permission for meeting audio?

macOS routes **system audio** (application sounds, not microphone input) through ScreenCaptureKit. Meetily therefore needs `kTCCServiceScreenCapture` approval to capture remote participants' voices in video calls. The [`check_screen_permission.swift`](https://github.com/Zackriya-Solutions/meetily/blob/main/check_screen_permission.swift) script uses `AXIsProcessTrustedWithOptions` to prompt the user if this permission is not yet granted.

### What happens if Windows WASAPI access is blocked by group policy?

If Windows enterprise policies block audio capture, the [`windows.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/windows.rs) platform module receives an `E_ACCESSDENIED` or similar HRESULT from `IMMDeviceEnumerator`. This error propagates through [`recording_commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/recording_commands.rs) to the frontend, where `usePermissionCheck` displays: "WASAPI access denied. Contact your system administrator." The recording does not start.

### Can Meetily work on Linux without root access?

Yes, provided the user belongs to the `audio` group and has read/write access to ALSA devices or PulseAudio socket. The [`linux.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/linux.rs) module attempts PulseAudio first (which often works for standard desktop sessions), falling back to direct ALSA access. No elevation is required if permissions are properly configured.