Inputs and Outputs
In FlowMaker, node IO is explicit and versioned as part of flow and box definitions.
inputs: inbound ports (id,name)outputs: outbound ports (id,name)
A connection always links:
source.nodeId + source.outputId- to
destination.nodeId + destination.inputId
Why explicit ports matter
- Validation before run: invalid port mappings fail early during spec gathering.
- Safer refactors: changing a box IO contract forces visible flow changes.
- Predictable routing: each edge in the graph corresponds to one declared output→input path.
Canonical TypeScript model
interface FlowNodeDef {
id: string;
boxVersionId: string;
inputs: { id: string; name: string }[];
outputs: { id: string; name: string }[];
options?: Record<string, any>;
}
interface FlowConnection {
source: { nodeId: string; outputId: string };
destination: { nodeId: string; inputId: string };
}
Runtime initialization contract
When a run starts, each node receives only the connected ports through INIT_FLOW_ELEMENT:
interface FlowBoxInitParams {
runtimeContext: {
flowBoxId: string;
jobId: string;
nodeId: string;
usedBy?: string;
};
connectedIO: {
input: string[];
output: string[];
};
options: any;
}
This minimizes ambiguity in workers: each box knows exactly which input/output channels are active for that run.
[INFO!tune/CONNECTED IO ONLY]
connectedIOis intentionally runtime-scoped: workers should process only declared connected ports and ignore assumptions about optional ports not wired in the current graph.
Concrete flow wiring example
const flowDef = {
nodes: [
{
id: 'src-meter',
boxVersionId: 'industream/source/modbus/1.2.0',
inputs: [],
outputs: [{ id: 'telemetry', name: 'Telemetry stream' }],
},
{
id: 'pipe-normalize',
boxVersionId: 'industream/pipe/normalize-energy/2.0.0',
inputs: [{ id: 'raw', name: 'Raw values' }],
outputs: [{ id: 'normalized', name: 'Normalized values' }],
},
{
id: 'sink-historian',
boxVersionId: 'industream/sink/timeseries/3.1.0',
inputs: [{ id: 'in', name: 'Input data' }],
outputs: [],
},
],
connections: [
{
source: { nodeId: 'src-meter', outputId: 'telemetry' },
destination: { nodeId: 'pipe-normalize', inputId: 'raw' },
},
{
source: { nodeId: 'pipe-normalize', outputId: 'normalized' },
destination: { nodeId: 'sink-historian', inputId: 'in' },
},
],
};
Real-life IO patterns
- One-to-many fan-out: one output sends to both storage and alerting sinks.
- Many-to-one merge: several source streams converge into one correlation pipe.
- Branch by responsibility: one pipe output for archival, one for real-time decisions.
Common pitfalls to avoid
- Reusing human-readable names (
name) instead of stable IDs (id) in runtime logic. - Versioning a box without updating downstream port expectations.
- Connecting ports that are semantically incompatible (e.g., event stream vs batch payload) even if structural types match.
[ERROR!rule/CONTRACT VIOLATION] If a box upgrade changes port IDs or semantics, treat it as a breaking change and migrate flows explicitly; silent remapping by label leads to misrouted data.
Runtime references
platform-backend/docs/runtime-v2.md(FlowDef,FlowNodeDef,FlowConnection)platform-backend/docs/runtime-v2.md(INIT_FLOW_ELEMENTpayload)platform-backend/src/runner/run-node.class.tssdk/typescript/src/flowbox-init-params.ts