Development
The primary build system used by Prismo is Bazel, which provides a hermetic and reproducible build environment for both the Rust and C++ parts of the project. Cargo manifests are still present for the Rust crates to allow for IDE integration and because rules_rust uses them to understand the workspace dependency graph.
Versioning
This project strictly follows semantic versioning, summarized below:
- MAJOR version when you make incompatible API changes
- MINOR version when you add functionality in a backwards compatible manner
- PATCH version when you make backwards compatible bug fixes
Note: read the whole spec at https://semver.org/ for more details, especially about pre-release versions and build metadata.
Version updates are anchored on MODULE.bazel, which acts as the single source of truth for the repo version. Cargo package versions are intentionally fixed at 0.0.0 because the Rust crates are maintained for in-repo development and IDE support rather than publication.
When making a change that requires a version bump, update MODULE.bazel, then run the tests:
$EDITOR MODULE.bazel
bazel test //...
After that change lands on main, GitHub Actions automatically creates a GitHub release tagged v<version> with generated release notes. The release job only runs when the MODULE.bazel version actually changed on that push.
Local Workflow
The main in-repo development path is the Bazel example bundle:
bazel run //app:example_prismo
# Or the shortcut
bazel run //:prismo
That target bundles the app with both in-repo example plugins so you can work on the full flow locally.
Bazel will automatically manage any tests that are defined. You can run all tests with:
bazel test //...
Format the workspace:
bazel run //:format
Clipboard Behavior
Copy uses OSC 52 terminal clipboard output from the app crate.
That means:
- it works best in terminals that explicitly support OSC 52
- it may be ignored in some local terminals, remote shells, or multiplexers depending on configuration
- the UI still shows success or failure messages based on whether the write itself succeeded
A shortlist of known good OSC 52 terminals include:
Notably, Apple Terminal does not support OSC 52.
Add a New Plugin
Plugins are designed to exist anywhere, either in this repo or closed sourced in your own projects. The only requirement is that they follow the protobuf-over-stdio contract and provide a manifest for discovery.
If you would like to build a plugin along with the project, please place it in the plugins/ directory and add a prismo-plugin.toml manifest. The example Rust and C++ plugins can be used as a reference.
For a new Rust plugin:
- ship a plugin manifest with a command entrypoint
- read
Initfromstdin - emit
Hello,DeclareChannels,SampleBatch, and optionalHealth - send descriptors before samples that reference them
The normal runtime path is discovery-first:
- bundle plugins under
./pluginsrelative to theprismoexecutable - use
--plugins /path/to/pluginswhen you want to pointprismoat a different plugin directory - for local repo development, use the Bazel example bundle targets so the plugin binaries are laid out next to their manifests
- keep
prismo-plugin.tomlchecked into the plugin directory rather than generating it inside the build
For the current C++ path:
- the canonical protocol is still the protobuf stdio contract
- C++ code talks to a small Diplomat-generated C++ API
- the Diplomat layer calls into the Rust SDK core
- Bazel generates the C and C++ bindings during the
cpp_sdkbuild, so there is no separate manual regeneration step
For downstream Bazel integration:
- load
//bazel:defs.bzl - use
prismo_pluginto package a plugin executable plus a checked-in manifest - use
prismo_bundleto assembleprismoplus packaged plugins; the bundle target itself is runnable withbazel run
Renderers
The TUI rendering logic is intentionally separate from the core data model and plugin protocol, so new renderers can be added without touching the runtime or plugin contract. Rendering logic lives in crates/tui/src/lib.rs.
The current renderer split is by ChannelValue:
- bytes -> hex/ASCII block
- numeric -> text summary plus line chart
- text/integer/bool -> text block
A few UI rules matter when extending renderers:
Detailsshould stay compact and fixed-height- long payloads belong in
Latest Value - scrollable panes should use the shared scrollbar path
- visible text colors now carry semantics similar to the channel tree, so keep label/value/status styling consistent
If you add richer value kinds later, this is where new pane renderers should go.
Evolve The Data Model
If you need more telemetry semantics, start in crates/core/src/model.rs.
Likely future expansions:
- enums with labels
- structured key/value values
- units and formatting hints
- source stream IDs
- richer quality/validity metadata
Website and Docs
The project website is built with Docusaurus from website/, and it uses the
markdown files in docs/ as the source for the /docs section of the site.
Build the static site with:
bazel build //website:site
The current prototype is intentionally small:
- a Rust workspace with a TUI app, an internal telemetry core, a protobuf-based plugin protocol, and example Rust and C++ plugins
- a two-pane telemetry UI built with
ratatuiandcrossterm - subprocess example plugins that generate randomized or synthetic telemetry so the UI can be developed without a live target
- a workspace split that keeps app wiring, UI, and telemetry contracts separate
Want to Contribute?
We appreciate any contributions! If you want to contribute, please open an issue and a PR with your proposed change. We are happy to provide feedback and guidance on how to get your contribution merged. If you don't know what to get started on, take a look at open issues with the "good first issue" label.
Notes For Future Multi-Language Plugins
The runtime is already subprocess-based and protocol-first.
That means new language support can be added by:
- keeping the same protobuf message contract
- providing language-specific SDKs
- reusing the same manifest and bundle layout model
The repo now demonstrates this in two ways:
- Rust plugins through
crates/plugin-sdk/rust - C++ plugins through
crates/plugin-sdk/cppand the generated Diplomat bindings