SmarterLink Protocol Specification
Status: Released (protocol version 1.0)
Library version: 1.0.1
1. Overview
SmarterLink is a publish/subscribe messaging protocol for distributed communication in industrial manufacturing environments. It defines how participants (devices and software systems with distinct roles) exchange events and data over MQTT.
SmarterLink deliberately avoids MQTT 5 features to ensure compatibility with a wide range of brokers.
2. Participants
2.1 Identity
Every SmarterLink participant has a Participant ID: a plain string that must be unique across the entire SmarterLink network (all sites). The Participant ID appears in MQTT topic paths and must not contain MQTT reserved characters (/, +, #, $).
2.2 Roles
Every participant is assigned a Role that describes its function in the system. Four canonical roles are defined:
| Role | Topic Segment | Description |
|---|---|---|
| Data Producer | producers |
Acquires and publishes production data (images, measurements, etc.) |
| Data Processor | processors |
Consumes and processes data from producers, publishes results |
| Data Store | store |
Persists data and serves files to other participants |
| Human-Machine Interface | hmi |
Displays information and interacts with operators |
Custom roles are permitted but carry no standard event definitions.
3. Transport
3.1 MQTT
SmarterLink is designed to work with MQTT 3.1.1. The protocol deliberately avoids MQTT 5 features to ensure broad broker compatibility.
3.2 Topic Hierarchy
SmarterLink topics follow the Universal Namespace (UNS) convention:
smarter-link/<site>/<area>/<sub-area>/<station>/<role>/<participant-id>/<event-name>
| Level | Name | Description |
|---|---|---|
| 1 | smarter-link |
Fixed protocol root identifier |
| 2 | site | Site or facility identifier |
| 3 | area | Area within the site |
| 4 | sub-area | Sub-area within the area |
| 5 | station | Station |
| 6 | role | Role segment (see §2.2), or _ if not applicable |
| 7 | participant-id | Publishing participant's ID prefixed with ~ (e.g. ~cam-01), or _ if not applicable |
| 8 | event-name | Event name in kebab-case |
When a level is not applicable to a particular event it is set to _ rather than omitted. All topics therefore always have exactly 8 levels, which makes subscription filter construction predictable.
Example:
smarter-link/site-x/area-1/sub-1/station-a/producers/~cam-01/component-image-acquired
3.3 Segment Constraints
Topic segments must not contain /, +, #, or $. Participant IDs must be unique across the entire network, not just within a single station.
Two characters are reserved as segment-type indicators:
| Character | Reserved for | Example |
|---|---|---|
~ |
Participant ID segments | ~cam-01 |
: |
File descriptor segments | fs:00, fr:99 |
Other segments (site, area, sub-area, station, role, event name) should avoid these characters.
4. Message Format
All SmarterLink messages, except raw file transfer chunks, are JSON objects.
4.1 Envelope
Every message has two required top-level fields:
{
"header": { ... },
"payload": { ... }
}
4.2 Header
| Field | Type | Required | Description |
|---|---|---|---|
payloadType |
string | yes | Simple type name of the payload (e.g. "ActiveJobUpdated"). Used by receivers to select the correct deserialization type. |
monotonicCounter |
integer (int64) | no | A value that increases with each successive message from a given sender. May be a UTC timestamp (e.g. .NET ticks since Unix epoch) or a simple counter. Used to determine relative message order. |
protocolRevision |
integer | yes | Integer revision number of the SmarterLink protocol. Each stable protocol release maps to a specific revision number (see §9.1). Current value: 0. |
userData |
string | no | Optional application-defined string. Passed through the protocol without interpretation. |
4.3 Payload
Every payload carries a version field for independent schema evolution. All remaining fields are event-specific.
| Field | Type | Required | Description |
|---|---|---|---|
version |
integer | yes | Schema version of this payload type. |
4.4 Serialization
- All JSON property names are camelCase.
JobId,ComponentId, andParticipantIdserialize as plain strings.Locationserializes as a URI string.HashingFunctionserializes as a string identifier (e.g.SHA-256). Supported values are defined in the SmarterLink File Transfer Protocol.- Required fields must always be present. Absent optional fields are treated as null by receivers.
4.5 Example
{
"header": {
"payloadType": "ActiveJobUpdated",
"monotonicCounter": 638765432100000000,
"protocolRevision": 0,
"userData": null
},
"payload": {
"version": 1,
"jobId": "job-2024-001"
}
}
5. Core Data Types
5.1 Identifiers
| Type | Wire Format | Constraints |
|---|---|---|
ParticipantId |
string | Must be unique network-wide; no /, +, #, $ |
JobId |
string | Can be used to correlate SmarterLink payloads with external job management systems. |
ComponentId |
string | Can be used to correlate SmarterLink payloads with external Component/Part/CAD management systems. |
5.2 Location
A Location is a URI string that references a resource accessible by SmarterLink Participants.
All URI schemes are permitted, however SmarterLink leaves it the the Participants to work out how to use the URIs.
5.2.1 SmarterLink File Transfer Locations
SmarterLink provides an optional protocol for File Transfers overt MQTT.
File locations in the protocol use the smarterlink:// scheme (see §8.2).
5.3 JobConfig
A JobConfig is a free-form JSON object ({ "key": value, ... }) carrying job-specific parameters. Its schema is entirely application-defined. It is typically published out-of-band (e.g. via an application-defined custom payload or a side-channel) rather than as a standard SmarterLink event.
6. Standard Events
6.1 Job Events
Job events are scoped to a station, not to a specific participant. Both the role and participant-id levels are _.
Topic pattern: smarter-link/<site>/<area>/<sub-area>/<station>/_/_/<event-name>
activeJobUpdated
Signals that the active job for a station has changed.
| Field | Type | Required |
|---|---|---|
version |
integer | yes |
jobId |
JobId | yes |
jobFileAvailable
Signals that a file associated with a job (e.g. a recipe or configuration) is available for download.
| Field | Type | Required |
|---|---|---|
version |
integer | yes |
jobId |
JobId | yes |
fileId |
string | yes |
location |
Location | yes |
6.2 Data Producer Events
Published by Data Producer participants. The participant-id level identifies the specific producer.
Topic pattern: smarter-link/<site>/<area>/<sub-area>/<station>/producers/~<participant-id>/<event-name>
componentImageAcquired
Signals that an image of a component has been acquired and is available for download.
| Field | Type | Required |
|---|---|---|
version |
integer | yes |
componentId |
ComponentId | yes |
fileId |
string | yes |
location |
Location | yes |
jobId |
JobId | yes |
userInformationAcquired
Carries operator-entered or operator-associated information for a component.
| Field | Type | Required |
|---|---|---|
version |
integer | yes |
componentId |
ComponentId | yes |
fileId |
string | yes |
location |
Location | yes |
jobId |
JobId | yes |
information |
Mapping (string → string) | yes |
inspectionSystemStatusAcquired
Reports status information from a Data Producer participant.
| Field | Type | Required |
|---|---|---|
version |
integer | yes |
participantId |
ParticipantId | yes |
information |
Mapping (string → string) | yes |
6.3 Data Processor Events
Published by Data Processor participants.
Topic pattern: smarter-link/<site>/<area>/<sub-area>/<station>/processors/~<participant-id>/<event-name>
componentAnalysisUpdated
Signals that analysis results for a component are available.
| Field | Type | Required |
|---|---|---|
version |
integer | yes |
componentId |
ComponentId | yes |
fileId |
string | yes |
location |
Location | yes |
jobId |
JobId | no |
componentReportUpdated
Signals that a consolidated report for a component has been updated.
| Field | Type | Required |
|---|---|---|
version |
integer | yes |
componentId |
ComponentId | yes |
fileId |
string | yes |
location |
Location | yes |
jobId |
JobId | no |
7. Subscriptions
Participants subscribe using MQTT topic filters. Standard MQTT wildcard characters apply:
+: matches exactly one topic level#: matches the remainder of the topic from that level onward (must be last)
Common subscription patterns:
| Intent | Filter |
|---|---|
| All job events at a station | smarter-link/<site>/<area>/<sub-area>/<station>/_/_/+ |
| Specific job event | smarter-link/<site>/<area>/<sub-area>/<station>/_/_/<event-name> |
| Specific Data Producer event from any producer | smarter-link/<site>/<area>/<sub-area>/<station>/producers/+/<event-name> |
| All events from a specific producer | smarter-link/<site>/<area>/<sub-area>/<station>/producers/~<participant-id>/+ |
| All events from all producers | smarter-link/<site>/<area>/<sub-area>/<station>/producers/# |
| All events from all processors | smarter-link/<site>/<area>/<sub-area>/<station>/processors/# |
| All events at a station | smarter-link/<site>/<area>/<sub-area>/<station>/# |
| All SmarterLink events site-wide | smarter-link/<site>/# |
8. Custom Payloads
Custom payloads are a deliberate escape hatch for use cases that do not fit the standard typed event model (for example, integrating with a third-party system, sending application-specific configuration, or establishing application-defined side-channels). Publishers should follow standard SmarterLink conventions (topic structure, envelope format) as closely as possible; the flexibility exists for cases where strict conformance is not achievable.
8.1 Envelope
A custom payload message uses the standard SmarterLink JSON envelope (§4.1). The header is identical to any other message. The payloadType field carries an application-defined string (e.g. "JobConfig") rather than a standard event type name.
{
"header": {
"payloadType": "JobConfig",
"monotonicCounter": 638765432100000000,
"protocolRevision": 0,
"userData": null
},
"payload": { }
}
The payload value is an arbitrary JSON object. Its schema is entirely application-defined.
8.2 Topic
Publishers should use the standard SmarterLink topic hierarchy (§3.2) wherever possible. Deviation is permitted when the standard structure cannot reasonably be applied, for example when the publisher has no meaningful role or participant identity in the SmarterLink sense.
8.3 Receiver Behaviour
Receivers identify custom payloads by the payloadType value in the header. If a receiver does not recognize the payloadType, it should ignore the message.
9. File Transfers
9.1 Using Data Store Intermediate (Preferred Approach)
The preferred way to share files between participants is through a Data Store participant. A sender uploads the file to the Data Store (via any suitable side-channel, e.g. HTTP) and then publishes a SmarterLink event carrying a Location URI that points to it. Receivers can fetch the file independently, at their own pace, without any coordination with the sender. This promotes decoupled, asynchronous interactions and avoids holding MQTT resources open for the duration of the transfer.
The MQTT file transfer protocol described in the remainder of this section should only be used when:
- no Data Store participant is available in the deployment, or
- the use case genuinely requires a synchronous, point-to-point transfer over MQTT.
9.2 File Transfer Over MQTT
SmarterLink includes a built-in file transfer mechanism for exchanging binary files (images, reports, recipes, etc.) directly over MQTT. It is loosely adapted from the AWS IoT Core file delivery approach.
All file transfer negotiation messages use the standard SmarterLink JSON envelope. Raw file chunks are the sole exception: they are published as binary with no envelope.
The full specification for this protocol is defined in SmarterLink File Transfer Protocol.
10. Versioning
10.1 Protocol Revision
The protocolRevision field in the message header is an integer that identifies the revision of the entire SmarterLink protocol, not just the envelope format. Integer revision numbers are used in messages to keep parsing simple and implementation-agnostic.
Stable protocol releases are identified by a human-readable version number (e.g. 1.2). Each stable release formally declares which protocolRevision integer it corresponds to, allowing implementations to map between the two. Pre-release revisions carry no such mapping guarantee.
| Stable Release Version | Protocol Revision |
|---|---|
| 1.0 | 0 |
10.2 Payload Schema Versions
Each payload type carries its own version field for independent schema evolution.