Single source of truth for the LSH wire protocol.
This repository contains the compact protocol specification shared by:
lsh-corelsh-bridgenode-red-contrib-lsh-logic
It is responsible for keeping the protocol contract explicit, versioned, and reproducible across firmware and application repositories.
If you want to implement the protocol from scratch, read the docs in this order:
- shared/lsh_protocol.md for the generated wire contract
- docs/profiles-and-roles.md for the role model, hop-local semantics and implementation guidance
shared/lsh_protocol.jsonCompact protocol spec and metadata.shared/lsh_protocol_golden_payloads.jsonGolden examples used to validate generated artifacts and codec behavior.shared/lsh_protocol.mdHuman-readable reference generated from the shared spec.docs/profiles-and-roles.mdHand-written semantic guide for implementers and transport/profile mapping.tools/generate_lsh_protocol.pyGenerator for shared protocol artifacts consumed by the LSH repositories.
This repo is the source of truth for:
- wire command IDs
- compact JSON keys
- click type IDs
- protocol compatibility metadata
- golden payload examples
- generated protocol documentation
- role-neutral semantic guidance for implementers
This repo is not responsible for:
- firmware logic
- repository-specific bridge behavior
- Node-RED business logic
- hardware configuration
- compact on-wire representation
- deterministic generated artifacts
- explicit compatibility rules
- simple maintenance across separate git repositories
- no ambiguity about protocol ownership
The LSH protocol assumes a trusted environment and a cooperative broker. It does not embed authentication, integrity, or confidentiality mechanisms in the payload format itself. That constraint is intentional and must stay documented in all consumer repositories.
The compatibility agreement is intentionally minimal:
BOOTis only a re-sync trigger. It does not carry version metadata.- runtime wire compatibility is checked when the controller later sends
DEVICE_DETAILS DEVICE_DETAILS.vmust match the locally compiledwireProtocolMajorspecRevisionis repository/generation metadata only; it is not negotiated on wire
Runtime decision rule:
- if
DEVICE_DETAILS.vmatches, the peers may continue the handshake - if
DEVICE_DETAILS.vdoes not match, the consumer must reject the handshake payload
Keep these terms distinct:
wireProtocolMajordecides whether two peers may speak to each other at runtimespecRevisiontracks source-of-truth evolution for generated code and documentation
The LSH logical payloads are transport-agnostic. Transport behavior is documented here because it affects interoperability between repositories:
- JSON over serial: newline-delimited
- MsgPack over serial: raw MsgPack payload
- MQTT JSON: raw JSON payload
- MQTT MsgPack: raw MsgPack payload
What is intentionally not part of the base transport model:
- how many hops a deployment has
- whether a bridge exists at all
- whether a command is forwarded transparently or interpreted locally
- how a specific implementation projects LSH state into Homie, Home Assistant, or other higher-level systems
Those choices belong to transport or implementation profiles, not to the base wire contract.
Keep these layers distinct when reading or implementing the protocol:
- Wire contract: command IDs, keys, payload shapes, compatibility checks
- Role semantics: what a command means between immediate peers, regardless of product names
- Profiles: how a concrete stack applies the protocol on serial, MQTT service topics, gateways, or direct networked controllers
- Reference implementations: how
lsh-core,lsh-bridge, andnode-red-contrib-lsh-logicchoose to realize those profiles
The autogenerated spec covers the wire contract. The hand-written guide in docs/profiles-and-roles.md covers the semantic layer that must remain understandable for third-party implementations.
- edit the shared spec or golden payloads
- run the generator for the targets you want to update
- commit the updated generated artifacts
- propagate changes to consumer repositories
Each consumer repository vendors this repo at vendor/lsh-protocol via git subtree.
Initial add inside a consumer repository:
git remote add lsh-protocol git@github.com:labodj/lsh-protocol.git || git remote set-url lsh-protocol git@github.com:labodj/lsh-protocol.git
git fetch lsh-protocol
git subtree add --prefix=vendor/lsh-protocol lsh-protocol main --squashSubsequent updates inside a consumer repository:
git remote add lsh-protocol git@github.com:labodj/lsh-protocol.git || git remote set-url lsh-protocol git@github.com:labodj/lsh-protocol.git
git fetch lsh-protocol
git subtree pull --prefix=vendor/lsh-protocol lsh-protocol main --squashAfter updating the vendored copy, regenerate or verify the target-specific outputs from the consumer itself:
python3 tools/update_lsh_protocol.py
python3 tools/update_lsh_protocol.py --checkThe consumer wrappers now default to the vendored subtree only. Local sibling repos are no longer auto-discovered. Use --protocol-root or LSH_PROTOCOL_ROOT only for explicit manual overrides.
Generate only the human-readable reference inside this repository:
python3 tools/generate_lsh_protocol.pyCheck only:
python3 tools/generate_lsh_protocol.py --checkGenerate outputs for consumer repositories explicitly:
python3 tools/generate_lsh_protocol.py \
--target shared-doc \
--target core \
--target bridge \
--target node-red \
--core-root /path/to/lsh-core \
--bridge-root /path/to/lsh-bridge \
--node-red-root /path/to/node-red-contrib-lsh-logicThis repository is intentionally standalone:
- it does not assume a monorepo layout
- consumer outputs are emitted only when their target roots are passed explicitly
shared-docis the default target when no--targetis provided
Typical maintainer flow from this repository:
python3 tools/generate_lsh_protocol.py
python3 tools/generate_lsh_protocol.py --check
python3 tools/generate_lsh_protocol.py \
--target shared-doc \
--target core \
--target bridge \
--target node-red \
--core-root /path/to/lsh-core \
--bridge-root /path/to/lsh-bridge \
--node-red-root /path/to/node-red-contrib-lsh-logicKeep these concepts distinct:
wireProtocolMajor: runtime wire compatibility onlyspecRevision: source-of-truth revision for generated code and docs- git tags: repository release milestones