General-purpose ZeroMQ communication node with pub/sub, request/reply, registry, heartbeats, stats, and diagnostics
Project description
intrabus
intrabus is a lightweight, general-purpose ZeroMQ communication node for Python applications.
It gives independent modules a simple way to communicate over:
- publish/subscribe events
- request/reply messages
- module registration
- heartbeats and liveness tracking
- runtime statistics
- diagnostics
- node health queries
The goal is to keep setup simple: install the library, start a CommunicationNode, connect modules with BusInterface, and communicate.
pip install intrabus
Current release target:
0.2.2
intrabusis still alpha software. The public API is usable, but the project may still evolve before1.0.
Why intrabus?
Use intrabus when you want multiple Python components to communicate inside one machine or between local processes without deploying external infrastructure such as Redis, RabbitMQ, Kafka, or HTTP services.
Typical use cases:
- test automation systems
- modular desktop applications
- robotics or hardware-control tools
- local monitoring/control systems
- plugin-like Python applications
- prototypes that may later grow into distributed systems
intrabus is not intended to replace large production message brokers. It is designed to be small, embeddable, and easy to reason about.
Quick start
from intrabus import BusInterface, CommunicationNode
def handle_request(message: dict) -> dict:
return {
"pong": True,
"received": message,
}
with CommunicationNode("local"):
server = BusInterface("server", request_handler=handle_request)
client = BusInterface("client")
reply = client.send_request("server", {"ping": True}, timeout=2)
print(reply)
health = client.send_request(
"intrabus.node",
{"command": "node.get_health"},
timeout=2,
)
print(health["status"])
server.stop()
client.stop()
Core concepts
CommunicationNode
CommunicationNode owns the core intrabus runtime:
TopicBrokerfor pub/sub trafficCentralBrokerfor request/reply trafficModuleRegistryfor known modulesStatsCollectorfor runtime metricsDiagnosticsManagerfor runtime diagnostics
Most users should start here:
from intrabus import CommunicationNode
with CommunicationNode("local") as node:
print(node.is_running)
BusInterface
BusInterface is what application modules use to communicate.
from intrabus import BusInterface
module = BusInterface("sensor")
A BusInterface can:
- publish events
- subscribe to events
- send requests
- handle incoming requests
- auto-register with the local node
- send heartbeats to the local node
Module registry
When a module starts, it can automatically register with the node:
worker = BusInterface("worker")
The node tracks:
- module name
- node ID
- status:
onlineoroffline - connected time
- last seen time
- capabilities
- metadata
Heartbeats and stale detection
By default, modules send periodic heartbeats to the node.
The node can mark modules offline when they stop sending heartbeats:
reply = client.send_request(
"intrabus.node",
{
"command": "node.get_health",
"timeoutSeconds": 5,
},
)
Publish/subscribe
from intrabus import BusInterface, CommunicationNode
with CommunicationNode():
publisher = BusInterface("publisher")
subscriber = BusInterface("subscriber")
def on_temperature(topic: str, message: dict) -> None:
print(topic, message)
subscriber.subscribe("temperature", on_temperature)
publisher.publish("temperature", {"value": 23.5})
publisher.stop()
subscriber.stop()
Request/reply
from intrabus import BusInterface, CommunicationNode
def echo(message: dict) -> dict:
return {"echo": message}
with CommunicationNode():
server = BusInterface("server", request_handler=echo)
client = BusInterface("client")
reply = client.send_request("server", {"hello": "world"}, timeout=2)
print(reply)
server.stop()
client.stop()
BusInterface reserves a few request/reply payload fields for protocol
metadata:
correlationIdsender__intrabus_type
The __intrabus_type marker is added automatically as "request" or
"reply". It prevents late replies from being treated as new application
requests after node restarts or transient disconnects.
Node management commands
The internal node service is available at:
intrabus.node
Send requests to it with BusInterface.send_request().
node.get_registry
Returns the current module registry.
client.send_request("intrabus.node", {"command": "node.get_registry"})
node.get_stats
Returns current runtime statistics.
client.send_request("intrabus.node", {"command": "node.get_stats"})
node.get_diagnostics
Returns current diagnostics derived from registry and stats.
client.send_request("intrabus.node", {"command": "node.get_diagnostics"})
node.get_health
Returns a compact node health payload suitable for monitoring:
client.send_request(
"intrabus.node",
{
"command": "node.get_health",
"timeoutSeconds": 5,
},
)
The health response contains:
{
"ok": True,
"nodeId": "local",
"status": "healthy", # healthy | degraded | unhealthy
"staleModules": [],
"summary": {
"modules": {
"total": 2,
"online": 2,
"offline": 0,
},
"stats": {
"totalMessages": 10,
"totalRequests": 5,
"totalReplies": 4,
"totalTimeouts": 0,
"totalErrors": 0,
"totalEvents": 1,
"totalDeliveryFailures": 0,
},
"diagnostics": {
"count": 0,
"severityCounts": {},
},
},
"diagnostics": {...},
}
To include the full registry and raw stats payload, including recent events and
recent messages, pass includeDetails:
client.send_request(
"intrabus.node",
{
"command": "node.get_health",
"includeDetails": True,
},
)
node.reset_stats
Clears runtime statistics.
client.send_request("intrabus.node", {"command": "node.reset_stats"})
node.clear_diagnostics
Clears currently stored diagnostics.
client.send_request("intrabus.node", {"command": "node.clear_diagnostics"})
Backward-compatible node commands
The following older command names are still supported:
| Older command | Preferred command |
|---|---|
registry.get |
node.get_registry |
stats.get |
node.get_stats |
diagnostics.get |
node.get_diagnostics |
stats.reset |
node.reset_stats |
diagnostics.clear |
node.clear_diagnostics |
Internal module lifecycle commands are also supported:
module.registermodule.unregistermodule.heartbeatregistry.health
Most application code should use the node.* commands.
Runtime statistics
StatsCollector tracks bounded in-memory application traffic statistics:
- total messages
- requests
- replies
- events
- timeouts
- errors
- delivery failures
- latency samples
- average latency
- max latency
- per-module stats
- per-topic stats
- recent events
- recent message metadata
User-facing traffic stats intentionally exclude internal node-management traffic, including module registration, module heartbeat, registry polling, health checks, diagnostics polling, and stats polling. This keeps monitoring clients and web dashboards from inflating application message totals.
Recent messages are bounded and do not include full payloads by default. This avoids unbounded RAM growth and reduces the risk of accidentally storing sensitive data.
Diagnostics
DiagnosticsManager currently derives diagnostics from registry and stats.
Repeated recent stats events are grouped into higher-level diagnostics to keep
monitoring output readable.
Supported diagnostic codes:
| Code | Meaning |
|---|---|
MODULE_OFFLINE |
A registered module is offline |
REQUEST_TIMEOUT |
A request timed out |
HANDLER_ERROR |
A request handler raised an exception |
DELIVERY_FAILURE |
A message could not be delivered by the broker |
Diagnostic severities:
infowarningerrorcritical
node.get_health maps diagnostics to node status:
| Status | Meaning |
|---|---|
healthy |
No warnings or errors |
degraded |
At least one warning |
unhealthy |
At least one error or critical diagnostic |
MessageEnvelope
MessageEnvelope is a dataclass for future-compatible standardized messages.
It includes fields for future multi-node support:
messageIdcorrelationIdsendertargettopicsourceNodetargetNodettlhopPathpayload
The current brokers still use lightweight JSON payloads internally, but MessageEnvelope is available as the standard message shape for higher-level integrations.
Advanced: manual brokers
Most users should use CommunicationNode.
For low-level control, you can still start brokers manually:
from intrabus import run_central_broker, run_topic_broker
run_topic_broker()
run_central_broker()
And stop singleton brokers:
from intrabus import stop_central_broker, stop_topic_broker
stop_topic_broker()
stop_central_broker()
This is mainly useful for simple scripts and compatibility with older examples.
Development
Install development dependencies:
uv sync --dev
Run tests:
uv run pytest -q
Run linting and formatting checks:
uv run ruff check .
uv run ruff format --check .
Format code:
uv run ruff format .
Build the package:
uv run python -m build
Check distributions:
uv run twine check dist/*
Roadmap
Planned future work:
- event/message sinks for streaming to external consumers
- demo project using this package
- monitoring dashboard as a separate project
- richer diagnostics
- multi-node routing
- optional dashboard/websocket integrations
The library itself will stay focused on being a general-purpose communication node.
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file intrabus-0.2.2.tar.gz.
File metadata
- Download URL: intrabus-0.2.2.tar.gz
- Upload date:
- Size: 57.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2f39dec79ae774d36d2d3e20efc66e6ff6f69ee256daf9c34895cbd5a4859b7
|
|
| MD5 |
af38ba9469c60a9319ba25d70bd2204f
|
|
| BLAKE2b-256 |
977583330fc1834c17230663753672b456592c0a7181dbed6b2d0b0865bb665e
|
Provenance
The following attestation bundles were made for intrabus-0.2.2.tar.gz:
Publisher:
publish.yaml on BuilderCrafter/intrabus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
intrabus-0.2.2.tar.gz -
Subject digest:
a2f39dec79ae774d36d2d3e20efc66e6ff6f69ee256daf9c34895cbd5a4859b7 - Sigstore transparency entry: 1705855385
- Sigstore integration time:
-
Permalink:
BuilderCrafter/intrabus@ac6bd50a291c9484057c3368d0c7d887fcb9089a -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/BuilderCrafter
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@ac6bd50a291c9484057c3368d0c7d887fcb9089a -
Trigger Event:
push
-
Statement type:
File details
Details for the file intrabus-0.2.2-py3-none-any.whl.
File metadata
- Download URL: intrabus-0.2.2-py3-none-any.whl
- Upload date:
- Size: 21.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5c1b8b34b883265b28ec98b30fabfd19457a801ff26f6668f1369be791448d00
|
|
| MD5 |
ac722298eb2335defcb7f3a314e7cfc9
|
|
| BLAKE2b-256 |
362c56d108af015fcfb83817c19758037a22f861455423f1733f98f216219c53
|
Provenance
The following attestation bundles were made for intrabus-0.2.2-py3-none-any.whl:
Publisher:
publish.yaml on BuilderCrafter/intrabus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
intrabus-0.2.2-py3-none-any.whl -
Subject digest:
5c1b8b34b883265b28ec98b30fabfd19457a801ff26f6668f1369be791448d00 - Sigstore transparency entry: 1705855456
- Sigstore integration time:
-
Permalink:
BuilderCrafter/intrabus@ac6bd50a291c9484057c3368d0c7d887fcb9089a -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/BuilderCrafter
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@ac6bd50a291c9484057c3368d0c7d887fcb9089a -
Trigger Event:
push
-
Statement type: