Warning
Plugin bundles are experimental. The format, ABI, tooling, and loader behavior can change at any time.
CyberEther plugins are .cep bundles that contain one or more shared libraries,
a manifest, and optional example flowgraphs. They are useful for keeping custom
processing blocks outside the main CyberEther source tree while still using
CyberEther's block, module, scheduler, runtime, and memory APIs.
Starting From The Blueprint
The repository includes a plugin blueprint at:
examples/plugins/blueprint
Copy that directory when starting a new plugin, then rename the project, include folder, source folder, block type, module type, and plugin name.
Folder Layout
The blueprint uses the same block and module layout as built-in CyberEther blocks:
.
|-- include/
| `-- blueprint/
| `-- gain/
| |-- block.hh
| `-- module.hh
|-- examples/
| `-- blueprint_gain.yml
|-- src/
| |-- plugin.cc
| |-- meson.build
| `-- blueprint/
| `-- gain/
| |-- block_impl.cc
| |-- meson.build
| |-- module_impl.cc
| |-- module_impl.hh
| `-- module_impl_native_cpu.cc
|-- subprojects/
| `-- cyberether.wrap
|-- tools/
| |-- bundler.py
| `-- merger.py
|-- meson.build
`-- meson_options.txt
The public headers in include/ define the block and module configuration.
The source files in src/ implement the block, implement the module, and
register the native CPU provider. Files in examples/ are bundled as plugin
examples. The tools/bundler.py script creates the .cep bundle for the copied
blueprint, and tools/merger.py combines single-target bundles into one
multi-target bundle.
CEP Bundles
A .cep file is a tar.gz archive with a .cep extension. It must include a
manifest.yml at the archive root:
metadata:
name: cyberether-blueprint-plugin
version: 0.1.0
minimumJetstreamVersion: 1.9.1
targets:
- path: targets/macos-arm64-cpu/cyberether_blueprint_plugin.dylib
system: macos
device: cpu
arch: arm64
examples:
- path: examples/blueprint_gain.yml
| Field | Purpose |
|---|---|
metadata.name |
Plugin bundle name. |
metadata.version |
Plugin bundle version in x.y.z form. |
metadata.minimumJetstreamVersion |
Minimum CyberEther/Jetstream version required to load the bundle, in x.y.z form. |
targets[].path |
Shared library path inside the bundle. |
targets[].system |
Target system, such as macos, linux, or windows. |
targets[].device |
Device backend required to load this library, such as cpu, cuda, metal, vulkan, or webgpu. |
targets[].arch |
Target architecture, such as arm64 or x86_64. |
examples[].path |
Example flowgraph path inside the bundle. |
The target device is a compatibility discriminator for one shared-library variant, not a list of every provider compiled into that library. CyberEther opens every target that matches the current system, architecture, and compiled device backends. Do not list the same shared library under multiple device labels: a host with those backends enabled would load the library more than once and attempt to drain its static registrations more than once.
Build each target with provider registrations for its declared device, produce
a single-target bundle, and merge those bundles for release. Common block
registrations may be present in each variant, but module provider registrations
with the same key must not overlap between compatible variants. Release
automation can package multiple systems, architectures, and devices in the same
.cep this way.
Both version fields contain exactly three decimal components between 0 and 255. Prerelease and build suffixes are not supported.
Plugin ABI
Every target shared library must export CyberEther's plugin ABI symbol. In the
blueprint this lives in src/plugin.cc:
#include <jetstream/plugin.hh>
JST_REGISTER_PLUGIN();
The ABI record only identifies the target library as a compatible CyberEther plugin ABI.
| Field | Purpose |
|---|---|
| Magic | Identifies the exported record as CyberEther's plugin ABI. |
| Size | Size of the ABI record exported by the plugin. |
| ABI version | Plugin ABI version expected by CyberEther. |
Blocks And Modules
A block is the user-facing graph node. It defines the block type, domain, description, configuration fields, inputs, and outputs.
A module does the runtime work. The blueprint includes a BlueprintGain module
with a native CPU implementation that accepts F32 and CF32 tensors.
The key registration points are:
JST_REGISTER_BLOCK(BlueprintGainImpl, {"blueprint_gain"});
and:
JST_REGISTER_MODULE(BlueprintGainImplNativeCpu,
DeviceType::CPU,
RuntimeType::NATIVE,
"generic");
When CyberEther loads a compatible target from the bundle, those static registrations are drained into the CyberEther registry.
Dependencies
A plugin bundle is copied to machines you do not control, so its shared libraries should load without extra setup. The recommendation is to depend only on libraries that are commonly present on the target system, such as the C and C++ standard libraries, and to link everything else statically into the plugin library.
If static linking is not possible for some dependency, the plugin documentation must clearly list every external dependency the user needs to install, including the expected version range and the package name on each supported system. A plugin that fails to load because of a missing shared library is hard for users to diagnose, so treat undocumented runtime dependencies as a packaging bug.
Bundling
Use the blueprint's tools/bundler.py to create .cep files. From your copied
blueprint directory:
./tools/bundler.py \
--output build/cyberether_blueprint_plugin.cep \
--name cyberether-blueprint-plugin \
--version 0.1.0 \
--minimum-jetstream-version 1.9.1 \
--target path=build/cyberether_blueprint_plugin.dylib,system=macos,device=cpu,arch=arm64 \
--example examples/blueprint_gain.yml
Repeat --target for distinct production library variants. Each system,
device, and architecture combination may appear only once, and one source
library cannot be reused under multiple device labels for the same system and
architecture. Repeat --example to include more example flowgraphs.
Merging Bundles
Passing every target to a single bundler.py invocation requires all shared
libraries to be available on one machine. Release automation usually builds
each target on its own runner instead, producing one single-target .cep per
platform. Use tools/merger.py to combine those into one multi-target bundle:
./tools/merger.py \
--output build/cyberether_blueprint_plugin.cep \
build/macos-arm64.cep \
build/linux-x86_64.cep
The merger validates the inputs before writing the output:
- Every input bundle must have identical metadata, including the name, version, and minimum Jetstream version.
- Each
system,device, andarchcombination may appear in only one input bundle. - Examples with the same path must have identical content. Matching examples are deduplicated in the output.
The output is written atomically, so an interrupted merge never leaves a
partial .cep behind.
Building Standalone
Build the blueprint as a standalone plugin from its own directory:
cd examples/plugins/blueprint
meson setup build -Ddevices=cpu
meson compile -C build
On Linux, the output is:
build/cyberether_blueprint_plugin.cep
The blueprint includes subprojects/cyberether.wrap, so Meson can fetch
CyberEther as a fallback when it cannot find an installed CyberEther dependency.
For a browser plugin, use Emscripten and CyberEther's cross file so the side module uses the same threading, exception, SIMD, and LTO settings as the browser host:
meson setup build-wasm examples/plugins/blueprint \
--cross-file meson/crosscompile/emscripten.ini \
-Dbuildtype=release \
-Ddevices=cpu \
-Dtests=false
meson compile -C build-wasm cyberether_blueprint_plugin_cep
The shared library in the build directory is an intermediate target. The .cep
file is the user-facing plugin artifact.
Loading A Plugin
CyberEther loads plugins through its plugin loader. At load time, CyberEther:
- Copies the
.cepbundle into the plugin cache. - Extracts the bundled
tar.gzinto a cache folder. - Reads and validates
manifest.yml. - Selects targets matching the current system, architecture, and device support.
- Opens every compatible shared library.
- Looks up the exported plugin ABI symbol for each target.
- Validates ABI magic, size, and ABI version.
- Drains static block and module registrations into the registry.
- Registers bundled examples from
examples[].path.
After the plugin is loaded, its registered blocks can be built like other CyberEther blocks. The user-facing side of this flow, including registration through the preferences window, is covered in Installing Plugins.