# How Meetily Handles Start and Stop Recording Commands with Tauri: A Complete Technical Guide

> Learn how Meetily handles start and stop recording commands with Tauri. Discover its async Rust commands, atomic flags for session control, and graceful shutdown for buffer flushing and transcription.

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

---

**Meetily exposes recording controls through asynchronous Tauri commands in [`lib.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/lib.rs) that delegate to a Rust audio layer, using global atomic flags to prevent concurrent sessions and guaranteeing graceful shutdown that flushes buffers and awaits transcription completion.**

Meetily is an open-source meeting assistant built with the Tauri framework, combining a React frontend with Rust-powered native audio processing. Understanding how Meetily handles start and stop recording commands with Tauri reveals a sophisticated async architecture that manages device selection, transcription pipelines, and resource cleanup across the JavaScript-Rust boundary.

## Tauri Command Architecture

The recording workflow is exposed to the frontend via two `#[tauri::command]` attributed functions declared in [`frontend/src-tauri/src/lib.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/lib.rs): `start_recording` and `stop_recording`. These functions serve as the bridge between the TypeScript UI and the native Rust audio subsystem, handling device resolution, state management, and graceful error propagation through Tauri's invoke mechanism.

## Starting a Recording Session

The start-recording flow orchestrates device initialization, model validation, and global state management through ten discrete steps.

### Entry Point and Concurrency Guard

The UI initiates recording by calling `invoke('start_recording', { ... })`, which triggers the command handler at lines 84-102 in [`frontend/src-tauri/src/lib.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/lib.rs). The function immediately checks the global `RECORDING_FLAG` (an `AtomicBool`) to reject requests if a session is already active, preventing race conditions and resource conflicts.

### Device Resolution and Model Validation

The command delegates to `audio::recording_commands::start_recording_with_devices_and_meeting`, passing optional microphone, system-audio, and meeting-name arguments. Inside [`recording_commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/recording_commands.rs), the function resolves requested devices or falls back to defaults, then validates that the Whisper transcription model is ready. If model validation fails, it emits a `transcription-error` event to the UI and aborts before allocating audio resources.

### Pipeline Initialization and Global State

A new `RecordingManager` is instantiated with the meeting name and an error callback. Calling `manager.start_recording()` launches the audio capture pipeline and returns a `transcription_receiver`. The manager and transcription task handle are stored in static globals (`RECORDING_MANAGER` and `TRANSCRIPTION_TASK`) while `IS_RECORDING` is set to **true** using `Ordering::SeqCst`, ensuring visibility across threads.

### Event Registration and UI Feedback

The command registers a listener for the `"transcript-update"` event, converting each incoming chunk into a `TranscriptSegment` saved through the manager. The listener ID is stored in `TRANSCRIPT_LISTENER_ID` for cleanup during shutdown. Finally, the command emits a `"recording-started"` event, updates the system tray menu via [`tray.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/tray.rs), and fires a native notification through [`notifications/commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/notifications/commands.rs).

## Stopping a Recording Session

The stop-recording flow guarantees no transcript data is lost through ordered resource deallocation and blocking awaits.

### Entry and Early Exit

When the UI calls `invoke('stop_recording', { save_path })`, the handler at lines 44-66 in [`frontend/src-tauri/src/lib.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/lib.rs) first checks `audio::recording_commands::is_recording()`. If no session is active, it returns immediately without error.

### Graceful Shutdown Sequence

The stop implementation in [`frontend/src-tauri/src/audio/recording_commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/recording_commands.rs) (lines 482-530) executes a strict shutdown protocol:

1. **Flush Audio Streams**: Calls `manager.stop_streams_and_force_flush()` to guarantee all buffered audio is processed and no new chunks enter the pipeline.

2. **Remove Event Listener**: Unlistens the `"transcript-update"` handler using the stored `TRANSCRIPT_LISTENER_ID`, releasing the microphone resource.

3. **Await Transcription**: The transcription task is awaited without timeout via `task.await`, ensuring every queued chunk is processed while emitting `"recording-shutdown-progress"` updates to the UI.

4. **Clear Globals**: `RECORDING_MANAGER`, `TRANSCRIPTION_TASK`, and `TRANSCRIPT_LISTENER_ID` are cleared, and `IS_RECORDING` is atomically set to **false**.

### Filesystem and UI Finalization

If the caller supplied a `save_path`, the command ensures the directory hierarchy exists, creating it recursively if necessary. After successful shutdown, it updates the tray menu, emits a `"recording-stopped"` event, and displays a completion notification.

## Implementation Code Examples

### Frontend TypeScript Calls

```typescript
// Start recording with specific devices
await invoke('start_recording', {
  mic_device_name: 'Built-in Microphone',
  system_device_name: 'BlackHole 2ch',
  meeting_name: 'Team Standup'
});

// Stop and save to specific path
await invoke('stop_recording', {
  save_path: '/Users/me/Meetings/Team_Standup_2024-03-01.m4a'
});

```

### Rust Command Signatures

```rust
// frontend/src-tauri/src/lib.rs
#[tauri::command]
async fn start_recording<R: Runtime>(
    app: AppHandle<R>,
    mic_device_name: Option<String>,
    system_device_name: Option<String>,
    meeting_name: Option<String>,
) -> Result<(), String> { /* ... */ }

#[tauri::command]
async fn stop_recording<R: Runtime>(
    app: AppHandle<R>, 
    args: RecordingArgs
) -> Result<(), String> { /* ... */ }

```

### Core Stop Logic

```rust
// frontend/src-tauri/src/audio/recording_commands.rs
pub async fn stop_recording<R: Runtime>(
    app: AppHandle<R>, 
    _args: RecordingArgs
) -> Result<(), String> {
    if !IS_RECORDING.load(Ordering::SeqCst) { 
        return Ok(()); 
    }

    // 1. Stop audio streams and force-flush
    let manager = RECORDING_MANAGER.lock().unwrap().take();
    if let Some(mut m) = manager {
        m.stop_streams_and_force_flush().await?;
    }

    // 2. Remove transcript listener
    if let Some(id) = TRANSCRIPT_LISTENER_ID.lock().unwrap().take() {
        app.unlisten(id);
    }

    // 3. Await transcription task completion
    if let Some(task) = TRANSCRIPTION_TASK.lock().unwrap().take() {
        task.await?;
    }

    IS_RECORDING.store(false, Ordering::SeqCst);
    Ok(())
}

```

## Key Source Files Reference

- **Command Entry Points**: [`frontend/src-tauri/src/lib.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/lib.rs) (start at lines 84-102, stop at lines 44-66)
- **Recording Orchestration**: [`frontend/src-tauri/src/audio/recording_commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/recording_commands.rs) (device resolution, lifecycle management)
- **Global State**: [`frontend/src-tauri/src/audio/recording_commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/recording_commands.rs) (static globals `IS_RECORDING`, `RECORDING_MANAGER`, `TRANSCRIPTION_TASK`)
- **Audio Pipeline**: [`frontend/src-tauri/src/audio/recording_manager.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/recording_manager.rs) and [`recording_saver.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/recording_saver.rs)
- **System Tray**: [`frontend/src-tauri/src/tray.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/tray.rs)
- **Notifications**: [`frontend/src-tauri/src/notifications/commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/notifications/commands.rs)

## Summary

- Meetily exposes recording controls through Tauri's `#[tauri::command]` attribute in [`lib.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/lib.rs), making them callable from TypeScript via `invoke()`.
- **Atomic flags** prevent concurrent recording sessions by checking `RECORDING_FLAG` before initialization.
- The **start flow** validates the Whisper model, initializes a `RecordingManager`, and stores handles in static globals for cross-command persistence.
- The **stop flow** guarantees data integrity by flushing audio buffers, awaiting transcription completion, and clearing global state in a strict sequence.
- Real-time UI updates occur through Tauri events like `"recording-started"`, `"transcript-update"`, and `"recording-shutdown-progress"`.

## Frequently Asked Questions

### How does Meetily prevent starting multiple recording sessions simultaneously?

Meetily uses a global `AtomicBool` named `RECORDING_FLAG` that is checked atomically at the start of the `start_recording` command. If the flag is already **true**, the command returns immediately with an error, preventing resource conflicts and ensuring only one audio pipeline runs at a time.

### What happens if the Whisper model isn't ready when starting a recording?

Before initializing audio capture, the `start_recording_with_devices_and_meeting` function validates model availability. If the model is not ready, it emits a `transcription-error` event to the frontend and aborts the recording initialization before any system resources are allocated.

### Why does the stop_recording command use Ordering::SeqCst for atomic operations?

The `IS_RECORDING` flag uses `Ordering::SeqCst` (sequentially consistent ordering) to ensure that the state change is visible to all threads immediately. This prevents race conditions where the UI might attempt to start a new session before the previous one's cleanup is globally visible.

### Where is the transcript data buffered during the shutdown process?

During shutdown, remaining audio chunks are flushed via `stop_streams_and_force_flush()`, and the transcription task is awaited without timeout in [`recording_commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/recording_commands.rs). This ensures all pending audio is processed through the Whisper pipeline and saved via the `RecordingManager` before the function returns and clears the global `TRANSCRIPTION_TASK` handle.