PyFlowRED
A port of Node-RED 5.0.7 to Python.
The same editor, the same flow file, the same node set, the same HTTP admin API — with a Python runtime and nodes written in Python.
What "port" means here
| Layer | Origin |
|---|---|
| Editor client | Node-RED source, vendored and built with the same pipeline |
| Node edit dialogs and message catalogues | Node-RED source, adapted only where the runtime language shows through |
| HTTP admin API | Re-implemented in Python, wire-compatible with the editor |
| Flow runtime, node registry, context, credentials, storage | Re-implemented in Python |
| Projects (git-backed flows), multiplayer, palette catalogue | Re-implemented in Python |
| Prometheus metrics endpoint and exporter nodes | New; the nodes ported from node-red-contrib-prometheus-exporter |
| Core nodes | Re-implemented in Python — all 50 types |
The editor is not a reimplementation. It is Node-RED's own editor, talking to a Python implementation of the API it expects. Flows exported from Node-RED import into PyFlowRED and back again: the flow file is the same JSON, and the credentials file uses the same encryption.
See docs/porting.md for the full correspondence.
Where it deliberately differs
Two things cannot be carried over one-to-one, because they are the JavaScript runtime:
-
The Function node runs Python. The same three tabs (On Start, On Message, On Stop), the same
node/flow/envobjects, the same send and done semantics — but the body is Python:msg["payload"] = msg["payload"] * 2 return msg
The body is compiled into an
async def, soawaitworks directly.globalis a Python keyword, so the global context object isglobal_. -
Packages come from PyPI. The Function node's Setup tab and the palette manager install Python distributions. A PyFlowRED node package is an ordinary distribution that advertises a
pyflowred.nodesentry point.
Installing
pip install pyflowred # or: uv pip install pyflowred
pyflowred
One name throughout: the distribution on PyPI, the command, the import
package, the PYFLOWRED_* settings and the editor are all pyflowred.
(pyred was already taken on PyPI, which is where the name came from; up to
0.1.0 the import package and command were still pyred, and 0.2.0 renamed
them to match.)
The wheel carries the built editor, so this needs Python and nothing else — no Node, no build step. The editor is then at http://127.0.0.1:1880/.
In a container
Two images, same application. Dockerfile installs the released package with
uv and carries no build tooling; Dockerfile.source builds this checkout in
three stages, Node included.
docker build -t pyflowred:0.2.0 . # released
docker build -f Dockerfile.source -t pyflowred:source . # this checkout
docker run -d -p 1880:1880 -v pyflowred-data:/data pyflowred:0.2.0
Your own CA is a build argument or a mount, and reaches every library in the runtime. See docker/README.md.
Building it
Node.js is needed once, to build the editor. It is never needed at runtime.
npm install # editor build dependencies
npm run build # builds src/pyflowred/editor_client/public
uv build --wheel # ~10 MB, py3-none-any
That build output is a git-ignored artifact, so the wheel has to pull it back
in deliberately (tool.hatch.build.targets.wheel.artifacts). A build hook
refuses to produce a wheel when the editor has not been built, because the
failure mode otherwise is a package that installs, starts, and serves a blank
page. The editor source is excluded from the wheel and kept in the sdist,
where it can be rebuilt.
Working on it
npm install && npm run build
uv venv --python 3.13
uv pip install -e ".[dev]"
.venv/bin/pyflowred
Without
adminAuth, anyone who can reach the editor can run arbitrary Python as the user running the process. See docs/operations.md.
Writing a node
def register(RED):
class LowerCaseNode(RED.Node):
def __init__(self, config):
super().__init__(config)
self.on("input", self._on_input)
def _on_input(self, msg, send, done):
msg["payload"] = msg["payload"].lower()
send(msg)
done()
RED.nodes.register_type("lower-case", LowerCaseNode)
with the matching .html next to it, exactly as in Node-RED. A complete
example package is in examples/pyflowred-node-lowercase; the details are in
docs/writing-nodes.md.
Layout
src/pyflowred/
util/ property expressions, JSONata, mustache, cloning, logging, i18n
runtime/ flow engine, node registry, context, credentials, storage
registry/ node discovery, loading, palette installation
api/ HTTP admin API, editor routes, comms WebSocket (Starlette)
metrics/ the Prometheus registry, runtime instrumentation, endpoint
nodes/core/ the core node set: Python implementations + Node-RED dialogs
editor_client/ the Node-RED editor, vendored and built
tools/
editor-build/ the Node-RED editor build pipeline, re-pointed at this repo
devserver.sh start/stop a throwaway instance
uicheck.py load the editor in a real browser and report on it
uiflow.py deploy and run a flow through the editor, in a real browser
uiprojects.py commit a deploy through the project UI, in a real browser
benchflows.py seven benchmark flows, 82 nodes across 33 node types
loadtest.py drive them and sample the runtime's CPU, RSS and fds
rawbaseline.py a bare Starlette endpoint, to measure the runtime against
Dockerfile the released package, installed with uv
Dockerfile.source this checkout, editor and all
docker/ entrypoint, compose, CA drop-in
examples/ an example node package
docs/ porting notes, node authoring, operations, benchmarks
tests/ the test suite, including a browser test of the editor
Tests
.venv/bin/python -m pytest tests
The suite covers every node group against real sockets (including an
in-process MQTT broker), the runtime (subflows, groups, context, credentials,
deploy types), the admin API, projects against a real git binary, and — when
Playwright is available — the actual editor in a headless browser: import a
flow, press Deploy, open the Function node, click Inject, read the debug
sidebar.
Licence
Apache-2.0, as Node-RED is. The vendored editor and node dialogs keep their original copyright headers.
Metadata
Release files for pyflowred 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pyflowred-0.2.0.tar.gz | 13.3 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyflowred-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 23.5 MB
Release files / pyflowred-0.2.0.tar.gz
| Download URL | pyflowred-0.2.0.tar.gz |
|---|---|
| Size | 13.3 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5b268691638f4637852cfa6dfcfd3a7d18895ed1013f29dcf9c298a763c47141
|
|
BLAKE2b-256 checksum How to use checksums |
c8d878f123a187d9932eee6d2af1c15dfc58002e986b9e6ba2d0594f00d611fd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / pyflowred-0.2.0-py3-none-any.whl
| Download URL | pyflowred-0.2.0-py3-none-any.whl |
|---|---|
| Size | 10.2 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
eb00c9d2193e1945400593e97db3c97e7a9abd235f1f38fcae5bdf979b115b13
|
|
BLAKE2b-256 checksum How to use checksums |
69a2188ebe127ca5b9074e6b6ea0723cde70a7414f3ca32ec1e2ad8c45602f12
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|