Stow Protocol
Overview
The Stow Protocol allows external clients to discover and interact with items in the Stow.
Gantry provides an implementation of the Stow Protocol where the firmware acts as a server containing a Stow. External clients can use the Stow protocol to interact with the firmware’s Stow. A Python client implementation is provided as an example available under python/stow-client.
Key Features
Notably, the Stow Protocol implementation supports:
Set, Get, & Subscribe to Stow items
Retrieving a full description of the Stow (so that client need not be aware of the
stow.yamlin use by the firmware)Multiple concurrent external client sessions
Set and get item values in bulk
Role-based access controls at the item level
Easy integration with arbitrary transport mediums and protocols
Use With Other Protocols
Stow Protocol messages are encoded with CBOR. The Stow Protocol uses Zephyr network buffers with user-defined head/tail room. This allows the Stow Protocol to be easily embedded into any arbitrary high-level protocols.
Messages
The Stow Protocol mostly follows a request-response pattern. However, some messages may arrive asynchronously (e.g. Update messages).
Summary:
Code |
Name |
Description |
|---|---|---|
|
Version |
Request the protocol version. |
|
Version Response |
Returns the current protocol version (currently |
|
Describe |
Request a chunk of the Stow description starting at a given item ID. |
|
Describe Response |
A chunk of the CBOR-encoded item description with continuation info. |
|
Get |
Get the current value of an item by ID. |
|
Get Response |
Returns the item ID and its current value. |
|
Set |
Set the value of an item by ID. |
|
Multi-Get |
Get the values of multiple items in a single request. |
|
Multi-Get Response |
Returns a flat sequence of ID/value pairs for all requested items. |
|
Multi-Set |
Set the values of multiple items in a single request. |
|
Subscribe |
Subscribe to value change notifications for an item. |
|
Unsubscribe |
Unsubscribe from notifications for an item. |
|
Update |
Sent to the client when a subscribed item’s value changes. |
|
OK |
Success response for operations without a dedicated response. |
|
Error |
Failure response with a numeric error code. |
The message examples are shown using JSON array notation. All messages are CBOR-encoded in practice; use cbor.me to view the CBOR representation. Variable values are represented using fully capitalized snake case.
Version
Request the protocol version. The client should send this to ensure a compatible protocol version prior to further communications.
[0]
Version Response
Sent in response to the Version request. It returns the protocol version (currently 1).
[1, VERSION]
Describe
Request a chunk of the Stow description starting at START_ITEM_ID. Pass 0 to start from the beginning. Use the NEXT_ITEM_ID from each Describe Response as the START_ITEM_ID for the following request. The description is complete when HAS_MORE is false.
[2, START_ITEM_ID]
Describe Response
Sent in response to a Describe request. Each response contains a byte string with a fragment of a larger CBOR-encoded description list. Reassemble all chunks in order before decoding. HAS_MORE indicates whether additional chunks remain.
The maximum size of the CBOR chunk is determined by the STOW_PROTOCOL_DESCRIBE_CHUNK_SIZE Kconfig option.
[3, NEXT_ITEM_ID, HAS_MORE, CBOR_CHUNK]
Get
Request the current value of an item. Will return Get Response or Error.
[4, ITEM_ID]
Get Response
Sent in response to a successful Get request. Contains the current value of the associated item ID.
[5, ITEM_ID, VALUE]
Set
Set the value of an item. Returns OK on success or Error on failure.
[6, ITEM_ID, VALUE]
Multi-Get
Get the current values of multiple items in a single request. Returns a Multi-Get Response on success, or an Error if any item could not be retrieved.
The maximum number of items in a single request is determined by the STOW_PROTOCOL_MULTI_MAX_ITEMS Kconfig option.
[7, FIRST_ITEM_ID, SECOND_ITEM_ID, ...]
Multi-Get Response
Sent in response to a valid Multi-Get request. The response is a flat sequence of alternating ID/value pairs.
[8, FIRST_ITEM_ID, FIRST_VALUE, SECOND_ITEM_ID, SECOND_VALUE, ...]
Multi-Set
Set multiple items in one request. Returns OK if all items were set successfully. On the first failure an Error is returned; subsequent items are not set. The client can read the Stow to determine which items were applied.
The maximum number of items in a single request is determined by the STOW_PROTOCOL_MULTI_MAX_ITEMS Kconfig option.
[9, FIRST_ITEM_ID, FIRST_VALUE, SECOND_ITEM_ID, SECOND_VALUE, ...]
Subscribe
Subscribe to receive Update messages when the associated item is set.Returns OK or Error.
[10, ITEM_ID]
Unsubscribe
Unsubscribe to stop receiving Update messages for the associated item. Returns OK or Error.
[11, ITEM_ID]
Update
Sent to the client when a subscribed item’s value is set.
[12, ITEM_ID, VALUE]
OK
Sent in response to successful operations that do no have a dedicated response.
[13]
Error
Sent in response to failed operations. See Error Codes for possible errors.
[14, ERROR_CODE]
Error Codes
Code |
Name |
Description |
|---|---|---|
|
|
The message was malformed. |
|
|
The message code was not recognized. |
|
|
The item ID was invalid. |
|
|
Not enough memory to handle the message. |
|
|
The client’s role(s) were insufficient. |
|
|
An unknown error occurred (should never occur). |
Client Interaction
Connecting to the Stow
External clients should verify the protocol version then check the Stow hash to see if they have the Stow description cached. The Stow hash is always the first item (with an ID of 0). The Python Stow client provides and example of caching descriptions.
%%{init: {"theme": "neutral"}}%%
sequenceDiagram
participant C as Client
participant S as Stow Server
C->>S: Version [0]
S->>C: Version Response [1, 1]
C->>C: Verify the version is 1
C->>S: Get [4, 0]
S->>C: Get Response [5, <hash>]
C->>C: Check if the Stow is cached based on the hash
If the description is not yet cached, the client should retrieve the Stow description.
Describing the Stow
The client pages through the Stow description by repeatedly sending Describe with the NEXT_ITEM_ID returned by each response. The loop ends when HAS_MORE is false.
%%{init: {"theme": "neutral"}}%%
sequenceDiagram
participant C as Client
participant S as Stow Server
C->>S: Describe [2, 0]
S->>C: Describe Response [3, next_id, true, chunk_1]
C->>S: Describe [2, next_id]
S->>C: Describe Response [3, _, false, chunk_N]
Note over C: Concatenate chunks and decode the CBOR list
Getting and Setting Items
%%{init: {"theme": "neutral"}}%%
sequenceDiagram
participant C as Client
participant S as Stow Server
C->>S: Get [4, item_id]
S->>C: Get Response [5, item_id, value]
C->>S: Set [6, item_id, new_value]
S->>C: OK [13]
C->>S: Multi-Get [7, id_1, id_2]
S->>C: Multi-Get Response [8, id_1, value_1, id_2, value_2]
C->>S: Multi-Set [9, id_1, value_1, id_2, value_2]
S->>C: OK [13]
Subscribing and Receiving Updates
Once subscribed, the server pushes Update messages to the client whenever the item’s value changes. The client must unsubscribe explicitly.
%%{init: {"theme": "neutral"}}%%
sequenceDiagram
participant C as Client
participant S as Stow Server
C->>S: Subscribe [10, item_id]
S->>C: OK [13]
Note over S: Item value changes
S-->>C: Update [12, item_id, new_value]
C->>S: Unsubscribe [11, item_id]
S->>C: OK [13]
Note over S: Item value changes again, no update sent
Stow Description
Once all Describe Response chunks are concatenated and decoded, the result is a CBOR list of item descriptions.
Description Format
{
"id": ITEM_ID,
"name": "item_name",
"categories": ["category", ...],
"storage": "Ephemeral" | "Persistent" | "TOFU",
"read_perm": ["RoleName", ...],
"write_perm": ["RoleName", ...],
"type": "Enum" | "Int" | "Float" | "String" | "Byte Array" | "Buffer" | "Struct",
"default": DEFAULT_VALUE,
"constraints": TYPE_SPECIFIC_CONSTRAINTS
}
The id corresponds to the ITEM_ID used in Get, Set, Subscribe, and Update messages. IDs may change across firmware versions; use the name field as the stable, human-readable identifier.
An empty read_perm or write_perm array means the item is firmware-only and cannot be accessed by any external client. Otherwise, a client whose session includes any of the listed role names is granted access.
Constraints by Type
Enum
A list of possible values paired with descriptive names.
[
{"value": 0, "name": "DISCONNECTED"},
{"value": 1, "name": "CONNECTED"}
]
Int / Float
Minimum and maximum bounds encoded as int32 or float32 respectively.
{"min": -100, "max": 100}
String / Byte Array / Buffer
Minimum and maximum length of the buffer.
{"min_len": 0, "max_len": 64}
Struct
A list of field descriptors. Each field has its own name, type, and constraints. Fields can be nested structs.
[
{"name": "CS", "type": "Enum", "constraints": [{"value": 0, "name": "OFF"}, {"value": 1, "name": "ON"}]},
{"name": "Bytes", "type": "Byte Array", "constraints": {"min_len": 0, "max_len": 6}}
]