Multi-Protocol Communication
The make87 platform enables direct communication between applications running on different nodes through a protocol-agnostic messaging system. Applications can communicate using any protocol or middleware by declaring their communication patterns in the MAKE87.yml
manifest.
Core Communication Concepts
make87 organizes all communication into six fundamental patterns that map to common messaging paradigms across different protocols:
Pattern | Direction | Use Case | Protocol Examples |
---|---|---|---|
Publisher | One-to-many | Broadcasting data streams | Zenoh pub, MQTT publish, DDS writer |
Subscriber | Many-to-one1 | Receiving data streams | Zenoh sub, MQTT subscribe, DDS reader |
Requester | Request-reply | Making service calls, querying data | Zenoh query, HTTP client, gRPC client |
Provider | Reply-request | Responding to queries, offering services | Zenoh queryable, HTTP endpoint, gRPC service |
Server | Persistent | Long-running services (HTTP, streaming) | HTTP server, RTSP server, WebSocket server |
Client | Connection | Connecting to persistent services | HTTP client, RTSP client, WebSocket client |
Message Specifications
The spec
field in each communication pattern is a string that serves as a soft contract to check compatibility between applications. This allows make87 to verify that two applications can communicate before deployment.
Message Spec Examples
make87 Message Pack (Protocol Buffers):
Custom Message Types:
API Specifications (for Server/Client):
spec: "frigate_api:15" # API name and version
spec: "openapi:my_service:v2" # OpenAPI spec reference
spec: "rtsp" # Protocol placeholder
The spec is flexible - anything is possible as long as both sides agree on it. For server/client APIs, it's recommended to reference an API specification like OpenAPI schemas or include the API name and version.
Message Encoding
The optional encoding
field specifies how messages are serialized over the wire. This is particularly useful when the same message type can be encoded in multiple formats:
Protocol Buffer Messages:
publishers:
- name: sensor_data
spec: "make87_messages.sensor.Temperature"
encoding: "proto" # Binary Protocol Buffers (default)
- name: sensor_json
spec: "make87_messages.sensor.Temperature"
encoding: "json" # JSON representation of the same message
If not specified, the encoding is assumed to default to the protocol's standard format.
Protocol Flexibility
Unlike traditional platforms that lock you into a specific middleware, make87 supports any protocol by abstracting the communication patterns. You specify:
- Interface definition - What communication patterns your application uses
- Protocol selection - Which protocol to use (Zenoh, HTTP, MQTT, etc.)
- Message types - What data structures are exchanged
- Configuration - Protocol-specific settings
The platform handles connection establishment, service discovery, and routing while your application code remains protocol-agnostic.
Protocol Field as Soft Contract
The protocol
field in each interface is a string that serves as a soft contract for matching applications that can communicate with each other. Like the spec
field, this is completely flexible and up to the developer.
Standard Protocol Names:
interfaces:
- name: sensor_data
protocol: zenoh # Standard Zenoh protocol
- name: web_api
protocol: http # Standard HTTP protocol
- name: iot_telemetry
protocol: mqtt # Standard MQTT protocol
Custom Protocol Names:
interfaces:
- name: robot_control
protocol: my-custom-protocol # Your own protocol implementation
- name: legacy_system
protocol: company-internal-v2 # Internal protocol version
- name: experimental
protocol: research-protocol-alpha # Experimental implementation
Protocol Matching: make87 uses the protocol field to determine which applications can communicate. Two applications can only exchange data if their interfaces use the same protocol string. This allows you to:
- Use standard protocols like
zenoh
,http
,mqtt
,dds
- Implement custom protocols with any name you choose
- Version your protocols (e.g.,
my-protocol-v1
,my-protocol-v2
) - Isolate different communication channels by protocol name
The protocol string is purely for matching - make87 doesn't validate or enforce any specific protocol implementation. Your application code is responsible for implementing the actual protocol logic.
Supported Protocols
make87 works with any protocol through its abstraction layer. Common protocols include:
- DDS / ROS2: Real-time pub/sub for robotics and industrial automation
- MQTT: Lightweight messaging for IoT and telemetry
- HTTP / REST: Standard web APIs and control interfaces
- WebSocket (WS): Low-latency bidirectional streaming
- Zenoh: High-performance pub/sub for edge computing
- MCP (Model Context Protocol): For AI model communication
- RTSP: Video streaming protocol
- gRPC: High-performance RPC framework
- Custom protocols: Use any transport stack compatible with your codebase
Publisher / Subscriber Pattern
Publisher/Subscriber is ideal for streaming data and event-driven communication where data flows from one or more sources to multiple consumers.
Publisher/Subscriber patterns typically work on topics plus IP/port combinations, where the topic name identifies the data stream and the underlying protocol handles the networking.
When to Use
- Streaming sensor data (camera feeds, telemetry, logs)
- Broadcasting events or notifications
- Real-time data distribution
- One-to-many communication
Example: Image Stream
A camera application publishes image data, while multiple AI applications subscribe to process different aspects:
Camera Publisher:
interfaces:
- name: camera_feed
protocol: zenoh
publishers:
- name: raw_images
spec: "make87_messages.image.compressed.ImagePNG"
config:
type: object
properties:
congestion_control:
type: string
enum: [DROP, BLOCK]
default: DROP
priority:
type: string
enum: [REAL_TIME, INTERACTIVE_HIGH, DATA]
default: REAL_TIME
AI Subscriber:
interfaces:
- name: vision_processing
protocol: zenoh
subscribers:
- name: image_input
spec: "make87_messages.image.compressed.ImagePNG"
config:
type: object
properties:
handler:
type: object
properties:
handler_type:
type: string
enum: [ FIFO, RING ]
default: RING
capacity:
type: integer
minimum: 0
description: "Capacity of the handler. For FIFO, this is the maximum number of messages it can hold. For RING, this is the size of the ring buffer."
default: 10
Requester / Provider Pattern
Requester/Provider enables request-response communication where applications query services and receive responses.
Like Publisher/Subscriber, Requester/Provider patterns work on endpoints plus IP/port combinations, where the endpoint identifies the service interface.
When to Use
- Service calls and RPC-style interactions
- Database queries or data retrieval
- Configuration requests
- Synchronous operations requiring acknowledgment
Example: Object Detection Service
An AI application provides object detection as a service:
Detection Provider:
interfaces:
- name: ai_services
protocol: zenoh
providers:
- name: detect_objects
request_spec: "make87_messages.image.compressed.ImagePNG"
response_spec: "make87_messages.detection.box.Box2DAxisAligned"
Client Requester:
interfaces:
- name: vision_client
protocol: zenoh
requesters:
- name: object_detection
request_spec: "make87_messages.image.compressed.ImagePNG"
response_spec: "make87_messages.detection.box.Box2DAxisAligned"
Server / Client Pattern
Server/Client is designed for persistent connection-based services like web servers, streaming protocols, or long-running TCP connections.
Server/Client patterns mainly work on IP/port combinations, where endpoints are defined by the message specification (such as OpenAPI schemas with multiple HTTP endpoints).
When to Use
- HTTP web services and REST APIs
- Streaming protocols (RTSP, WebSocket)
- Persistent TCP/UDP connections
- File servers or database connections
Example: RTSP Streaming
An RTSP server streams video data, while clients connect to consume the feed:
RTSP Server:
interfaces:
- name: video_stream
protocol: rtsp
servers:
- name: camera_stream
spec: "rtsp"
port:
name: stream
target_port: 554
protocol: rtsp
RTSP Client:
Example: Mixed Protocol System
A complete example showing multiple protocols in one system:
apiVersion: v1
name: fusion-node
interfaces:
- name: lidar_feed
protocol: dds
publishers:
- name: point_cloud
spec: "sensor_msgs.PointCloud2"
config:
type: object
properties:
reliability:
type: string
enum: [reliable, best_effort]
default: best_effort
- name: telemetry_out
protocol: mqtt
publishers:
- name: system_status
spec: "make87_messages.system.Status"
config:
type: object
properties:
qos:
type: integer
enum: [0, 1, 2]
default: 1
- name: config_api
protocol: http
servers:
- name: configuration
spec: "fusion_config_api:v2"
port:
name: config
target_port: 8080
protocol: http
- name: dashboard_feed
protocol: ws
servers:
- name: live_stream
spec: "dashboard_stream:v1"
port:
name: stream
target_port: 9001
protocol: ws
Choosing the Right Pattern
Need | Pattern | Reason |
---|---|---|
Stream sensor data to multiple consumers | Publisher/Subscriber | Efficient one-to-many distribution via topics |
Request data on-demand | Requester/Provider | Synchronous query-response via endpoints |
Serve a web interface | Server/Client | Persistent HTTP connections with multiple API endpoints |
Send commands to actuators | Publisher/Subscriber | Fire-and-forget messaging via topics |
Validate configurations | Requester/Provider | Need confirmation/validation via endpoints |
Stream video feeds | Publisher/Subscriber or Server/Client | Depends on protocol (Zenoh pub/sub topics or RTSP servers) |
Configuration Schema
The optional config
field under each communication pattern contains a YAML-encoded JSON schema that defines protocol-specific configuration parameters. This schema is completely flexible and allows you to configure any aspect of the communication behavior.
Example: Zenoh Publisher Configuration
publishers:
- name: camera_feed
spec: "make87_messages.image.ImageRGB"
config:
type: object
properties:
congestion_control:
type: string
enum: [ DROP, BLOCK ]
default: DROP
priority:
type: string
enum:
- REAL_TIME
- INTERACTIVE_HIGH
- INTERACTIVE_LOW
- DATA_HIGH
- DATA
- DATA_LOW
- BACKGROUND
default: DATA
express:
type: boolean
default: true
reliability:
type: string
enum: [ BEST_EFFORT, RELIABLE ]
default: RELIABLE
Example: MQTT Publisher Configuration
publishers:
- name: device_status
spec: "make87_messages.device.Status"
config:
type: object
properties:
qos:
type: integer
enum: [0, 1, 2]
default: 1
description: "Quality of Service level"
retain:
type: boolean
default: false
description: "Whether messages should be retained by broker"
Example: HTTP Server Configuration
servers:
- name: api_endpoint
spec: "my_api:v1.0"
config:
type: object
properties:
enable_cors:
type: boolean
default: true
timeout_ms:
type: integer
default: 30000
minimum: 1000
rate_limit:
type: object
properties:
requests_per_minute:
type: integer
default: 100
burst_size:
type: integer
default: 10
The configuration schema supports all standard JSON Schema features including: - Type validation (string, integer, boolean, object, array) - Enums for restricted value sets - Default values for automatic parameter initialization - Nested objects for complex configuration structures - Descriptions for documentation - Validation constraints (minimum, maximum, pattern, etc.)
Best Practices
Pattern Selection
- Publisher/Subscriber: Streaming data, events, one-to-many distribution
- Requester/Provider: Service calls, queries, request-response interactions
- Server/Client: Persistent connections, web services, streaming protocols
Message Design
- Use structured message types for type safety and compatibility
- Include timestamps for synchronization across distributed systems
- Design message hierarchies for organized data management
- Consider using make87's message pack for standardized Protocol Buffer messages
Performance Optimization
- Co-locate high-frequency communicating applications on the same node
- Use appropriate QoS settings for your reliability requirements
- Consider message size and frequency for bandwidth optimization
- Choose encoding formats that balance performance and compatibility
Runtime Integration
- Access interface configurations through the
MAKE87_CONFIG
environment variable - For complete details on runtime configuration structure and usage, see Runtime Configuration
By leveraging make87's protocol-agnostic approach, you can build applications that communicate efficiently across any infrastructure while maintaining the flexibility to change protocols as requirements evolve.
-
Note: make87's System designer only allows to draw one-to-one subscribers for now, but many-to-one can be configured by manually setting the topic names to the same value in the UI. This is a limitation of the current System designer, not the platform itself. ↩