audio-plugin-bsd
Dynamic .so audio-plugin loader with ABI verification and FreeBSD
Capsicum/pdfork per-process sandboxing for real-time audio in Rust.
Note: The doc comments on each public item are the primary reference. This README is an overview only.
Role
A standalone dynamic plugin loader for real-time audio in Rust. It loads
third-party audio plugins compiled as cdylib (.so / .dylib / .dll)
shared objects, verifies their ABI against the host, instantiates them as
AudioNode implementations behind a stable extern "C" entry point, and —
on FreeBSD — confines each plugin to a Capsicum capability sandbox
(optionally one isolated child process per plugin via pdfork).
Core types
| Type | Description |
|---|---|
[PluginError] |
Loader / sandbox error enum (thiserror-backed) + Result alias. |
[AUDIO_PLUGIN_ABI_MAGIC] |
Magic word (0x41504C47, ASCII "APLG") every plugin must return. |
[AUDIO_PLUGIN_ABI_VERSION] |
Host ABI version (1.0), encoded by [encode_abi_version]. |
[encode_abi_version] / [abi_major] / [abi_minor] |
Encode/decode the (major, minor) ABI version word. |
[is_abi_compatible] |
Major-only ABI compatibility rule. |
[AbiVersion] |
Typed, validated wrapper around an encoded ABI version word. |
[PluginMetadata] |
Plugin identity (name / version / description / author / license). |
Wire ABI
Plugins do not cross the .so boundary as Rust trait objects
(Box<dyn AudioNode>). A Rust trait object's layout and vtable are defined by
the compiling toolchain and are not ABI-stable; casting a foreign
.so's pointer to a Rust trait object is undefined behaviour. Instead every
plugin exposes plain extern "C" symbols:
audio_plugin_abi_magic() -> u32— must equalAUDIO_PLUGIN_ABI_MAGIC.audio_plugin_abi_version() -> u32— encoded viaencode_abi_version; the host accepts any plugin whose major matches its own.audio_plugin_metadata()/audio_plugin_create()— identity + opaque handle (consumed by the loader in a follow-up task).
The host never reinterpret-casts a returned pointer as a Rust trait object; it
only calls further extern "C" functions on it.
Dependencies
| Dependency | Version | Purpose |
|---|---|---|
audio-core-bsd |
0.1.0 (path) | Shared AudioNode / AudioFrame contract. |
libloading |
0.9 | dlopen / dlsym shared-library loading. |
thiserror |
2.0 | PluginError derive. |
libc |
0.2 | FFI types for pdfork. |
capsicum |
0.4.5 | FreeBSD only — Capsicum capability mode. |
pdfork |
0.1.1 | FreeBSD only — process-descriptor isolation. |
capsicum and pdfork are gated behind cfg(target_os = "freebsd") and are
not compiled on Linux/macOS/Windows (see NOTICE for the
MPL-2.0 attribution).
Status
0.x — experimental. The API is not yet frozen. Breaking changes are expected before a 1.0 release.
- edition: 2021
- MSRV: 1.85
- license: BSD-2-Clause
Example
use ;
// The host advertises ABI 1.0.
assert_eq!;
assert_eq!;
assert_eq!;
// A plugin built against 1.3 is compatible (same major).
let plugin_v = encode_abi_version;
assert!;
// A plugin built against 2.0 is NOT compatible (major bump).
assert!;
// Typed wrapper.
let host = decode;
assert!;
assert!;
// Metadata a plugin might expose.
let meta = new
.with_author
.with_license;
assert_eq!;
// The error path the loader raises on a mismatch.
let _err = AbiVersionMismatch ;
// The magic is checked separately from the version word.
let _magic = AUDIO_PLUGIN_ABI_MAGIC;
Security warning — untrusted shared objects
Loading a .so executes its initialisation code in the host process.
Never load a plugin you do not fully trust unless it is confined. On
FreeBSD this crate confines plugins with Capsicum; on other platforms the
sandbox is a no-op and untrusted plugins must not be loaded at all.
License
BSD-2-Clause. See LICENSE and NOTICE.
Design decisions are recorded in docs/adr/.