# How the Voice Facade Pattern Works with Stub Implementations in OpenHuman

> Learn how the voice facade pattern in OpenHuman uses stub implementations and Cargo feature flags to provide flexible voice functionality. Explore compile-time facades and no-op stubs for efficient development.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: deep-dive
- Published: 2026-08-30

---

**The voice facade pattern in OpenHuman uses a compile-time facade layer that always builds, while routing to either a full-featured implementation or a no-op stub based on the `voice` Cargo feature flag.**

The `tinyhumansai/openhuman` repository implements a sophisticated compile-time abstraction for its voice processing capabilities. This architectural pattern ensures that public API symbols remain available regardless of feature configuration, while allowing heavy audio dependencies to be excluded from builds that do not require speech-to-text or text-to-speech functionality. The implementation relies on Cargo's conditional compilation to switch between real audio processing modules in `src/openhuman/voice/` and a lightweight stub that maintains type safety.

## Understanding the Three-Layer Compile-Time Architecture

OpenHuman's voice domain separates concerns into three distinct compile-time layers. This structure guarantees that code depending on `openhuman::voice` will always compile, even when the underlying voice stack is omitted.

### The Facade Layer (Always Compiled)

The file [`src/openhuman/voice/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/voice/mod.rs) serves as the immutable public interface. This module is the **only** voice-related code compiled unconditionally, regardless of whether the `voice` Cargo feature is enabled or disabled. It defines the public `openhuman::voice` namespace, re-exports compile-status flags from [`compile_status.rs`](https://github.com/tinyhumansai/openhuman/blob/main/compile_status.rs), and conditionally includes either the real implementation or the stub using `#[cfg]` attributes.

When the facade imports symbols, it uses conditional compilation to expose the appropriate backend:

```rust
// In src/openhuman/voice/mod.rs
#[cfg(feature = "voice")]
pub use factory::{create_stt_provider, effective_stt_provider, /* ... */};

#[cfg(not(feature = "voice"))]
pub use stub::*;

```

### The Real Implementation (Feature-Gated)

When `voice` is enabled via `--features voice`, the facade imports heavy sub-modules including `always_on`, `audio_capture`, `audio_toolkit`, `bus`, `dictation_listener`, `factory`, `hotkey`, `realtime`, `reply_speech`, and `server`. These modules in `src/openhuman/voice/` provide actual speech-to-text processing, audio capture via `cpal`, dictation servers, and RPC endpoints. This implementation is wrapped in `#[cfg(feature = "voice")]` gates and only compiles when explicitly requested.

### The Stub Implementation (Fallback)

When the `voice` feature is disabled, [`src/openhuman/voice/stub.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/voice/stub.rs) compiles instead. This stub mirrors the exact public surface of the real implementation—including types like `SttResult`, traits like `SttProvider`, and functions like `publish_ptt_transcript_committed`—but provides no-op or error-returning bodies. This guarantees type checking succeeds while ensuring runtime calls fail gracefully with `Err(anyhow!("voice feature disabled at compile time"))`.

## Surface-Level Consistency and Drift Detection

The facade pattern enforces API compatibility between the real and stub implementations through several mechanical safeguards.

**Public Re-exports Guarantee Symbol Availability.** The facade in [`mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/mod.rs) re-exports identical symbols regardless of which backend is active. When `voice` is enabled, it exposes real factory functions. When disabled, it exposes the same names from [`stub.rs`](https://github.com/tinyhumansai/openhuman/blob/main/stub.rs).

**Identical Signatures Across Implementations.** The stub implements the exact same function signatures, trait methods, and type definitions as the real implementation. For example, `create_stt_provider` accepts the same parameters and returns the same `Result` type in both `src/openhuman/voice/factory/` (real) and [`src/openhuman/voice/stub.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/voice/stub.rs) (stub). The only difference is the body.

**CI Enforced Drift Detection.** A continuous integration step runs `cargo check --no-default-features --features "<all-but-voice>"`. If any real signature changes without a corresponding update to [`stub.rs`](https://github.com/tinyhumansai/openhuman/blob/main/stub.rs), the build fails. This mechanical check guarantees that the two surfaces remain locked together.

## Runtime Behavior: Feature On vs. Feature Off

The facade determines application behavior at runtime based on which implementation was compiled.

**With Voice Enabled:** The real modules link into the binary. The RPC namespace `openhuman.voice_*` becomes fully functional, the dictation server can start via `server::VoiceServer`, and the bus in [`bus.rs`](https://github.com/tinyhumansai/openhuman/blob/main/bus.rs) emits transcription events. Heavy audio crates like `cpal`, `hound`, and `lettre` are included in the dependency tree.

**With Voice Disabled:** The stub implementation links instead. Calls to `create_stt_provider` return a clear error message indicating the feature is disabled. The `publish_ptt_transcript_committed` function logs a debug message and performs no other action. The CLI subcommand `openhuman voice` returns a descriptive error, and the core application starts normally without audio dependencies.

## Practical Code Examples

The following examples demonstrate how client code interacts with the facade without concerning itself with the underlying feature flag state.

### Factory Pattern Usage

This code compiles and runs regardless of the `voice` feature status:

```rust
use openhuman::voice::factory::{effective_stt_provider, create_stt_provider};
use openhuman::config::Config;

// Resolve provider name from configuration - always available
let provider_name = effective_stt_provider(&config);

// Attempt to initialize - returns Err when voice is disabled
match create_stt_provider(&provider_name, "whisper-v1", &config) {
    Ok(provider) => println!("STT ready: {:?}", provider),
    Err(e) => eprintln!("Voice unavailable: {}", e),
}

```

When compiled with `--no-default-features`, `create_stt_provider` returns `Err(anyhow!("voice feature disabled at compile time"))`, allowing the application to handle the absence of voice capabilities gracefully.

### Event Bus Publishing

The PTT (Push-to-Talk) transcript notification system works transparently through the facade:

```rust
use openhuman::voice::publish_ptt_transcript_committed;

// Signal that a dictation session completed
publish_ptt_transcript_committed(
    thread_id.to_string(),
    session_id,
    transcript.len(),
    elapsed_ms,
    false,
);

```

With the feature disabled, the stub implementation in [`stub.rs`](https://github.com/tinyhumansai/openhuman/blob/main/stub.rs) executes a silent no-op, avoiding runtime panics in code paths that expect the bus to exist.

### CLI Integration

The standalone voice subcommand integrates through the facade:

```rust
// CLI dispatcher pattern
match args.subcommand.as_str() {
    "voice" => {
        openhuman::voice::cli::run_standalone_subcommand(&args.rest)?;
    }
    _ => handle_other_commands(),
}

```

When voice is disabled, `run_standalone_subcommand` returns the standardized disabled error, providing clear user feedback rather than a missing symbol error.

## Why OpenHuman Uses the Voice Facade Pattern

**Binary Size and Dependency Management.** The voice stack pulls in heavyweight audio processing crates. By gating the implementation behind a Cargo feature, builds targeting embedded or server environments can exclude these dependencies entirely, producing leaner binaries.

**Compile-Time API Guarantees.** External crates and internal domains can depend on `openhuman::voice` without adding optional dependencies to their own manifests. The always-compiled facade guarantees that the namespace exists for type checking, even when the functionality is stubbed.

**Graceful Degradation.** Applications boot successfully without voice capabilities. RPC routes register but return disabled errors, and the CLI remains functional. This provides better operational flexibility than compilation failures or runtime link errors.

## Summary

- **The facade pattern** in [`src/openhuman/voice/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/voice/mod.rs) provides a stable public API that compiles unconditionally while delegating to either real or stub implementations based on the `voice` Cargo feature.
- **Stub implementations** in [`src/openhuman/voice/stub.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/voice/stub.rs) mirror the exact signatures of real voice modules, returning descriptive errors or no-ops when the feature is disabled.
- **Drift detection** via CI ensures that changes to real implementation signatures require corresponding updates to the stub, maintaining API consistency.
- **Runtime behavior** switches between full voice capabilities (STT/TTS servers, audio capture) and lightweight error responses without requiring conditional code in callers.

## Frequently Asked Questions

### What happens if I call a voice function without the voice feature enabled?

The stub implementation in [`src/openhuman/voice/stub.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/voice/stub.rs) handles the call. Functions like `create_stt_provider` return `Err(anyhow!("voice feature disabled at compile time"))`, while event functions like `publish_ptt_transcript_committed` execute as no-ops. Your code compiles and runs, but voice operations fail gracefully with descriptive error messages rather than causing runtime panics.

### How does OpenHuman prevent the stub and real implementations from drifting out of sync?

The project uses a CI step that runs `cargo check --no-default-features --features "<all-but-voice>"`. Since the stub must provide identical signatures to the real implementation, any change to the real API that isn't reflected in [`stub.rs`](https://github.com/tinyhumansai/openhuman/blob/main/stub.rs) will cause a compilation error. This mechanical check guarantees that the two surfaces remain identical.

### Can I depend on the voice module in my crate without pulling in audio dependencies?

Yes. By depending on `openhuman` without the `voice` feature, you can import from `openhuman::voice` and use types like `SttResult` or call `effective_stt_provider` without linking heavy audio crates like `cpal` or `hound`. The facade guarantees these symbols exist, while the stub ensures your binary remains small and free of audio processing dependencies.

### Where is the compile-time feature flag checked in the source code?

The primary conditional compilation occurs in [`src/openhuman/voice/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/voice/mod.rs), which uses `#[cfg(feature = "voice")]` to include real modules and `#[cfg(not(feature = "voice"))]` to include the stub. Additional compile-time status information is available in [`src/openhuman/voice/compile_status.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/voice/compile_status.rs), which exports boolean constants indicating whether voice support was compiled into the current binary.