# Keeping state in a flow Need a running total or a last-seen value? Make it a message. Function nodes have no hidden `context` or global flow store. ## Read and write the same message Declare `reading` and `total` as inputs, and `total` as an output: ```python def process(reading, total): return {"total": total + reading} ``` Add `total` to the flow's **Inputs** with a starting value of `0.0`. Each execution reads the previous total and publishes the next one. - A node is never downstream of itself, so its own publication does not run it again. - A triggering input needs a starting value. Without one, validation reports `self_loop_needs_initial`; a Python default alone does not supply the message. ## Feedback between two nodes A controller may read an estimate that depends on its own previous output. Ordinary triggering inputs would form a cycle. Mark the feedback input **non-triggering** in the flow definition instead: ```json {"name": "estimate", "port": "estimate", "dtype": "float", "trigger": false} ``` It creates no dependency and does not wake the node. Whenever another input runs the node, it reads the latest estimate. If no estimate exists yet, that argument is omitted, so give it a Python default: ```python def process(reading, estimate=0.0): return {"command": reading - estimate} ``` Use this on the feedback input; keep the inputs that should wake the node triggering. Configure `trigger` through the API or flow JSON; the node panel does not currently expose it. ## Built-in state Built-in nodes such as delay, rate limit and filter-on-change keep their own timing or comparison state in the engine. Storage and device nodes also manage the state needed for their connections. Your Function code still exchanges state through declared messages. ## See also - [Where a node's values come from](values.md) - [Node types](../reference/node-types.md)