# How Meetily Implements Audio Permission Handling Across macOS, Windows, and Linux

> Discover how Meetily handles audio permissions on macOS Windows and Linux using a central Rust module for seamless microphone access and screen recording consent.

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

---

**Meetily requests platform-native audio permissions through a centralized Rust module before initializing any capture stream, ensuring microphone access on all platforms and additional screen-recording consent on macOS.**

The **audio permission handling** architecture in the [Zackriya-Solutions/meetily](https://github.com/Zackriya-Solutions/meetily) repository uses a Tauri-based backend to abstract OS-specific security models. The implementation routes all recording requests through a single validation layer that detects the host platform and invokes the appropriate system APIs.

## Platform-Specific Permission Architecture

The permission system centers on **[`frontend/src-tauri/src/audio/permissions.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/permissions.rs)**, which exposes an async `ensure_permissions()` function. This module acts as a cross-platform abstraction layer that delegates to platform-specific implementations in `audio/devices/platform/`.

- **macOS**: Requires both microphone access and screen-recording permissions to capture system audio.
- **Windows**: Relies on WASAPI implicit permissions granted when the user accepts the OS dialog on first access.
- **Linux**: Depends on user group membership (`audio`) and PulseAudio/ALSA socket access rather than interactive prompts.

## macOS: Microphone and Screen Recording Permissions

### Permission Requirements

On macOS, Meetily must obtain two distinct entitlements before starting a recording session. The **microphone permission** is requested via the `AVAudioSession` API, while **screen-recording permission** is required to capture system audio output using ScreenCaptureKit.

### Implementation Details

The Rust module calls a Swift helper script to trigger the macOS security dialog. The **[`frontend/src-tauri/check_screen_permission.swift`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/check_screen_permission.swift)** file uses the `AXIsProcessTrustedWithOptions` API to prompt the user:

```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 **[`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)** module handles device enumeration after permissions are granted, interfacing with CoreAudio to list available input and virtual audio devices.

## Windows: WASAPI Access Control

Windows audio permission handling relies on the Windows Audio Session API (**WASAPI**). The **[`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)** module uses `IMMDeviceEnumerator` to access capture devices.

Unlike macOS, Windows does not require a dedicated permission API call. Instead, the first attempt to open a capture stream triggers the system consent dialog. If the user denies access, the Rust code surfaces the `E_ACCESSDENIED` error from WASAPI:

```rust
// Conceptual flow in windows.rs
match device.Activate::<IAudioClient>(CLSCTX_ALL, None) {
    Ok(client) => client,
    Err(e) if e.code() == E_ACCESSDENIED => {
        Err("Microphone access denied by Windows privacy settings".into())
    }
    Err(e) => Err(e.into()),
}

```

## Linux: ALSA and PulseAudio Group Requirements

Linux implementations in **[`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)** do not use interactive permission dialogs. Instead, the application checks for access to the ALSA device nodes or PulseAudio sockets.

The permission model depends on:
- Membership in the **`audio`** system group for ALSA hardware access.
- Access to the PulseAudio server socket (typically via user session permissions).

If the user lacks the necessary group membership or socket access, the device open operation fails with a permission error that propagates to the frontend.

## Integration with the Recording Pipeline

The permission flow integrates with the UI through the **[`frontend/src/hooks/usePermissionCheck.ts`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src/hooks/usePermissionCheck.ts)** React hook and the **[`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.

When the user initiates a recording, the frontend invokes the `start_recording` command:

```typescript
// frontend/src/hooks/usePermissionCheck.ts usage
import { invoke } from '@tauri-apps/api/tauri';

const startRecording = async () => {
  try {
    await invoke('start_recording', {
      mic_device_name: 'Built-in Microphone',
      system_device_name: 'BlackHole 2ch',
      meeting_name: 'Team Sync',
    });
  } catch (error) {
    console.error('Permission or device error:', error);
  }
};

```

The Rust handler in [`recording_commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/recording_commands.rs) immediately calls `ensure_permissions()`:

```rust
// frontend/src-tauri/src/audio/recording_commands.rs
#[tauri::command]
pub async fn start_recording(
    mic_device_name: String,
    system_device_name: String,
    meeting_name: String,
) -> Result<(), String> {
    // Validate permissions before allocating resources
    audio::permissions::ensure_permissions().await?;
    
    // Proceed with pipeline initialization...
    audio::pipeline::start(mic_device_name, system_device_name, meeting_name).await
}

```

The `ensure_permissions()` function in [`permissions.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/permissions.rs) dispatches to the appropriate platform module:

```rust
// frontend/src-tauri/src/audio/permissions.rs
pub async fn ensure_permissions() -> Result<(), String> {
    #[cfg(target_os = "macos")]
    {
        macos::request_microphone_permission().await?;
        macos::request_screen_recording_permission().await?;
    }
    #[cfg(target_os = "windows")]
    {
        windows::ensure_wasapi_access().await?;
    }
    #[cfg(target_os = "linux")]
    {
        linux::ensure_alsa_or_pulse_access().await?;
    }
    Ok(())
}

```

## Summary

- **Centralized validation**: All recording commands route through [`permissions.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/permissions.rs), preventing unauthorized audio capture.
- **macOS dual-permission model**: Requires explicit microphone and screen-recording consent via Swift helper scripts.
- **Windows implicit model**: Leverages WASAPI security dialogs triggered on first device access.
- **Linux group-based model**: Relies on `audio` group membership and PulseAudio socket permissions rather than interactive prompts.
- **Graceful error propagation**: Permission denials surface as Tauri command errors, allowing the React frontend to display user-friendly guidance via `usePermissionCheck`.

## Frequently Asked Questions

### Does Meetily request audio permissions every time I start a recording?

No. Meetily requests permissions only when the OS indicates they have not been previously granted. On macOS, the system remembers the user's choice after the first approval, while Windows caches the WASAPI consent until the user manually revokes it in Privacy settings. The `ensure_permissions()` function checks the current authorization status before displaying any dialogs.

### Why does Meetily require screen recording permission on macOS?

macOS classifies system audio capture as a screen-recording operation under the `kTCCServiceScreenCapture` privacy category. To record meeting audio from applications like Zoom or Teams, Meetily must capture the system output device, which requires the same entitlement used for screenshot utilities. The **[`check_screen_permission.swift`](https://github.com/Zackriya-Solutions/meetily/blob/main/check_screen_permission.swift)** script explicitly requests this access using `AXIsProcessTrustedWithOptions`.

### How does Meetily handle denied permissions on Windows?

When Windows returns an access-denied error from the WASAPI `Activate` call, the **[`windows.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/windows.rs)** module converts the `HRESULT` into a descriptive string error. This propagates through [`recording_commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/recording_commands.rs) to the frontend, where the `usePermissionCheck` hook can detect the failure and prompt the user to enable microphone access in Windows Settings > Privacy > Microphone.

### What Linux user group is required for Meetily audio capture?

The user must belong to the **`audio`** group to access hardware ALSA devices directly. For PulseAudio installations (most modern distributions), the user typically needs access to the PulseAudio socket in `/run/user/$UID/pulse`, which is granted automatically to the logged-in user. The **[`linux.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/linux.rs)** module attempts to open the default device and returns a permission error if group membership is insufficient.