The pub/sub broker that filters, governs and discovers — at the wire level.

HYQP is a lightweight publish/subscribe protocol and broker for the Internet of Things. It adds regex topic routing, broker-side JSON Schema validation, value-based payload predicates, topic ownership & discovery, and multi-format payloads — over a simple, human-readable TCP text protocol. Built on Java 21 virtual threads with zero dependencies.

Java 21 · virtual threads Zero dependencies Text protocol over TCP QoS 0 / 1 / 2 Docker: 78549/hyqp:v2.0

Protocol & reference implementation designed by Dr. Badr El Khalyly.

Why HYQP?

Semantic filtering

Subscribers receive only the messages they want, selected by value with ten operators — the broker evaluates the predicate before any byte hits the wire.

Regex routing

Topic segments can be Java regular expressions, usable symmetrically in SUBSCRIBE and PUBLISH. One publish reaches a value-defined set of topics.

Schema governance

Attach a JSON Schema to a topic; the broker rejects malformed payloads at ingress, before any fan-out.

Ownership & discovery v2.0

Every topic has an owner. Query the owner, publishers, subscribers and metadata at runtime — no external registry.

Multi-format payloads v2.0

Carry JSON, text, XML and binary (octet/PDF, base64-framed) on one hierarchy, with per-topic content typing.

Virtual-thread scale

One Java 21 virtual thread per connection — thousands of clients, ~80 MB resident, zero message loss in every benchmark.

TL;DR — Run the broker with one Docker command, then build publishers and subscribers with the Python or Arduino/ESP32 SDK. Start here →

Quick Start

Run the broker, then connect a subscriber and a publisher. Three terminals, five minutes.

1 · Run the broker

With Docker (no JDK needed):

# Pull and run the broker (TCP 4444, state persisted in a volume)
docker pull 78549/hyqp:v2.0
docker run -d --name hyqp -p 4444:4444 -v hyqp-data:/app/data 78549/hyqp:v2.0

Or from source (Java 21):

javac -d target/classes $(find src/main/java -name "*.java")
java -cp target/classes com.hyqp.broker.BrokerMain   # default port 4444

2 · Subscribe (only hot readings)

java -cp target/classes com.hyqp.broker.client.Subscriber dash sensors/temp '{"value":{"$gt":30}}'

3 · Publish

java -cp target/classes com.hyqp.broker.client.Publisher sensor-1
> CREATE sensors/temp {"type":"object","required":["value"],
    "properties":{"value":{"type":"number","minimum":-50,"maximum":100}}}
> PUBLISH sensors/temp {"value":22}    # not delivered (22 <= 30)
> PUBLISH sensors/temp {"value":75}    # delivered to dash
> PUBLISH sensors/temp {"value":200}   # ERROR: exceeds schema maximum 100
You can drive the broker by hand with telnet localhost 4444 — the protocol is plain text.

Protocol Reference

Every frame is a single UTF-8 line terminated by \n, over a long-lived TCP connection.

Client → Broker

CommandSyntax
CONNECTCONNECT <client> [PERSISTENT]
CREATECREATE <topic> [<schema-json>]
SUBSCRIBESUBSCRIBE <topic> [FILTER <predicate-json>]
UNSUBSCRIBEUNSUBSCRIBE <topic>
PUBLISHPUBLISH [RETAIN] [QOS0|QOS1|QOS2] [TYPE <ct>] <topic> <payload>
DISCONNECTDISCONNECT
QoS acksPUBACK | PUBREC | PUBREL | PUBCOMP <messageId>
OWNER v2.0OWNER <topic>
PUBLISHERS v2.0PUBLISHERS <topic>
SUBSCRIBERS v2.0SUBSCRIBERS <topic>
DESCRIBE v2.0DESCRIBE <topic>
LIST v2.0LIST [prefix]

Topic matching

SegmentMatches
literalexactly that string
+exactly one arbitrary segment
#zero or more trailing segments (last only)
regex('pattern')one segment matching a Java regex

Payload predicate operators

OperatorMeaningOperatorMeaning
$eqequals$inin list
$neqnot equal$ninnot in list
$gt / $gtegreater (or equal)$containssubstring
$lt / $lteless (or equal)$regexpattern match

Content types v2.0

TYPEWire framing
json (default)verbatim JSON — validated if the topic has a schema
text / xmlverbatim UTF-8 line
octet / pdfbase64 of raw bytes (rejected on a schema-governed topic)

Tutorials

Short, focused walkthroughs of each HYQP capability.

Regex topic routing

Subscribe to every numbered production line at once, ignoring non-numeric zones:

SUBSCRIBE factory/regex('line-[0-9]+')/temp
# matches factory/line-1/temp, factory/line-42/temp -- NOT factory/zone-A/temp

One command to many topics (symmetric publish)

PUBLISH factory/regex('line-[0-9]+')/control {"cmd":"recalibrate"}
# the broker expands the pattern and delivers to every matching concrete topic

Schema governance

CREATE sensors/temp {"type":"object","required":["value"],
  "properties":{"value":{"type":"number","minimum":-50,"maximum":100}}}
# a later PUBLISH of {"value":150} is rejected before any subscriber sees it

Dynamic filter (no reconnect)

SUBSCRIBE sensors/temp FILTER {"value":{"$gt":50}}   # set
SUBSCRIBE sensors/temp FILTER {"value":{"$gt":90}}   # tighten, same connection
SUBSCRIBE sensors/temp                              # remove filter

Governance & discovery v2.0

OWNER sensors/temp          # -> OK Owner of sensors/temp: admin
PUBLISHERS sensors/temp     # -> OK Publishers of sensors/temp: sensor-1
SUBSCRIBERS sensors/temp    # -> OK Subscribers of sensors/temp: dash
DESCRIBE sensors/temp       # -> OK Topic ... | owner=admin | schema=yes | ...
LIST sensors/               # -> OK Topics: sensors/humidity, sensors/temp

Multi-format payloads v2.0

PUBLISH TYPE xml   sensors/cfg     <cfg rate="5s"/>
PUBLISH RETAIN TYPE octet line-1/firmware  iVBORw0KGgo...   # base64 bytes
PUBLISH           sensors/temp    {"value":22.5}              # json (default)

Tutorial 1 — Topic model & matching, in depth

A topic is a slash-separated path. A filter (used in SUBSCRIBE and in wildcard PUBLISH) may mix four kinds of segment. The table shows what each filter matches against a set of concrete topics.

FilterMatchesDoes NOT match
sensors/tempsensors/tempsensors/temp/in, sensors/hum
sensors/+sensors/temp, sensors/humsensors/temp/in (two levels)
sensors/#sensors, sensors/temp, sensors/temp/inother/x
+/temp/+home/temp/livinghome/temp (missing 3rd level)
factory/regex('line-[0-9]+')/tempfactory/line-1/temp, factory/line-42/tempfactory/zone-A/temp
regex('b-[0-9]+')/regex('f-[0-9]+')/tb-5/f-12/tb-5/lobby/t
Literal-only filters match exactly like MQTT, so existing MQTT topic conventions keep working; regex segments are a strict, additive extension.

Tutorial 2 — The ten payload operators, by example

A predicate is a JSON object: each field maps to one or more operators; all conditions are ANDed. The broker evaluates it before delivery, so non-matching messages never reach the subscriber. Each row shows a filter, a payload, and whether it is delivered.

Filter clausePayloadDelivered?
{"unit":{"$eq":"celsius"}}{"unit":"celsius"}yes
{"unit":"celsius"} (shorthand $eq){"unit":"fahrenheit"}no
{"state":{"$neq":"ok"}}{"state":"alarm"}yes
{"v":{"$gt":30}}{"v":30}no (strict)
{"v":{"$gte":30,"$lt":80}}{"v":30}yes
{"v":{"$lte":80}}{"v":80}yes
{"zone":{"$in":["A","B"]}}{"zone":"B"}yes
{"zone":{"$nin":["X"]}}{"zone":"X"}no
{"msg":{"$contains":"alarm"}}{"msg":"alarm-high"}yes
{"id":{"$regex":"S[0-9]+"}}{"id":"S42"}yes

Combine conditions across fields (logical AND). There is no explicit OR — express it as two subscriptions on the same topic:

SUBSCRIBE sensors/temp FILTER {"value":{"$gt":20,"$lt":80},"unit":{"$eq":"celsius"}}
# OR (value>90) OR (value<0): two subscriptions
SUBSCRIBE sensors/temp FILTER {"value":{"$gt":90}}
SUBSCRIBE sensors/temp FILTER {"value":{"$lt":0}}

Tutorial 3 — Schema governance, every constraint

A schema is attached at CREATE time and enforced on every PUBLISH, before fan-out. Supported constraints: type, required, nested properties, minimum/maximum, minLength/maxLength, enum, and items.

CREATE sensors/temp {"type":"object","required":["value"],
  "properties":{
    "value":{"type":"number","minimum":-50,"maximum":100},
    "unit" :{"type":"string","enum":["celsius","fahrenheit"]},
    "tags" :{"type":"array","items":{"type":"string","minLength":1}}
  }}
PayloadResultReason
{"value":22.5,"unit":"celsius"}acceptedall constraints met
{"unit":"celsius"}ERRORmissing required value
{"value":150}ERRORexceeds maximum 100
{"value":"hot"}ERRORvalue is not a number
{"value":22,"unit":"kelvin"}ERRORunit not in enum
# On rejection the publisher gets a descriptive error:
ERROR Schema validation failed: $.value: value 150.0 exceeds maximum 100.0
A schema is immutable once set, and a schema-governed topic only accepts json payloads — a TYPE xml/octet publish to it is rejected.

Tutorial 4 — Quality of Service flows

QoS 0 — at most once (fire-and-forget)

PUBLISH sensors/temp {"value":22}
# subscriber gets:  MESSAGE sensors/temp {"value":22}

QoS 1 — at least once (PUBACK)

PUBLISH QOS1 sensors/temp {"value":22}
# broker -> subscriber:  MESSAGE 7 QOS1 sensors/temp {"value":22}
# subscriber -> broker:  PUBACK 7
# 10s retransmit window, up to 3 retries if no PUBACK

QoS 2 — exactly once (4-step handshake)

PUBLISH QOS2 sensors/temp {"value":22}
# broker -> sub:  MESSAGE 8 QOS2 sensors/temp {"value":22}
# sub   -> broker: PUBREC 8
# broker -> sub:  PUBREL 8
# sub   -> broker: PUBCOMP 8   (delivered exactly once)

Tutorial 5 — Retained messages

PUBLISH RETAIN sensors/temp {"value":22}     # stored as last-known value
# any LATER subscriber receives it immediately on subscribe:
SUBSCRIBE sensors/temp                       # -> MESSAGE sensors/temp {"value":22}
PUBLISH RETAIN sensors/temp                   # empty body clears the retained value
Retained messages survive broker restarts (flushed to data/retained/) and compose with any QoS level and with payload predicates.

Tutorial 6 — Persistent sessions

CONNECT edge-1 PERSISTENT
SUBSCRIBE factory/line-1/temp FILTER {"celsius":{"$gt":50}}
DISCONNECT
# ...later, the same client reconnects:
CONNECT edge-1 PERSISTENT
# -> OK Connected as edge-1 (session restored)  -- subscriptions & filters are back

Tutorial 7 — Governance & discovery, every verb v2.0

CREATE sensors/temp {...schema...}    # caller becomes the owner
OWNER sensors/temp                    # -> OK Owner of sensors/temp: admin
PUBLISHERS sensors/temp               # -> OK Publishers of sensors/temp: sensor-1, sensor-2
SUBSCRIBERS sensors/temp              # -> OK Subscribers of sensors/temp: dash, alarm
DESCRIBE sensors/temp                 # -> OK Topic sensors/temp | owner=admin | created=... | schema=yes | contentType=json | publishers=2 | subscribers=2
LIST sensors/                         # -> OK Topics: sensors/humidity, sensors/temp
A topic implicitly created by an early SUBSCRIBE is unowned; a later CREATE may claim it while it is still unowned and schema-less.

Tutorial 8 — Multi-format payloads, round-trip v2.0

TYPEPublishDelivered as (Python)
jsonPUBLISH t {"v":1}dict {"v":1}
textPUBLISH TYPE text t hellostr "hello"
xmlPUBLISH TYPE xml t <c/>str "<c/>"
octetPUBLISH TYPE octet t <base64>bytes (decoded)
pdfPUBLISH TYPE pdf t <base64>bytes (decoded)
# Delivery echoes the type so the subscriber knows how to decode:
MESSAGE TYPE octet line-1/firmware iVBORw0KGgo...
# json deliveries omit the tag, so plain subscribers are unaffected:
MESSAGE sensors/temp {"value":22.5}

Tutorial 9 — Responses & error handling

ResponseMeaning
OK Connected as <c> [(session restored)]session established
OK Topic created[ with schema]: <t>CREATE ok
OK Subscribed to <t>[ with filter]subscription registered
OK Filter updated / removed on <t>predicate mutated in place
OK Published to <t>publication accepted
ERROR Schema validation failed: <detail>payload rejected at ingress
ERROR Topic already exists: <t>CREATE on an owned/schema'd topic
ERROR No such topic: <t>discovery query on an unknown topic

Python SDK

Zero-dependency client for dashboards, scripts and gateways. Runs on CPython 3.8+ and Raspberry Pi.

pip install -e sdk/python        # or set PYTHONPATH=sdk/python

Publisher

from hyqp import Publisher

with Publisher(host="localhost", port=4444, client_id="sensor-1") as pub:
    pub.create_topic("sensors/temp", schema={
        "type":"object", "required":["value"],
        "properties":{"value":{"type":"number","minimum":-50,"maximum":100}}})
    pub.publish("sensors/temp", {"value": 23.5}, qos=1)

Subscriber with a value filter

from hyqp import Subscriber

def on_msg(topic, payload, qos, mid):
    print(topic, payload)      # dict for JSON, str for text/xml, bytes for octet/pdf

sub = Subscriber(host="localhost", port=4444, client_id="dash", on_message=on_msg)
sub.connect()
sub.subscribe("sensors/+", filter={"value":{"$gt":30}})
sub.loop_forever()

Multi-format & discovery v2.0

pub.publish_text("line-1/note", "calibrated 09:00")
pub.publish_xml("line-1/cfg", "<cfg ver='4.2.1'/>")
pub.publish_bytes("line-1/firmware", open("fw.bin","rb").read())   # base64 on the wire

pub.owner("sensors/temp")        # -> 'sensor-1'
pub.subscribers("sensors/temp")  # -> ['dash']
pub.describe("sensors/temp")     # -> {'owner':'sensor-1','schema':'yes',...}
pub.list_topics("sensors/")      # -> ['sensors/temp', ...]

API reference (facades)

MethodPurpose
connect(timeout=5.0)open TCP + send CONNECT (returns the OK line)
create_topic(topic, schema=None)CREATE, optionally with a JSON Schema dict
publish(topic, payload, qos=0, retain=False, content_type=None)publish; dict/list→JSON, bytes→octet/base64
publish_text / publish_xml / publish_bytes / publish_filetyped helpers (v2.0)
subscribe(topic, callback=None, filter=None)subscribe with optional per-topic callback & predicate
unsubscribe(topic)cancel a subscription
owner / publishers / subscribers / describe / list_topicsgovernance & discovery (v2.0)
loop_forever() / stop() / disconnect()blocking loop / stop / close

Scenario A — Continuous sensor publisher

import time, random
from hyqp import Publisher

pub = Publisher(host="localhost", port=4444, client_id="sensor-01")
pub.connect()
try:
    pub.create_topic("sensors/temp", schema={
        "type":"object","required":["value"],
        "properties":{"value":{"type":"number","minimum":-50,"maximum":150}}})
except Exception:
    pass   # topic already exists

while True:
    pub.publish("sensors/temp", {"value": round(random.uniform(15,45),1)}, qos=1)
    time.sleep(2)

Scenario B — Per-topic callbacks + default handler

from hyqp import Subscriber

def on_temp(topic, payload, qos, mid): print("temp", payload["value"])
def on_any(topic, payload, qos, mid):  print("other", topic, payload)

sub = Subscriber(client_id="dash", on_message=on_any)   # default handler
sub.connect()
sub.subscribe("sensors/temp", callback=on_temp)            # per-topic handler
sub.subscribe("sensors/humidity")                        # falls back to on_any
sub.loop_forever()

Scenario C — Dynamic filter during an incident

sub.subscribe("sensors/temp")                              # see everything
sub.subscribe("sensors/temp", filter={"value":{"$gt":80}})  # incident: only hot
sub.subscribe("sensors/temp")                              # back to normal (filter removed)
# the connection and any QoS state are preserved across all three

Scenario D — Persistent session + lifecycle callbacks

def on_connect(client, resp):    print("connected:", resp)
def on_disconnect(client, why):  print("lost:", why)

sub = Subscriber(client_id="edge-7", persistent=True,
                 on_message=lambda t,p,q,m: print(t,p),
                 on_connect=on_connect, on_disconnect=on_disconnect)
sub.connect()                       # "(session restored)" on reconnect
sub.subscribe("factory/regex('line-[0-9]+')/temp", filter={"celsius":{"$gt":50}})
sub.loop_forever()

Scenario E — Receiving multi-format payloads

def on_msg(topic, payload, qos, mid):
    # payload type reflects the content type automatically:
    if isinstance(payload, dict):    handle_json(payload)                # json
    elif isinstance(payload, bytes): open("fw.bin","wb").write(payload) # octet/pdf
    else:                            handle_text(payload)                # text/xml (str)

sub = Subscriber(client_id="fw-watch", on_message=on_msg); sub.connect()
sub.subscribe("line-1/#")

Scenario F — Error handling

from hyqp import Publisher, HYQPError
try:
    pub.publish("sensors/temp", {"value": 999})   # violates schema maximum
except HYQPError as e:
    print("rejected:", e)   # ERROR Schema validation failed: $.value: ...
Full runnable examples ship in sdk/python/examples/: pub_example.py, sub_example.py, full_scenario.py, test_regex_filters.py, test_v2_features.py and flagship_smart_factory.py.

Arduino / ESP32 SDK

A C++ client for ESP32/ESP8266 endpoints — zero dependencies beyond the Arduino WiFi stack.

Copy sdk/arduino/HYQP into your Arduino libraries/ folder (or zip-import it), then:

Publisher sketch

#include <WiFi.h>
#include <HYQP.h>

HYQPClient client("192.168.1.10", 4444, "esp32-1");

void setup(){
  WiFi.begin("ssid", "pass");
  while(WiFi.status()!=WL_CONNECTED) delay(300);
  client.connect();
  client.createTopic("sensors/temp", "{\"type\":\"object\",\"required\":[\"value\"]}");
  client.publish("sensors/temp", "{\"value\":23.5}", 1);   // QoS 1
}
void loop(){ client.loop(); delay(10); }

Subscriber with callback & filter

void onMsg(const char* topic, const char* payload, uint8_t qos, int id){
  Serial.printf("%s -> %s\n", topic, payload);
}
// in setup():
client.onMessage(onMsg);
client.subscribe("sensors/temp", "{\"value\":{\"$gt\":30}}");

Multi-format & discovery v2.0

uint8_t blob[] = {0x89,0x50,0x4E,0x47};
client.publishBytes("line-1/firmware", blob, sizeof(blob));  // base64
client.publishXml("line-1/cfg", "<cfg/>");
String owner = client.owner("sensors/temp");
String meta  = client.describe("sensors/temp");

API reference

MethodPurpose
connect(bool persistent=false)connect; true requests a persistent session
createTopic(topic, schemaJson=nullptr)CREATE, optional schema string
publish(topic, payload, qos=0, retain=false)publish JSON/text
publishText / publishXml / publishBytestyped helpers; publishBytes base64-encodes (v2.0)
subscribe(topic, filterJson=nullptr, cb=nullptr)subscribe with predicate & per-topic callback
owner / publishers / subscribers / describe / listTopicsdiscovery (return String) (v2.0)
base64Encode / base64Decodestatic binary helpers
loop()call every iteration of Arduino loop()
QoS 1/2 acknowledgements are handled automatically inside loop(); just call client.loop() frequently.

Scenario A — Robust publisher with WiFi auto-reconnect

#include <WiFi.h>
#include <HYQP.h>

HYQPClient client("192.168.1.10", 4444, "esp32-line1");

void ensureConnected(){
  if(WiFi.status()!=WL_CONNECTED){ WiFi.reconnect(); return; }
  if(!client.isConnected()) client.connect(true);   // persistent session
}

void setup(){
  Serial.begin(115200);
  WiFi.begin("ssid", "pass");
  while(WiFi.status()!=WL_CONNECTED) delay(300);
  client.connect(true);
  client.createTopic("factory/line-1/temp",
    "{\"type\":\"object\",\"required\":[\"value\"],"
    "\"properties\":{\"value\":{\"type\":\"number\",\"minimum\":-50,\"maximum\":150}}}");
}

unsigned long last=0;
void loop(){
  ensureConnected();
  client.loop();
  if(millis()-last > 2000){
    last=millis();
    char buf[48]; snprintf(buf,sizeof(buf), "{\"value\":%.1f}", readTemp());
    client.publish("factory/line-1/temp", buf, 1, true);  // QoS1 + RETAIN
  }
}

Scenario B — Subscriber with a value filter

void onTemp(const char* topic, const char* payload, uint8_t qos, int id){
  Serial.printf("[%s] %s\n", topic, payload);
}
// in setup(), after connect():
client.subscribe("factory/regex('line-[0-9]+')/temp",
                 "{\"value\":{\"$gt\":50}}", onTemp);

Scenario C — Sending and decoding binary

// send a small binary blob (base64 on the wire)
uint8_t img[] = {0x89,0x50,0x4E,0x47,0x0D,0x0A};
client.publishBytes("line-1/snapshot", img, sizeof(img));

// decode an incoming base64 (octet) payload back to bytes
uint8_t out[256];
size_t n = HYQPClient::base64Decode(payload, out, sizeof(out));

Scenario D — Runtime discovery from the device

Serial.println(client.owner("factory/line-1/temp"));        // "plant-ops"
Serial.println(client.subscribers("factory/line-1/temp"));  // "dash, alarm"
Serial.println(client.describe("factory/line-1/temp"));     // full metadata line
Serial.println(client.listTopics("factory/"));             // comma-separated topics
Ready-to-flash examples ship in sdk/arduino/HYQP/examples/: publisher, subscriber, cpu_temp_sensor (real ESP32 sensor + OTA-style RETAIN), regex_filters, full_scenario and governance_multiformat (v2.0).

IoT Wiring

From a sensor pin to a governed topic — on ESP32 and on Raspberry Pi.

ESP32 + DHT22 (temperature/humidity)

DHT22 pinESP32 pin
VCC3V3
DATAGPIO 4 (10kΩ pull-up to 3V3)
GNDGND
// read sensor, then publish a governed JSON reading
float t = dht.readTemperature();
char buf[48]; snprintf(buf,sizeof(buf), "{\"value\":%.1f,\"unit\":\"celsius\"}", t);
client.publish("factory/line-1/temp", buf, 1, false);   // QoS 1
A ready-to-flash example ships in sdk/arduino/HYQP/examples/cpu_temp_sensor (ESP32 internal temperature sensor, auto-reconnect, QoS 1 + RETAIN).

Raspberry Pi gateway

  1. Flash Raspberry Pi OS and enable SSH.
  2. Copy the SDK: scp -r sdk/python pi@raspberrypi.local:~/hyqp-sdk
  3. SSH in and run a subscriber that bridges to a dashboard:
    PYTHONPATH=~/hyqp-sdk python3 sub_example.py
  4. Auto-start at boot with a systemd unit (see the Python SDK guide PDF for the unit file).
The broker itself can also run on the Pi: docker run -d -p 4444:4444 78549/hyqp:v2.0 — it fits in ~80 MB resident.

Building Complete Scenarios

Wire publishers and subscribers into an end-to-end flow. Example: a smart-factory line.

1 · Owner creates governed topics

from hyqp import Publisher
ops = Publisher(client_id="plant-ops"); ops.connect()
for n in range(1,4):
    ops.create_topic(f"factory/line-{n}/temp", schema={
        "type":"object","required":["celsius"],
        "properties":{"celsius":{"type":"number","minimum":-50,"maximum":200}}})

2 · Dashboard subscribes to all lines, only hot readings

dash = Subscriber(client_id="dashboard", on_message=on_hot); dash.connect()
dash.subscribe("factory/regex('line-[0-9]+')/temp", filter={"celsius":{"$gt":50}})

3 · Lines publish; one command reaches all of them

line1.publish("factory/line-1/temp", {"celsius":75})         # -> dashboard
ops.publish("factory/regex('line-[0-9]+')/control", {"cmd":"recalibrate"})

4 · Operator audits the topic graph at runtime

ops.subscribers("factory/line-1/temp")   # who is listening?
ops.describe("factory/line-1/temp")      # owner, schema, content type, counts
A complete, runnable version of this scenario ships in sdk/python/examples/flagship_smart_factory.py — it exercises all ten HYQP feature groups against a single broker.

Docker

The broker is published on Docker Hub as 78549/hyqp.

Pull & run

docker pull 78549/hyqp:v2.0
docker run -d --name hyqp -p 4444:4444 -v hyqp-data:/app/data 78549/hyqp:v2.0

docker-compose (broker + MQTT/AMQP for benchmarks)

docker compose up -d        # starts hyqp + mosquitto + rabbitmq
What ships in the image: only the broker (the server). Publishers and subscribers are your apps, built with the Python or Arduino/ESP32 SDK; they connect over TCP on port 4444.

Image facts

Download & Resources

Docker image

docker pull 78549/hyqp:v2.0

Docker Hub ↗

Python SDK

sdk/python — Publisher, Subscriber, discovery & multi-format helpers.

Arduino/ESP32 SDK

sdk/arduino/HYQP — library + examples (DHT, OTA, regex filters).

Guides (PDF)

Protocol Specification, Python & ESP32 SDK guides, Reproducibility guide — in docs/.

At a glance

PropertyValue
LanguageJava 21 (virtual threads), zero runtime dependencies
TransportTCP, UTF-8 text, newline-delimited (default port 4444)
QoS0 (at most once), 1 (at least once), 2 (exactly once)
Governance v2.0ownership, OWNER/PUBLISHERS/SUBSCRIBERS/DESCRIBE/LIST
Payloads v2.0json, text, xml, octet, pdf (binary base64-framed)
Licenseopen source (see repository)