Skip to content

RustedBytes/fswtch

Repository files navigation

fswtch

Crates.io Version fswtch-sys

Rust bindings and helper APIs for writing FreeSWITCH modules.

This workspace is intentionally split into three crates:

  • fswtch: safe-ish helpers for module exports, module interface creation, API command registration, stream writes, status conversion, and example logging.
  • fswtch-sys: raw FreeSWITCH ABI bindings generated with bindgen.
  • fswtch-src: packaged FreeSWITCH headers used by default bundled builds.

Wrapper API

The fswtch crate provides a small higher-level layer over the raw FreeSWITCH ABI. It focuses on the parts that every module needs first:

  • module_exports! declares the exported FreeSWITCH module table.
  • module_load! generates the FreeSWITCH load callback while giving the body a ModuleBuilder.
  • Module::create builds the loader-owned module interface from raw load callback arguments for lower-level integrations.
  • Module::add_api, Module::add_application, Module::add_chat_application, and Module::add_endpoint register common FreeSWITCH interfaces without hand-writing interface allocation and field assignment.
  • ModuleBuilder chains module and interface registration in load callbacks.
  • api_callback!, app_callback!, and chat_callback! generate FreeSWITCH ABI callbacks while giving the callback body typed wrapper values.
  • Session wraps common application callback operations such as answering, sleeping, and file playback.
  • Stream wraps switch_stream_handle_t for byte and string responses.
  • command_text converts nullable FreeSWITCH callback command pointers into trimmed Rust strings.
  • Event and EventRef wrap custom event creation, headers, firing, cleanup, and inbound event header reads while accepting Rust strings.
  • Module registration, media bug config, session playback, XML helpers, and event helpers convert Rust strings to C strings inside fswtch.
  • Status, SwitchError, and status_to_result convert common FreeSWITCH status handling into Rust Result values.
  • LogLevel, log, and convenience helpers such as log_info, log_warning, log_error, and log_debug1 through log_debug10 route module logs through FreeSWITCH logging.
  • MediaBugConfig, MediaBugFlags, MediaBugHandler, and attach_media_bug provide a higher-level media bug API for bidirectional read/write audio stream callbacks and read/write replacement hooks.

The wrapper does not try to hide the full ABI yet. Examples use fswtch::sys directly where FreeSWITCH exposes interfaces that still need raw pointer setup, such as endpoint I/O routine tables and lifecycle callbacks. Keep those raw calls narrow, document the callback and ownership assumptions, and prefer adding focused helpers to fswtch when the same unsafe pattern appears in more than one module.

Media bug handlers are owned by FreeSWITCH until the close callback. A module can implement MediaBugHandler to observe read and write frames, mutate replacement frames, or pull frames explicitly through MediaBugContext:

struct Meter;

impl fswtch::MediaBugHandler for Meter {
    fn on_read(
        &mut self,
        _ctx: &mut fswtch::MediaBugContext<'_>,
        frame: fswtch::MediaFrame<'_>,
    ) -> fswtch::MediaBugAction {
        fswtch::log_debug("mod_meter", format!("read {} bytes", frame.data_len()));
        fswtch::MediaBugAction::Continue
    }
}

let config = fswtch::MediaBugConfig::new(
    "mod_meter",
    "read-write",
    fswtch::MediaBugFlags::READ_STREAM
        | fswtch::MediaBugFlags::WRITE_STREAM
        | fswtch::MediaBugFlags::NO_PAUSE,
)?;

fswtch::attach_media_bug(session, config, Meter)?;

Build

Default builds use the bundled FreeSWITCH headers from fswtch-src:

cargo check -p fswtch-sys
cargo check -p fswtch --examples
cargo test --workspace
cargo fmt --all --check
cargo clippy --workspace --all-targets

The default bundled feature only generates Rust bindings from packaged headers. It does not compile or statically link FreeSWITCH.

To generate bindings from a configured local FreeSWITCH install:

FREESWITCH_INCLUDE_DIR=/usr/include/freeswitch \
  cargo check -p fswtch-sys --no-default-features --features bindgen

If link metadata is not available through pkg-config, set the library directory explicitly:

FREESWITCH_LIB_DIR=/usr/lib/freeswitch cargo build

Set FREESWITCH_NO_PKG_CONFIG=1 to disable pkg-config probing.

Docker Smoke Test

The repository includes a full smoke image that builds FreeSWITCH, builds every Rust example as a cdylib, installs the modules, starts FreeSWITCH, and verifies APIs through fs_cli.

docker build -t fswtch-freeswitch-smoke .
docker run --rm fswtch-freeswitch-smoke

Successful output ends with:

all fswtch example module checks passed

The smoke script enables FSWTCH_AI_ALLOW_MOCK=1 so the local AI example can run without model files or OpenAI credentials.

Module Shape

A minimal module exports a FreeSWITCH load callback:

fswtch::module_exports! {
    module = mod_hello,
    load = switch_module_load,
}

Use module_load! to create the typed load callback and register one or more APIs:

fswtch::module_load! {
    fn switch_module_load(module) for "mod_hello" {
        fswtch::log_info("mod_hello", "loading module");
        module.api(
            "rust_hello",
            "prints a Rust greeting",
            "rust_hello",
            hello_api,
        )
    }
}

Examples use fswtch::log_info and fswtch::log_error, which route through FreeSWITCH logging.

Examples

All examples live in crates/fswtch/examples and are compiled as FreeSWITCH modules.

Basic module and API patterns:

  • mod_hello: minimal API command.
  • mod_api_suite: multiple API commands in one module.
  • mod_stream_tools: stream responses and command argument parsing.
  • mod_lifecycle: load, runtime, and shutdown callbacks.

Operational and integration patterns:

  • mod_async_job_queue: background worker queue with bounded result history.
  • mod_event_sink: JSON-to-custom-event bridge.
  • mod_http_webhook: queued plain HTTP webhook delivery.
  • mod_registration_check: async registration validation and custom event emission.
  • mod_rate_limiter: token-bucket style API rate limiting with bounded cardinality.
  • mod_metrics: Prometheus-style metrics output with bounded cardinality.
  • mod_config_xml: FreeSWITCH XML config loading and reload.
  • mod_cdr_enricher: CDR JSON enrichment and custom event emission.

FreeSWITCH interface skeletons:

  • mod_app_playback_control: dialplan application interface that answers and plays a supplied target.
  • mod_media_bug_meter: media bug application that counts observed read/write-stream audio frames.
  • mod_endpoint_skeleton: endpoint interface registration skeleton.
  • mod_chatbot_bridge: chat application interface that emits chatbot bridge events.

AI and media integration:

  • mod_remote_vad: async websocket VAD worker with custom event reporting.
  • mod_local_ai_bridge: local ASR/TTS integration boundary plus OpenAI Responses API NLP calls.

Local AI Example

mod_local_ai_bridge exposes:

  • rust_local_ai_status
  • rust_local_asr <pcm16le-file>
  • rust_local_tts <text>
  • rust_local_nlp <prompt>
  • rust_local_nlp_sync <prompt>

Environment variables:

  • FSWTCH_ASR_ONNX: local ASR ONNX model path.
  • FSWTCH_TTS_ONNX: local TTS ONNX model path.
  • OPENAI_API_KEY: enables OpenAI NLP calls.
  • OPENAI_MODEL: defaults to gpt-5.1.
  • OPENAI_BASE_URL: defaults to https://api.openai.com/v1.
  • FSWTCH_AI_ALLOW_MOCK=1: allows smoke-test fallback behavior when models or API credentials are absent.

For production, do not set FSWTCH_AI_ALLOW_MOCK; provide real model paths and API credentials. The example isolates the ORT boundary, but real ASR/TTS inference still needs the tensor contracts for the chosen ONNX models.

Production Notes

The examples are production-oriented examples, not drop-in production services. Before deploying a module, review:

  • Session lifetime and locking for any work that touches a live switch_core_session_t outside the original callback.
  • Backpressure and queue limits for background work.
  • Timeout and retry policy for network integrations.
  • Secret handling for API keys and webhook credentials.
  • Cardinality limits for metrics, rate limiters, and per-call state.
  • Cleanup and ownership rules for FreeSWITCH events, media bugs, XML roots, and allocated user data.
  • Real model initialization and tensor validation for ORT-backed ASR/TTS.

Unsafe blocks are kept small and local to FFI operations. Public unsafe APIs in the wrapper should document a # Safety contract.

Repository Layout

  • crates/fswtch: wrapper API and compile-checked Rust module examples.
  • crates/fswtch-sys: raw generated FreeSWITCH bindings and bindgen build script.
  • crates/fswtch-src: packaged FreeSWITCH headers.
  • docker/fswtch: smoke-test FreeSWITCH config and verification script.
  • Dockerfile: full FreeSWITCH smoke-test image.

The vendored FreeSWITCH trees are third-party inputs. Avoid reformatting or refactoring them unless intentionally updating vendored FreeSWITCH content.

About

Rust bindings and helpers for writing FreeSWITCH modules

Topics

Resources

License

Stars

Watchers

Forks

Contributors