A visual workflow automation tool with a WebGL-rendered node graph editor. Built with Rust + WGPU for high-performance rendering and a Blazor WebAssembly frontend for UI controls.
┌──────────────────────────────┐
│ Front End │
│ Blazor WebAssembly UI │
│ - Panels, Buttons │
│ - Inspectors, Controls │
└───────────────┬──────────────┘
│ Commands & Events (Protobuf)
┌───────────────▼──────────────┐
│ Render Engine Core │
│ (Rust) │
│ - Node graph data model │
│ - Geometry generation │
│ - Camera & interaction │
└───────────────┬──────────────┘
│ GPU abstraction
┌───────────────▼──────────────┐
│ WGPU backend │
│ - Native (Vulkan/Metal/DX) │
│ - Web (WebGPU) │
└──────────────────────────────┘
Tech Stack:
- Frontend: Blazor WebAssembly (UI controls, panels, inspectors)
- Backend: Rust (node graph data model, rendering engine)
- Rendering: WGPU (cross-platform GPU abstraction)
- Desktop: Tauri 2.5 (embeds Blazor in native webview)
- Communication: Protocol Buffers (Blazor ↔ Rust)
- Plugins: WASM-based node definitions (first-party and third-party)
- Rust toolchain (latest stable via rustup)
- .NET 9.0 SDK or later (download)
- Node.js (for Tauri CLI)
- Tauri CLI:
cargo install tauri-cli@^2.0.0 - wasm-bindgen-cli:
cargo install wasm-bindgen-cli - cargo-component:
cargo install cargo-component - WASM targets:
rustup target add wasm32-unknown-unknown(for renderer)rustup target add wasm32-wasip1(for plugins)
cognexus/
├── wit/ # WebAssembly Interface Type definitions
│ └── plugin.wit # Plugin interface for types and nodes
├── backend/
│ ├── model/ # Node graph data structures & traits
│ ├── types/ # First-party data types (WASM component)
│ ├── nodes/ # First-party nodes (WASM component)
│ ├── renderer/ # WGPU rendering engine (WASM for browser)
│ ├── proto/ # Protobuf message definitions
│ └── common/ # Shared utilities
├── frontend/
│ └── cognexus/ # Blazor WebAssembly UI
├── cli/
│ └── inspect/ # CLI tool for inspecting plugin components
└── apps/
└── desktop/
└── cognexus/ # Tauri desktop app
# Clone the repository
git clone https://github.com/yourusername/cognexus.git
cd cognexus
# Install Rust dependencies
cargo build
# Install .NET dependencies
cd frontend/cognexus
dotnet restore
cd ../..The project uses Just for automated builds. Just is a cross-platform command runner that handles the entire build process.
Install Just:
cargo install just
# or on macOS: brew install justDevelopment Mode:
just devThis builds WASM components and runs the desktop app with hot-reload.
Production Build:
just build-allThis builds everything (WASMs + desktop app) and creates the release bundle.
Run Production Build:
To see debug output, plugin discovery logs, and error messages, run the binary directly in a terminal:
# macOS - run binary directly to see console output
/Users/tony/git/cognexus/target/release/bundle/macos/Cognexus.app/Contents/MacOS/cognexus-desktop
# Windows
./target/release/cognexus-desktop.exe
# Linux
./target/release/bundle/appimage/cognexus.AppImageThis shows plugin discovery output, resource paths, and any debug/error messages.
Individual Component Builds:
Debug builds:
just build-types-debug # Build only the types WASM component (debug)
just build-nodes-debug # Build only the nodes WASM component (debug)
just build-wasm-debug # Build both WASM components (debug)Release builds:
just build-types-release # Build only the types WASM component (release)
just build-nodes-release # Build only the nodes WASM component (release)
just build-wasm-release # Build both WASM components (release)Other targets:
just build-desktop # Build the desktop app (requires WASMs already built)The justfile automatically:
- Builds WASM components using
cargo component build - Copies WASM files to
target/{debug,release}/resources/builtin/ - Runs Tauri build/dev commands in the correct order
- Handles both debug and release build configurations
To build and run the desktop application:
# Step 1: Build the renderer as WASM
cargo build --target wasm32-unknown-unknown -p cognexus-renderer
# Step 2: Generate JavaScript bindings for the renderer
wasm-bindgen target/wasm32-unknown-unknown/debug/cognexus_renderer.wasm \
--out-dir frontend/cognexus/wwwroot/wasm \
--target web
# Step 3: Publish Blazor frontend (copies to Tauri frontend directory)
cd frontend/cognexus
dotnet publish -c Release
cd ../..
# Step 4: Run the desktop app
cargo tauri devFirst-party types and nodes are compiled as WASM Component Model components using cargo-component:
# Build types component
cargo component build -p cognexus-types
# Build nodes component
cargo component build -p cognexus-nodesOutput files:
target/wasm32-wasip1/debug/cognexus_types.wasmtarget/wasm32-wasip1/debug/cognexus_nodes.wasm
These are WASM Components (not raw WASM modules), implementing the WIT interfaces defined in wit/plugin.wit.
# Build renderer with optimizations
cargo build --target wasm32-unknown-unknown --release -p cognexus-renderer
# Generate JS bindings
wasm-bindgen target/wasm32-unknown-unknown/release/cognexus_renderer.wasm \
--out-dir frontend/cognexus/wwwroot/wasm \
--target web
# Publish Blazor
cd frontend/cognexus
dotnet publish -c Release
cd ../..
# Build production desktop app
cargo tauri buildThe cognexus-inspect CLI tool allows you to interrogate WASM plugin components and discover their metadata.
# Inspect a types plugin
cargo run -p cognexus-inspect -- <path-to-wasm> --kind types
# Inspect a nodes plugin
cargo run -p cognexus-inspect -- <path-to-wasm> --kind nodesInspecting types:
cargo run -p cognexus-inspect -- target/wasm32-wasip1/debug/cognexus_types.wasm --kind typesOutput:
Found 1 data type(s):
- Signal (989bcbb2-b1a1-4f3f-be15-22ada278aedc)
Description: A flow control signal with no data payload
Version: 0.1.0
Inspecting nodes:
cargo run -p cognexus-inspect -- target/wasm32-wasip1/debug/cognexus_nodes.wasm --kind nodesOutput:
Found 2 node(s):
- Start (40ebe0be-d2db-4eed-80f3-91267352ee42)
Description: Initiates workflow execution
Version: 0.1.0
Input ports: 0
Output ports: 1
- End (e7a20e26-27ce-4d49-9759-50db835d46e6)
Description: Terminates workflow execution
Version: 0.1.0
Input ports: 1
Output ports: 0
The inspect tool:
- Loads the WASM component using wasmtime's Component Model support
- Instantiates the component with WASI
- Calls the appropriate discovery function (
list-typesorlist-nodes) - Displays metadata including UUIDs, names, descriptions, versions, and port information
This demonstrates the plugin discovery mechanism that will be used by the desktop app to load plugins at runtime.
# 1. Edit Rust code in backend/renderer/
# 2. Rebuild WASM and bindings
cargo build --target wasm32-unknown-unknown -p cognexus-renderer
wasm-bindgen target/wasm32-unknown-unknown/debug/cognexus_renderer.wasm \
--out-dir frontend/cognexus/wwwroot/wasm \
--target web
# 3. Republish Blazor
cd frontend/cognexus && dotnet publish -c Release && cd ../..
# 4. Restart Tauri
cargo tauri dev# Changes to backend/model/ are automatically picked up by cargo tauri dev
# Just save your changes and Tauri will rebuild# 1. Edit backend/types/src/
# 2. Rebuild types component
cargo component build -p cognexus-types
# 3. Inspect the component to verify changes
cargo run -p cognexus-inspect -- target/wasm32-wasip1/debug/cognexus_types.wasm --kind types
# 4. Type components will be loaded at runtime (TODO: implement loader)# 1. Edit backend/nodes/src/
# 2. Rebuild node component
cargo component build -p cognexus-nodes
# 3. Inspect the component to verify changes
cargo run -p cognexus-inspect -- target/wasm32-wasip1/debug/cognexus_nodes.wasm --kind nodes
# 4. Node components will be loaded at runtime (TODO: implement loader)See AGENTS.md for detailed architecture documentation.
- Implement the
NodeDefinitionandNodeDefinitionInfotraits:
use cognexus_model::graph::{NodeDefinition, NodeDefinitionInfo};
pub struct MyCustomNode;
impl NodeDefinitionInfo for MyCustomNode {
fn definition_id(&self) -> Uuid { /* ... */ }
fn name(&self) -> &str { "My Node" }
fn input_port_specs(&self) -> Vec<(&str, Uuid)> { /* ... */ }
fn output_port_specs(&self) -> Vec<(&str, Uuid)> { /* ... */ }
// ...
}
impl NodeDefinition for MyCustomNode {
type Error = MyNodeError;
fn execute(&self, inputs: Vec<u8>) -> Result<Vec<u8>, Self::Error> {
// Your node logic here
}
}- Add WIT component metadata to your
Cargo.toml:
[package.metadata.component]
package = "cognexus:plugin"
target = { path = "../../wit", world = "nodes-plugin" }- Implement the Component Model guest trait in your crate's
lib.rs:
mod bindings;
use bindings::exports::cognexus::plugin::nodes::Guest;
struct Component;
impl Guest for Component {
fn list_nodes() -> Vec<NodeInfo> {
// Return metadata for your nodes
}
}
bindings::export!(Component with_types_in bindings);- Compile to WASM Component:
cargo component build -p your-node-crate- Inspect to verify:
cargo run -p cognexus-inspect -- target/wasm32-wasip1/debug/your_node_crate.wasm --kind nodes- The runtime will load your component at runtime (loader implementation in progress).
# Run all tests
cargo test
# Test specific crate
cargo test -p cognexus-model
cargo test -p cognexus-renderer
# Test with output
cargo test -- --nocapture# Format code
cargo fmt
# Lint code
cargo clippy# Format code
cd frontend/cognexus
dotnet formatAdd the js feature to uuid in Cargo.toml:
uuid = { version = "1.19.0", features = ["v4", "serde", "js"] }Make sure you've run all three steps:
- Build renderer WASM
- Generate JS bindings with wasm-bindgen
- Publish Blazor frontend
Check that the Blazor frontend was published:
ls apps/desktop/cognexus/frontend/Should contain index.html and related files.
This is a learning project. See AGENTS.md for architectural decisions and development guidelines.
See LICENSE for details.