🚧Documentation is under active development — content may be incomplete or change frequently.
Skip to Content
Node System

Node System

A node is one computational step. A workflow is nodes wired together on a canvas, executed in dependency order. That’s the whole model.

Salpa ships with the Hello World Pipeline as a worked example you already have installed — three nodes that encrypt a message, decrypt it, and write a side-by-side comparison report. Pure Python, no dependencies, runs in seconds. The rest of this page is a tour of that pipeline.

Run it

  1. Open Salpa. Click New Workflow, give it a name, and pick a working directory.
  2. In the Node Library or the Templates dialog, find Hello World Pipeline and load it.
  3. The canvas now shows three connected nodes: Hello EncryptHello DecryptHello Reveal.
  4. Click Execute All.

You’ll see status indicators light up in order. When the run finishes, three files appear in your working directory:

demo_encrypted.txt Ciphertext from Encrypt demo_decrypted.txt Recovered plaintext from Decrypt demo_reveal.txt Side-by-side comparison report from Reveal

Open demo_reveal.txt. That report — character mapping, statistics, the whole thing — was built by a node that read two files written by the two upstream nodes. You just ran a real data pipeline.

Anatomy of a node on the canvas

Each node on the canvas is a box with five things you can interact with:

ElementWhat it is
BodyThe node itself. Click it to open the configuration panel on the right.
Input portsDots on the left. Wires coming in deliver data from upstream nodes.
Output portsDots on the right. Wires going out feed downstream nodes.
Status indicatorColor-coded dot showing idle, queued, running, completed, or failed.
Side panelForm on the right where you set the node’s parameters.

When you connect two nodes, you’re saying “the output of A flows into B.” Salpa figures out the execution order from the graph.

What flows on the wires?

A wire doesn’t carry “the whole world.” It carries a small dictionary that the upstream node returns. In the Hello World Pipeline, the only thing that flows between nodes is case_name — the identifier used to name output files.

Here’s the real fallback logic from Hello Encrypt (full source):

# hello_encrypt/node.py case_name = flow_vars["case_name"].get_value() if not case_name and predecessor_data: input_data = predecessor_data[0] if predecessor_data else None if input_data and isinstance(input_data, dict): case_name = input_data.get("case_name", "secret") if not case_name: case_name = "secret"

The pattern: each node reads its own configured value first, then falls back to whatever the upstream node sent, then falls back to a hardcoded default. Other parameters — the message text, the cipher shift, the output directory — are configured independently on each node. That’s what makes nodes composable: each one can stand alone, with predecessor data as a hint rather than a hard dependency.

Anatomy of a node on disk

A node package is just a directory. The minimum viable node has two files:

hello_encrypt/ ├── meta.toml The manifest — name, class, hashtags ├── node.py The code — parameters and execute() ├── README.md Optional: documentation shown in the marketplace └── pixi.toml Optional: triggers isolated environment

Larger packages add core.py (pure logic), tests/, and demo_data/, but the manifest plus a Python file is the whole irreducible core.

View the source. The full Hello World Pipeline source is published alongside this site. Open any file in your browser, or right-click and save it to disk:

FilePurpose
package.tomlMulti-node bundle manifest
workflows/hello-world-pipeline.jsonThe pre-wired workflow template
hello_encrypt/meta.toml · node.py · README.mdThe Encrypt node
hello_decrypt/meta.toml · node.py · README.mdThe Decrypt node
hello_reveal/meta.toml · node.py · README.mdThe Reveal node

Here’s the real manifest for Hello Encrypt (view full file):

# hello_encrypt/meta.toml [package] name = "hello-encrypt" version = "1.0.0" description = "Encrypts a message using a Caesar cipher and writes the ciphertext to a file" author = "BoCoFlow Development Team" license = "MIT" [node] class_name = "HelloEncrypt" display_name = "Hello Encrypt" hashtags = [ "hello-world", "encryption", "starter", "tutorial", "text-output", "beginner", ] [node.metadata] tooltip = "Encrypts a message with a Caesar cipher and writes the ciphertext to a file"

class_name ties to the Python class in node.py. display_name is what you see on the canvas. hashtags drive search and grouping in the Node Library — there’s no rigid category hierarchy. Five to ten descriptive hashtags is the sweet spot.

For the Python side, see Build Custom Nodes — that page dissects this same node line by line.

Execution strategies

Salpa picks an execution strategy automatically based on what’s in the package directory:

StrategyWhenWhat happens
In-processNo pixi.tomlRuns directly in the worker process. Fast startup, no isolation.
Pixi subprocesspixi.toml presentCreates an isolated Python environment, runs the node in a subprocess. Full dependency isolation.
ContainerReservedDocker/Podman execution. Implemented but not currently used.
HPC / SLURMVendor-specificRemote cluster execution via SSH. Configured per node, not auto-detected.

The Hello World Pipeline runs in-process because none of its three nodes have a pixi.toml — that’s why it starts instantly. Heavier nodes like pdb2pqr or esmfold-prediction ship a pixi.toml, and Salpa creates an isolated Python environment for them on first run. Your existing nodes don’t see those dependencies. Their nodes don’t see yours.

A handful of nodes — including the Hello World Pipeline — ship pre-installed with Salpa. If you ever uninstall one, you can reinstall it from the marketplace offline; no network connection required.

Naming conventions

Salpa uses different cases in different contexts:

ContextConventionExample
Display name (canvas, palette)Title CaseHello Encrypt
Package name (meta.toml)kebab-casehello-encrypt
Directory namesnake_casehello_encrypt/
Python classPascalCaseHelloEncrypt

The reason: Python imports require snake_case directories, but kebab-case is the standard for distributable package names, and Title Case reads better in the UI.

What’s next