Section 1
1. CLI Reference
The morflow CLI handles dependency resolution, offline action caching, action discovery, and action specification inspection.
1.1. Installation
Install the standalone morflow command-line interface globally via cURL, npm, or pip:
# Install standalone native binary via cURL
curl -fsSL https://morflow.org/install.sh | bash
# Or install via NPM (Global CLI)
npm install -g morflow
# Or install via Pip (Python CLI)
pip install morflow1.2. Core CLI Commands
| Command | Syntax | Description |
|---|---|---|
check | morflow check <file.morf> [-p <dir>] | Validates syntax, verifies Single Static Assignment (SSA), inspects dynamic action FFI types, and performs a dry-run execution simulation. |
prep | morflow prep <file.morf> [-p <dir>] [-f] | Pre-downloads required actions for completely offline execution. |
search | morflow search <query> [-l <limit>] | Fuzzy searches actions in the central registry. |
spec | morflow spec <pkg>/<ver>/<action> | Inspects argument schemas, types, and action documentation. |
install | morflow install <action> [-p <dir>] [-f] | Installs a specific action binary directly into the cache. |
list | morflow list [-p <dir>] | Lists all installed action binaries in the local cache. |
clean | morflow clean [-p <dir>] | Removes cached action binaries from the action path. |
1.3. Pipeline Verification (check)
The check command performs a comprehensive static analysis and dry-run simulation of your .morf pipeline without requiring live input data. It guarantees that syntax, Single Static Assignment (SSA) rules, action binaries, and data type transitions are 100% valid before deployment:
# Verify pipeline syntax, action binaries, and type compatibility
morflow check ./pipeline.morf
# Check with a custom action cache directory
morflow check ./pipeline.morf --path ./actions_cache1.4. Offline Prep (prep)
In production deployments, you can ensure zero network requests at runtime by pre-downloading all action binaries during container image build:
# Download required action binaries into local cache
morflow prep ./pipeline.morf
# Force re-download even if action is already present
morflow prep ./pipeline.morf --force
# Download action binaries into a custom directory
morflow prep ./pipeline.morf --path ./actions_cache1.5. Search & Spec
Search for available actions and view their expected parameters directly in your terminal:
# Search for color or audio transformation actions
morflow search color
morflow search biquad
# Inspect argument schemas and SPEC.md docs
morflow spec image_essentials/latest/color_adjust
morflow spec audio_essentials/latest/biquad_filter1.6. Environment Variables
| Variable | Default | Description |
|---|---|---|
MORFLOW_ACTIONS_PATH | ~/.morflow/actions | Directory where Morflow caches and resolves compiled dynamic action libraries (.so, .dylib, .dll). |
MORFLOW_REPO | JiraPit/Morflow | Target GitHub repository used by the CLI to query releases and download precompiled action binaries. |
Section 2
2. .morf Syntax Guide
Learn .morf syntax from the ground up: starting from the smallest building block (an Action), to chaining them into a Flow, producing results with Emit, and composing Multiple Flows in a single file.
2.1. Anatomy of an Action
An Action is the fundamental atomic unit of computation in Morflow. It represents an isolated, high-performance compiled operation (e.g. image resizing, Gaussian blur, audio biquad filtering, tensor normalization).
Actions can be invoked in three main forms:
# Form 1: Zero-argument actions (parentheses are optional)
to_image
to_wav
identity
# Form 2: Keyword arguments (key=value)
resize(width=512, height=512, filter="bilinear")
color_adjust(contrast=1.15, saturation=1.05, brightness=0.02)
biquad_filter(type="highpass", freq=30.0, sample_rate=48000)
# Form 3: Variable arguments (passing pipeline parameters to action parameters)
resize(width=$target_width, height=$target_height)
resample(to_rate=$target_sample_rate)Before using actions, import their parent Action Pack at the top of your .morf file:
# Package-level import (brings actions into scope or under an alias)
import base/latest
import audio_essentials/latest as audio
# Item import (brings specific actions into local scope)
from image_essentials/latest import to_tensor, resize, color_adjust, gaussian_blur, to_image2.2. Constructing a Flow
A Flow connects an input variable through a sequence of actions using the >> dataflow operator. Each action receives the output payload of the previous action with zero memory copying.
Inputs and parameters are declared using accept at the top of the file:
# Required input passed from host runtime
accept $img_in
# Configurable parameters with default values
accept $target_width = 512
accept $target_height = 512Once inputs are declared, chain actions into a continuous dataflow stream:
# The flow starts with the input variable $img_in and passes data step-by-step
$img_in
>> to_tensor(color="rgb", dtype="f32", layout="hwc", normalize=true)
>> resize(width=$target_width, height=$target_height, filter="bilinear")
>> color_adjust(contrast=1.15, saturation=1.05)
>> gaussian_blur(sigma=1.2)
>> to_image(color="rgba", dtype="u8")2.3. Emitting Output & Intermediate Emission (emit)
Outputs are transferred back to the calling host application across many platforms and languages using the emit keyword. Importantly, emit is a non-terminating passthrough operation — it yields the computed payload at that point in execution while passing the data directly down the chain for subsequent operations.
This enables intermediate emission, allowing a single continuous flow to dispatch checkpoints, previews, or multi-scale outputs without duplicating upstream work or branching into separate pipelines:
# Form A: Single primary emission
$img_in >> to_tensor >> resize(width=512, height=512) >> emit
# Form B: Named stream emission
$audio_in >> to_audio >> normalize >> emit("normalized_audio")
# Form C: Intermediate emission with chained downstream processing
$img_in
>> to_tensor(color="rgb", normalize=true)
>> resize(width=1024, height=1024)
>> emit("high_res") # Emits intermediate 1024x1024 tensor to host
>> resize(width=256, height=256) # Continues stream without re-ingesting $img_in
>> color_adjust(contrast=1.2)
>> emit("preview") # Emits downstream preview thumbnaildict[str, np.ndarray] in Python, Map<String, MorflowTensor> in Java, or MorflowOutput in Rust). For streaming invocations, intermediate emissions can also be consumed asynchronously as they are produced.2.4. Multiple Flows in One File
A single .morf file can contain multiple flows. You can capture intermediate states mid-stream into a tapped variable using >> $var_name, and then spawn independent branch flows from that variable.
Morflow automatically compiles all flows into an acyclic dependency graph (DAG) and executes non-dependent branches concurrently across multi-threaded CPU cores with zero lock overhead:
import audio_essentials/latest
accept $input_audio
# FLOW 1: Ingest input audio, convert to structured stream, and tap into $decoded
$input_audio >> to_audio >> $decoded
# FLOW 2: Extract channel 0 (Left), apply low-pass filtering, convert to WAV, and emit
$decoded[0] >> biquad_filter(type="lowpass", freq=1200.0) >> to_wav >> emit("left_filtered")
# FLOW 3: Extract channel 1 (Right), apply high-pass filtering, convert to WAV, and emit
$decoded[1] >> biquad_filter(type="highpass", freq=800.0) >> to_wav >> emit("right_filtered")In this pipeline, Flow 2 and Flow 3 run concurrently in parallel immediately after Flow 1 finishes. Because both downstream branches depend solely on the shared output of Flow 1 ($decoded) but have no dependency on each other, Morflow's runtime automatically schedules and evaluates them simultaneously across CPU worker threads with zero lock overhead.
Static Single Assignment (SSA) Rule: A tapped variable identifier (e.g. $decoded) cannot be reassigned or mutated within the same scope. This mathematical guarantee ensures safe multi-threaded memory buffer sharing without copy overhead.
2.5. Advanced Control Flow (Loops & Conditionals)
Morflow includes built-in constructs for parallel loops, conditional branching, and pattern routing within flows:
Parallel Map Loop (each): The each block applies a transformation sub-flow across elements of a collection, batch, or multi-channel audio/image tensor. Each iteration is executed concurrently in parallel across multi-threaded CPU workers. Once all parallel iterations complete, their resulting outputs are automatically stacked back together in sequence order and passed directly to the next action in the stream.
# Iterates each channel concurrently in parallel, transforms, and stacks back together
$input_audio
>> to_audio(sample_rate=44100)
>> each ($channel) {
$channel
>> biquad_filter(type="highpass", freq=30.0, sample_rate=48000)
>> compressor(threshold_db=-12.0, ratio=3.0)
}
>> stereo_widen(width=1.2) # Receives the stacked multi-channel stream
>> to_wav
>> emitemit) inside an each block is strictly not allowed. Because loop iterations execute concurrently and asynchronously out-of-order, emissions must be performed outside the each block on the aggregated and stacked stream to maintain deterministic host synchronization and graph integrity.Conditionals & Dynamic Routing: Branch pipelines conditionally based on runtime metadata or route streams dynamically:
# 1. If / Else branching
if ($sample_rate != 48000) {
$input_audio >> resample(to_rate=48000) >> $prepared
} else {
$input_audio >> $prepared
}
# 2. Dynamic route pattern matching
$prepared >> route {
$channels == 1 => biquad_filter(type="lowpass", freq=1000.0)
$channels == 2 => stereo_widen(width=1.4)
else => identity
} >> to_wav >> emit2.6. Complete End-to-End Examples
Here are two complete, production-ready .morf pipeline definitions:
# Morflow Image Processing Pipeline (.morf)
import base/latest
from image_essentials/latest import to_tensor, resize, color_adjust, gaussian_blur, to_image
accept $img_in
accept $target_width = 512
accept $target_height = 512
# Direct chained dataflow from host image input to emitted output
$img_in
>> to_tensor(color="rgb", dtype="f32", layout="hwc", normalize=true)
>> resize(width=$target_width, height=$target_height, filter="bilinear")
>> color_adjust(contrast=1.15, saturation=1.05, brightness=0.02)
>> gaussian_blur(sigma=1.2)
>> to_image(color="rgba", dtype="u8")
>> emit# Morflow Audio DSP Pipeline (.morf)
import audio_essentials/latest
accept $input_audio
accept $sample_rate = 44100
# Direct chained DSP processing with inline multi-channel loop
$input_audio
>> to_audio(sample_rate=$sample_rate)
>> resample(to_rate=48000)
>> normalize(target_peak=0.95)
>> each ($channel) {
$channel
>> biquad_filter(type="highpass", freq=30.0, sample_rate=48000)
>> compressor(threshold_db=-12.0, ratio=3.0)
}
>> stereo_widen(width=1.2)
>> limiter(ceiling_db=-0.1)
>> to_wav
>> emitSection 3
3. Pipeline Execution
Execute compiled .morf dataflow graphs natively across languages with zero train-serve skew. Morflow provides idiomatic host SDKs for Python, JavaScript/Node.js, Java, and Rust, mapping host tensors, image arrays, and raw binary streams directly into the engine's execution graph.
3.1. Python
The Python SDK (morflow) integrates directly with NumPy arrays, raw bytes, and file streams with zero-copy buffer sharing into the underlying engine.
pip install morflowimport numpy as np
from PIL import Image
import morflow
# 1. Load pipeline from file
pipeline = morflow.load("image_pipeline.morf")
# 2. Pass NumPy arrays directly [H, W, C]
img = Image.open("input.png").convert("RGB")
np_img = np.array(img, dtype=np.uint8)
# 3. Execute pipeline synchronously with native speed
result_np = pipeline.run(np_img)
# 4. Save result
out_img = Image.fromarray(result_np)
out_img.save("output.png")
# ----------------------------------------------------
# Audio Processing (Pass and receive raw WAV bytes)
# ----------------------------------------------------
audio_pipe = morflow.load("audio_pipeline.morf")
with open("input.wav", "rb") as f:
input_bytes = f.read()
# Single output returns bytes; multi-stream returns dict[str, bytes]
output_wav_bytes = audio_pipe.run(input_bytes)
with open("output.wav", "wb") as f:
f.write(output_wav_bytes)3.2. JavaScript / Node.js
The Node.js SDK provides both asynchronous execution (via pipeline.run() to offload work to a background worker pool without blocking the V8 event loop) and synchronous execution (via pipeline.runSync()).
npm install morflowimport fs from 'node:fs';
import morflow from 'morflow';
// 1. Load pipeline from file
const pipeline = morflow.load('image_pipeline.morf');
// 2. Construct TensorInput with shape and buffer
const inputBuffer = fs.readFileSync('input_raw_rgb.bin');
const tensorInput = {
data: inputBuffer,
shape: [1080, 1920, 3],
dtype: 'u8'
};
// 3. Run asynchronously off the main thread
const outputTensor = await pipeline.run(tensorInput);
console.log(`Shape: ${outputTensor.shape}, Dtype: ${outputTensor.dtype}`);
const outBuffer = outputTensor.toBuffer();
fs.writeFileSync('output_raw_rgba.bin', outBuffer);
// ----------------------------------------------------
// Audio DSP Execution (Direct Buffer I/O)
// ----------------------------------------------------
const audioPipe = morflow.load('audio_pipeline.morf');
const wavBuffer = fs.readFileSync('input.wav');
// Execute asynchronously
const outWav = await audioPipe.run(wavBuffer);
fs.writeFileSync('output.wav', outWav.toBuffer());3.3. Java
The Java SDK wraps native JNI bindings with type-safe classes, automatic memory cleanup, and direct tensor buffers for high-throughput enterprise JVM backends.
<dependency>
<groupId>org.morflow</groupId>
<artifactId>morflow</artifactId>
<version>0.1.0</version>
</dependency>import org.morflow.Morflow;
import org.morflow.Pipeline;
import org.morflow.MorflowTensor;
import java.nio.file.Files;
import java.nio.file.Path;
public class MorflowRunner {
public static void main(String[] args) throws Exception {
// 1. Load pipeline
try (Pipeline pipeline = Morflow.load("image_pipeline.morf")) {
// 2. Wrap image bytes into MorflowTensor [H, W, C]
byte[] rgbBytes = Files.readAllBytes(Path.of("input_rgb.raw"));
MorflowTensor inputTensor = MorflowTensor.fromByteArray(
rgbBytes,
new int[]{1080, 1920, 3}
);
// 3. Execute pipeline
try (MorflowTensor outputTensor = pipeline.run(inputTensor)) {
int[] outShape = outputTensor.getShape();
byte[] outBytes = outputTensor.toByteArray();
Files.write(Path.of("output.raw"), outBytes);
}
}
// Audio WAV execution
Pipeline audioPipe = Morflow.load("audio_pipeline.morf");
byte[] inputWav = Files.readAllBytes(Path.of("input.wav"));
MorflowTensor audioOut = audioPipe.run(inputWav);
Files.write(Path.of("output.wav"), audioOut.toByteArray());
}
}3.4. Rust
The native Rust engine crate (morflow) provides direct access to Payload variants (Image, Audio, Tensor, Data) with compile-time type safety and zero runtime overhead.
cargo add morflowuse morflow::{ColorSpace, Image, Morflow, Payload, RVec};
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Load pipeline from file
let mut pipeline = Morflow::load("image_pipeline.morf")?;
// 2. Wrap image buffer into a Morflow Image payload
let img = image::open("input.png")?.to_rgb8();
let input = Image::from_u8_hwc(
&img,
img.width() as usize,
img.height() as usize,
ColorSpace::Rgb,
).map_err(|e| e.to_string())?;
// 3. Execute Morflow pipeline
let outputs = pipeline.run(Payload::Image(input))?;
// 4. Extract emitted payload and save output
if let Payload::Image(out) = outputs.into_single()? {
let bytes = out.to_contiguous_bytes();
image::save_buffer(
"output.png",
bytes.as_slice(),
out.width() as u32,
out.height() as u32,
image::ColorType::Rgba8,
)?;
}
// ----------------------------------------------------
// Audio DSP Execution (Direct Data Payload)
// ----------------------------------------------------
let mut audio_pipe = Morflow::load("audio_pipeline.morf")?;
let wav_bytes = fs::read("input.wav")?;
let audio_out = audio_pipe.run(Payload::Data {
buffer: RVec::from(wav_bytes),
})?;
if let Payload::Data { buffer } = audio_out.into_single()? {
fs::write("output.wav", buffer.as_slice())?;
}
Ok(())
}