PyRED
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 PyRED 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 PyRED node package is an ordinary distribution that advertises a
pyred.nodesentry point.
Installing
The distribution is pyflowred on PyPI — pyred was already taken there
by an unrelated project. The import package, the command and the editor are
all still PyRED.
pip install pyflowred # or: uv pip install pyflowred
pyred # pyflowred runs the same thing
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 pyred:0.1.0 . # released
docker build -f Dockerfile.source -t pyred:source . # this checkout
docker run -d -p 1880:1880 -v pyred-data:/data pyred:0.1.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/pyred/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/pyred
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/pyred-node-lowercase; the details are in
docs/writing-nodes.md.
Layout
src/pyred/
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.1.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.1.0.tar.gz | 13.3 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyflowred-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 23.5 MB
Release files / pyflowred-0.1.0.tar.gz
| Download URL | pyflowred-0.1.0.tar.gz |
|---|---|
| Size | 13.3 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d2e7ef1b79f38bf403e639e0f53f0d7995ec256c0d3438f8d9e969e3f58267fe
|
|
BLAKE2b-256 checksum How to use checksums |
b6fc8d11cad48f779342940e3dd6a4c79b8015a2c5b7a75644520bdc9dc25cf1
|
| 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.1.0-py3-none-any.whl
| Download URL | pyflowred-0.1.0-py3-none-any.whl |
|---|---|
| Size | 10.2 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
21a0fa3045aa0734423b06b08f9aa8c30f5749994702790425e05919ea984d42
|
|
BLAKE2b-256 checksum How to use checksums |
195bf95295fce55d2d2d3ddb08bda0dbe62a58ab5aafd1d0d5f6c79da7c48659
|
| 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}
|