## Cluster Broadcast (CLUSTER_EVENT) ### Overview Spore supports cluster-wide event broadcasting via UDP. Services publish a local event, and the core broadcasts it to peers as a `CLUSTER_EVENT`. Peers receive the event and forward it to local subscribers through the internal event bus. - **Local trigger**: `ctx.fire("cluster/broadcast", eventJson)` - **UDP message**: `CLUSTER_EVENT:{json}` - **Receiver action**: Parses `{json}` and calls `ctx.fire(event, data)` This centralizes network broadcast in core, so services never touch UDP directly. ### Message format - UDP payload prefix: `CLUSTER_EVENT:` - JSON body: ```json { "event": "", "data": "" // or an inline JSON object/array } ``` Notes: - The receiver accepts `data` as either a JSON string or a nested JSON object/array. Nested JSON is serialized back to a string before firing the local event. - Keep payloads small (UDP, default buffer 512 bytes). ### Core responsibilities - `ClusterManager` registers a centralized handler: - Subscribes to `cluster/broadcast` to send the provided event JSON over UDP broadcast. - Listens for incoming UDP `CLUSTER_EVENT` messages and forwards them to local subscribers via `ctx.fire(event, data)`. - Broadcast target uses subnet-directed broadcast (e.g., `192.168.1.255`) for better reliability. Both nodes must share the same `udp_port`. ### Service responsibilities Services send and receive events using the local event bus. 1) Subscribe to an event name and apply state from `data`: ```cpp ctx.on("api/neopattern", [this](void* dataPtr) { String* jsonStr = static_cast(dataPtr); if (!jsonStr) return; JsonDocument doc; if (deserializeJson(doc, *jsonStr)) return; JsonObject obj = doc.as(); // Parse and apply fields from obj }); ``` 2) Build a control payload and update locally via the same event: ```cpp JsonDocument payload; payload["pattern"] = "rainbow_cycle"; // example payload["brightness"] = 100; String payloadStr; serializeJson(payload, payloadStr); ctx.fire("api/neopattern", &payloadStr); ``; 3) Broadcast to peers by delegating to core: ```cpp JsonDocument envelope; envelope["event"] = "api/neopattern"; envelope["data"] = payloadStr; // JSON string String eventJson; serializeJson(envelope, eventJson); ctx.fire("cluster/broadcast", &eventJson); ``` With this flow, services have a single codepath for applying state (the event handler). Broadcasting simply reuses the same payload. ### Logging - Core logs source IP, payload length, and event name for received `CLUSTER_EVENT`s. - Services can log when submitting `cluster/broadcast` and when applying control events. ### Networking considerations - Ensure all nodes: - Listen on the same `udp_port`. - Are in the same subnet (for subnet-directed broadcast). - Some networks may block global broadcast (`255.255.255.255`). Subnet-directed broadcast is used by default. ### Troubleshooting - If peers do not react: - Confirm logs show `CLUSTER_EVENT raw from ` on the receiver. - Verify UDP port alignment and WiFi connection/subnet. - Check payload size (<512 bytes by default) and JSON validity. - Ensure the service subscribed to the correct `event` name and handles `data`.