Menu

Plugin System

Relevant source files

Purpose and Scope

The plugin system is Hyprnote's modular architecture for extending Tauri's core functionality. Each plugin encapsulates a distinct domain (window management, audio processing, speech-to-text, etc.) and follows a consistent architectural pattern to expose functionality to the frontend via type-safe commands and events.

This document covers the plugin architecture pattern, plugin lifecycle, type-safety mechanisms, and the actor-based concurrency model used by some plugins. For the overall desktop application architecture, see Desktop Application Architecture. For specific audio processing implementation details, see Audio Capture Layer and Listener Plugin.

Plugin Architecture Pattern

All plugins in Hyprnote follow a standardized structure that ensures consistency and type safety across the codebase.

Common Plugin Structure

Sources: plugins/windows/src/lib.rs42-83 plugins/local-stt/src/lib.rs32-97 plugins/listener/src/lib.rs33-67

Each plugin consists of:

ComponentPurposeExample Files
Extension TraitExtends tauri::Manager with plugin-specific methodsWindowsPluginExt, LocalSttPluginExt, ListenerPluginExt
Commands ModuleTauri commands exposed to frontendplugins/*/src/commands.rs
Events ModuleEvent definitions for frontend emissionplugins/*/src/events.rs
Managed StatePlugin-specific state stored in Tauri's state containerManagedState, SharedState
Error TypesPlugin-specific error enum implementing Serializeplugins/*/src/error.rs
Init FunctionPlugin initialization and registrationinit() function returning TauriPlugin<R>

Sources: plugins/windows/src/ext.rs113-236 plugins/local-stt/src/ext.rs17-48 plugins/listener/src/ext.rs35-52

Extension Trait Pattern

Each plugin defines an extension trait that adds methods to any type implementing tauri::Manager<R>:

This pattern allows plugin functionality to be called directly on AppHandle or Window instances anywhere in the codebase, providing ergonomic access without tight coupling.

Sources: plugins/local-stt/src/ext.rs17-48 plugins/windows/src/ext.rs113-236 plugins/listener/src/ext.rs35-52

Core Plugins Overview

Plugin Responsibilities

Sources: plugins/windows/src/commands.rs plugins/local-stt/src/commands.rs plugins/listener/src/commands.rs

Windows Plugin

The Windows plugin manages application windows and navigation.

Key Commands:

  • window_show: Creates or shows a window
  • window_destroy: Destroys a window instance
  • window_navigate: Navigates window to a path
  • window_emit_navigate: Emits navigation event
  • window_is_exists: Checks if window exists

Managed State: The plugin tracks window state including visibility and session IDs for analytics.

Sources: plugins/windows/src/lib.rs23-40 plugins/windows/src/commands.rs1-108 plugins/windows/src/ext.rs1-236

Events Emitted:

  • Navigate: Window navigation events
  • WindowDestroyed: Cleanup notification when windows close
  • MainWindowState: Sidebar/panel expansion state

Sources: plugins/windows/src/events.rs65-109

Local STT Plugin

The Local STT plugin manages speech-to-text model lifecycle and server processes.

Architecture:

Sources: plugins/local-stt/src/ext.rs100-153 plugins/local-stt/src/server/supervisor.rs

Key Commands:

  • start_server(model): Starts STT server (internal or external)
  • stop_server(server_type): Stops running STT server
  • download_model(model, channel): Downloads model with progress tracking
  • get_servers(): Returns status of all STT servers
  • is_model_downloaded(model): Checks model availability

Sources: plugins/local-stt/src/commands.rs1-94

Server Types:

Server TypeModelsImplementation
InternalWhisper variants (QuantizedTiny, QuantizedBase, etc.)InternalSTTActor - Axum HTTP server in-process plugins/local-stt/src/server/internal.rs32-136
ExternalAM models (Parakeet v2/v3, Whisper Large v3)ExternalSTTActor - Sidecar process via tauri_plugin_shell plugins/local-stt/src/server/external.rs69-260

State Management:

The download_task map tracks active model downloads with cancellation support, while stt_supervisor provides access to the actor supervision tree.

Sources: plugins/local-stt/src/lib.rs23-28

Listener Plugin

The Listener plugin orchestrates audio capture, real-time transcription, and recording sessions.

Architecture:

Sources: plugins/listener/src/ext.rs119-155 plugins/listener/src/actors/controller.rs

Key Commands:

  • start_session(params): Spawns ControllerActor with session parameters
  • stop_session(): Gracefully stops active session
  • run_batch(params): Processes audio file for batch transcription
  • get_mic_muted() / set_mic_muted(muted): Microphone mute control
  • set_microphone_device(device_name): Changes audio input device

Sources: plugins/listener/src/commands.rs1-83

Session Events:

The plugin emits SessionEvent variants to communicate session state and transcription results:

Sources: plugins/listener/src/events.rs12-51

Plugin Initialization and Lifecycle

Plugin Registration Flow

Sources: plugins/windows/src/lib.rs62-83 plugins/local-stt/src/lib.rs51-97

Plugin Setup Implementation

Each plugin implements a setup closure that executes during application initialization:

Example: Local STT Plugin Initialization

The Local STT plugin spawns a supervisor actor during setup:

plugins/local-stt/src/lib.rs80-92

Sources: plugins/local-stt/src/lib.rs51-97

Type Safety and IPC Communication

tauri-specta Integration

All plugins use tauri-specta to generate type-safe TypeScript bindings from Rust types. This ensures compile-time type safety across the IPC boundary.

Sources: plugins/windows/src/lib.rs42-60 plugins/local-stt/src/lib.rs32-49

Generated Bindings Structure

Each plugin's js/bindings.gen.ts exports:

Commands object:

Events object:

Type definitions: All Rust types marked with #[derive(specta::Type)] are exported as TypeScript types.

Sources: plugins/local-stt/js/bindings.gen.ts9-80 plugins/listener/js/bindings.gen.ts87-116

Command Definition Pattern

Commands are defined with both Tauri and specta attributes:

Key aspects:

  • #[tauri::command]: Registers with Tauri's IPC system
  • #[specta::specta]: Generates TypeScript type information
  • Commands are thin wrappers calling extension trait methods
  • Errors are converted to String for serialization across IPC

Sources: plugins/local-stt/src/commands.rs62-68 plugins/listener/src/commands.rs50-58

Event Definition Pattern

Events use the tauri_specta::Event derive macro:

Events are emitted using the generated trait methods:

Sources: plugins/listener/src/events.rs12-51 plugins/windows/src/events.rs65-101

Actor-Based Plugin Architecture

Plugins that manage complex concurrent operations use the ractor actor framework for fault-tolerant supervision trees.

Actor Pattern in Local STT Plugin

Sources: plugins/local-stt/src/server/supervisor.rs19-32 plugins/local-stt/src/server/internal.rs32-136 plugins/local-stt/src/server/external.rs69-260

Supervisor Configuration

The STT supervisor uses DynamicSupervisor with strict resource limits:

Sources: plugins/local-stt/src/server/supervisor.rs19-32

Actor Lifecycle Management

Starting a Server:

plugins/local-stt/src/ext.rs100-153

  1. Check if server already running and healthy
  2. If wrong model loaded, stop existing server
  3. Create ChildSpec with spawn function and restart policy
  4. Call DynamicSupervisor::spawn_child()
  5. Wait for health check to confirm readiness

Stopping a Server:

plugins/local-stt/src/server/supervisor.rs94-120

  1. Call DynamicSupervisor::terminate_child()
  2. Wait for actor to shut down via registry polling
  3. Cleanup state (kill process, abort tasks)

Internal vs External Actor Differences

AspectInternalSTTActorExternalSTTActor
ProcessIn-process Axum serverSpawned sidecar via shell
ModelsWhisper variantsAM models
PortRandom ephemeral portProvided by args or random
LifecycleGraceful shutdown via watch channelProcess kill on cleanup
HealthImmediate (in-memory)Retry with backoff (HTTP)

Sources: plugins/local-stt/src/server/internal.rs40-100 plugins/local-stt/src/server/external.rs106-219

Actor Messages

Each actor defines a message enum for inter-actor communication:

Messages are sent using ractor primitives:

  • call_t!(): Synchronous RPC with timeout
  • send_message(): Async message send
  • cast(): Fire-and-forget message

Sources: plugins/local-stt/src/server/internal.rs14-17 plugins/local-stt/src/server/external.rs10-13

Permission System

Each plugin defines granular permissions that control frontend access to commands.

Permission Structure

Permissions are defined in TOML and auto-generated into JSON schemas:

plugins/windows/permissions/default.toml1-12

Build-time Integration

Permissions are registered during the plugin build process:

plugins/windows/build.rs1-14

This generates permission schemas in permissions/schemas/schema.json and documentation in permissions/autogenerated/reference.md.

Sources: plugins/windows/build.rs plugins/local-stt/build.rs plugins/listener/build.rs

Permission Naming Convention

All permissions follow the pattern: <plugin-name>:<allow|deny>-<command-name>

Examples:

  • windows:allow-window-show
  • local-stt:allow-start-server
  • listener:allow-start-session

Sources: plugins/windows/permissions/autogenerated/reference.md plugins/local-stt/permissions/autogenerated/reference.md plugins/listener/permissions/autogenerated/reference.md

Plugin Summary

PluginPrimary ResponsibilityKey TechnologiesActor-Based
WindowsWindow lifecycle and navigationTauri window APIs, analytics integrationNo
Local STTSTT model and server managementractor, Axum, whisper.cpp, sidecar processesYes
ListenerAudio session orchestrationractor, hypr-audio, owhisper-clientYes
DatabaseData persistence and synclibsql, TursoNo
Local LLMLLM server managementllama.cpp, OpenAI-compatible APIYes
AuthAuthentication and user managementNango integrationNo
AnalyticsEvent trackingExternal analytics serviceNo

Sources: Referenced throughout document; diagram structure based on Diagram 4 from overview