Skip to main content

Nodes

Nodes are computation units—the building blocks of your Cerulion graphs. If you’ve written code before, think of a node as a function (inputs → logic → outputs), packaged into a draggable box on the canvas. Each node contains code that processes data and produces output.

What is a Node?

A node is a self-contained unit of computation that:
  • Receives data through input ports (on the left side)
  • Processes data using code you write (in Rust, Python, or C++)
  • Sends data through output ports (on the right side)
  • Executes when triggered by incoming data
Think of nodes as specialized workers in a factory assembly line. Each worker (node) receives materials (input data), performs a specific task (your code), and passes the result (output data) to the next worker.
Nodes are similar to functions in traditional programming, but they’re designed for data-flow systems where execution is driven by data arrival rather than explicit function calls.

Why Use Nodes?

Modularity

Break complex systems into manageable, reusable components. Each node has a single responsibility.

Visual Design

See your system architecture at a glance. Visual representation makes data flow easy to understand.

Language Flexibility

Mix Rust, Python, and C++ in the same graph. Use the right language for each component.

Parallel Execution

Nodes can run in parallel when data is available. The framework handles concurrency automatically.

What a Node Contains

Every node has these components:
  • Name: Human-friendly identifier (e.g., Sensor Reader, Data Processor, Temperature Logger)
  • Language: Rust, Python, or C++ (you choose when creating the node)
  • Input ports: Connection points on the left side that receive data
  • Output ports: Connection points on the right side that send data
  • Implementation code: The node’s logic (written in the chosen language)
  • Triggers: Configuration that determines when the node executes

How Nodes Look on the Canvas

Nodes appear as rectangular boxes with a top banner and ports on the edges.

Top Banner

  • Color indicates the node’s language:
    • Orange/Rust - Rust nodes
    • Blue/Python - Python nodes
    • Purple/C++ - C++ nodes
  • Text shows the node name

Left Edge (Inputs)

From left-to-right you’ll see:
  • Input port circle (blue by default, or red if it’s a trigger)
  • (Optional) star marker if trigger is explicitly set
  • Argument name, formatted as i(n) where n is the input index (e.g., i0, i1)
  • Topic name (the channel this input listens to)

Right Edge (Outputs)

From right-to-left:
  • Output port circle (green)
  • Argument name, formatted as o(n) where n is the output index (e.g., o0, o1)
  • Topic name (the channel this output publishes to)
Visual Note: An annotated “node anatomy” diagram would be helpful here, showing the banner, ports, i(n)/o(n) labels, topic names, and trigger star marker.

Node Execution Model

Nodes execute when data arrives on their trigger inputs. Here’s how it works:
  1. Data arrives on a trigger input port
  2. Node wakes up and receives data from all input ports
  3. Your code runs with the received data
  4. Output is sent through output ports
  5. Node waits for the next trigger
By default, all inputs are triggers. This means the node executes whenever data arrives on any input. You can configure specific inputs as triggers for more control over execution timing.

Triggers

Inputs can be marked as triggers. When new data arrives on a trigger input, the node’s code executes.
  • If you do not explicitly set triggers, all inputs are triggers by default
  • Trigger inputs are visually indicated as:
    • a red input port (instead of blue)
    • a star next to the input name
Visual Note: A close-up screenshot showing a normal blue input vs a trigger input (red + star) would be helpful here.

Common Node Roles

Nodes typically fall into one of these roles:

Publisher Node

Produces data and publishes it. Usually has:
  • No inputs (or minimal configuration inputs)
  • One or more outputs that send data
Example: A Temperature Sensor node reads temperature values from hardware and outputs them through a green output port.

Processor Node

Transforms or filters incoming data. Usually has:
  • One or more inputs that receive data
  • One or more outputs that send processed data
Example: A Celsius to Fahrenheit node receives temperature in Celsius and outputs it in Fahrenheit.

Subscriber Node

Receives data and reacts (logging, display, storage). Usually has:
  • One or more inputs that receive data
  • No outputs (or minimal status outputs)
Example: A Temperature Logger node receives temperature readings and logs them to a file.

Actuator Node

Turns messages into actions (hardware/software control). Usually has:
  • One or more inputs that receive commands
  • No outputs (or status/feedback outputs)
Example: A Motor Controller node receives speed commands and controls a motor.
Visual Note: A simple 2–3 node pipeline graph (Publisher → Processor → Subscriber) would help illustrate these roles.

Node Lifecycle

Understanding the node lifecycle helps you write better node code:
  1. Initialization - Node is created, ports are set up
  2. Waiting - Node waits for data on trigger inputs
  3. Execution - Data arrives, your code runs
  4. Output - Results are sent through output ports
  5. Repeat - Node returns to waiting state
Node code should be non-blocking. If your code blocks (e.g., waits for user input, sleeps indefinitely), it will prevent the node from processing new data. Use async patterns or timers for delays.

Writing Node Code

Node code follows a simple pattern:
def main(input_port, output_port):
    # Receive data from input
    data = input_port.receive()
    
    # Process the data (your logic here)
    result = process_data(data)
    
    # Send result to output
    output_port.send(result)

Best Practices

Single Responsibility

Each node should do one thing well. If a node is doing too much, consider splitting it into multiple nodes.

Clear Naming

Use descriptive names that indicate what the node does. “Temperature Processor” is better than “Node1”.

Type Safety

Ensure input and output types match your schemas. The editor will warn you about type mismatches.

Error Handling

Handle errors gracefully. Don’t let one bad message crash your entire pipeline.

Next Steps

Now that you understand nodes, learn about the other building blocks: