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

> Discover how Meetily manages audio permissions on macOS Windows and Linux using Rust for seamless microphone and screen recording consent before capture.

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

---

**Meetily centralizes audio permission requests through a Rust orchestration layer that delegates to platform-specific APIs, ensuring microphone and screen-recording consent is obtained before any capture begins.**

Meetily is an open-source meeting transcription application built with Tauri and React that must navigate distinct audio permission models across operating systems. The application implements a unified permission architecture in [`frontend/src-tauri/src/audio/permissions.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/permissions.rs) that abstracts OS-specific implementations, prompting users appropriately while preventing unauthorized access to input devices.

## Cross-Platform Permission Architecture

Meetily’s audio subsystem treats permissions as a prerequisite gate before initializing any capture pipeline. The [`recording_commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/recording_commands.rs) module invokes the central **ensure_permissions()** function, which selects the appropriate platform implementation via conditional compilation. This design ensures that macOS, Windows, and Linux each trigger their native consent dialogs through a common asynchronous interface.

### macOS: Microphone and Screen Recording Consent

On macOS, Meetily must obtain two distinct permissions: microphone access via `AVAudioSession` and screen recording access for system audio capture via ScreenCaptureKit. The [`macos.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/macos.rs) module requests microphone permissions directly through Rust bindings, while screen recording authorization is handled by the Swift helper script [`check_screen_permission.swift`](https://github.com/Zackriya-Solutions/meetily/blob/main/check_screen_permission.swift). This Swift file uses the `AXIsProcessTrustedWithOptions` API to prompt the user for screen recording access, returning a boolean result that Rust interprets as permission granted or denied.

### Windows: WASAPI Access Control

Windows handles audio permissions through the **WASAPI** (Windows Audio Session API) layer. When Meetily attempts to enumerate capture devices in [`windows.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/windows.rs), the `IMMDeviceEnumerator` interface implicitly triggers the OS permission dialog if the application has not previously been granted microphone access. If the user denies access, the WASAPI call returns an error code that `ensure_permissions()` surfaces as a structured error to the frontend.

### Linux: ALSA and PulseAudio Group Requirements

Linux distributions rely on **ALSA** or **PulseAudio** for audio capture, which do not implement graphical permission prompts. Instead, Meetily checks whether the user belongs to the `audio` group or has sufficient capabilities to open the default PCM device. The [`linux.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/linux.rs) module attempts to initialize the audio context; failure results in a permission error propagated back to the UI, advising the user to configure group membership or PulseAudio settings.

## Centralized Permission Orchestration in Rust

The [`permissions.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/permissions.rs) file acts as the single source of truth for all audio authorization checks. It exposes an async `ensure_permissions()` function that recording commands must await before initializing the capture pipeline. The function uses `#[cfg(target_os = "...")]` attributes to compile the correct platform module, which implements the `AudioPermissionProvider` trait for consistent error handling.

```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(())
}

```

This architecture guarantees that no audio stream starts until the OS confirms access, preventing runtime failures deep in the capture pipeline.

## Frontend Integration with React Hooks

The React frontend consumes these Rust commands through the **usePermissionCheck** hook located in [`frontend/src/hooks/usePermissionCheck.ts`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src/hooks/usePermissionCheck.ts). This hook provides methods to query current permission states and trigger the Rust-side authorization flow. When a user initiates a recording, the hook calls the Tauri `start_recording` command, which internally invokes `ensure_permissions()` before proceeding.

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

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

  if (!hasPermission) {
    await requestPermission();
  }

  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);
  }
};

```

## Platform-Specific Implementation Details

### macOS Swift Helper

For screen recording permissions on macOS, Meetily shells out to a dedicated Swift executable:

```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)

```

This script returns exit code 0 only when the user has granted screen recording privileges, allowing the Rust backend in [`permissions.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/permissions.rs) to distinguish between denied and authorized states.

### Windows and Linux Modules

The [`windows.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/windows.rs) and [`linux.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/linux.rs) files in `frontend/src-tauri/src/audio/devices/platform/` implement the platform-specific logic. Windows uses COM interfaces to query device state, while Linux attempts to open the default PulseAudio source or ALSA device, treating any access denial as a permission failure that aborts the recording command.

## Summary

- Meetily centralizes audio permission logic in [`permissions.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/permissions.rs), ensuring all platform-specific checks complete before capture begins.
- **macOS** requires separate microphone (`AVAudioSession`) and screen recording (`AXIsProcessTrustedWithOptions`) prompts handled via Rust and a Swift helper.
- **Windows** relies on WASAPI (`IMMDeviceEnumerator`) to implicitly request microphone access when devices are enumerated.
- **Linux** checks user group membership and device file access via ALSA/PulseAudio, returning explicit errors if permissions are insufficient.
- The **usePermissionCheck** React hook bridges the frontend and Rust backend, providing a unified interface for permission state management.

## Frequently Asked Questions

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

If the user denies either microphone or screen recording access on macOS, the platform functions in [`macos.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/macos.rs) return an error that propagates through `ensure_permissions()` to the `start_recording` command. The **usePermissionCheck** hook captures this rejection and displays a UI notification directing the user to System Settings.

### Does Meetily support headless Linux environments without GUI permission dialogs?

Yes, Meetily supports headless Linux servers because ALSA and PulseAudio do not require interactive permission dialogs. However, the user account running the application must belong to the `audio` group or have appropriate capabilities to access `/dev/snd/*` devices.

### Why does Meetily use a separate Swift file for macOS screen recording?

Meetily uses [`check_screen_permission.swift`](https://github.com/Zackriya-Solutions/meetily/blob/main/check_screen_permission.swift) because Rust’s Core Audio bindings cannot directly invoke the `AXIsProcessTrustedWithOptions` API required for screen recording consent. By compiling and executing this Swift script as a subprocess, Meetily triggers the native macOS transparency consent dialog while maintaining the main application logic in Rust.

### How are permission checks integrated with the recording lifecycle?

Permission checks are synchronous prerequisites within the asynchronous recording command. When the frontend calls `invoke('start_recording', ...)`, the handler in [`recording_commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/recording_commands.rs) immediately calls `audio::permissions::ensure_permissions().await`. Only after this call resolves successfully does the code initialize the audio pipeline, ensuring that no system resources are allocated for capture until the OS confirms the user has granted all necessary entitlements.