The Protocol
Strata's protocol is the contract between services and clients. It defines the shape of messages exchanged over the message queue -- JSON envelopes with a fixed structure. The official implementation is Node.js, but the protocol is language-agnostic. Any process that can push and pop JSON from the backend can participate in a Strata system.
This is the key to interoperability. The protocol is the contract -- anyone can build a service or client in any language that speaks it.
Request Envelope
A request envelope is sent by a client to a service. It contains everything the service needs to route and process the request.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | yes | -- | Unique identifier. Generated with nanoid (20 chars, alphanumeric). |
messageType | string | yes | -- | Always 'request'. |
context | string | yes | -- | The context to route to (e.g. 'users'). |
operation | string | yes | -- | The operation within the context (e.g. 'get'). |
responseQueue | string | yes | -- | Where to send the response. Set by the backend before sending. |
timestamp | string | yes | -- | ISO 8601 datetime (e.g. '2024-06-25T04:08:22.000Z'). |
timeout | number | no | 0 | Milliseconds the client will wait for a response. 0 uses the service default. |
payload | object | yes | {} | Application-specific request data. Opaque to the framework. |
metadata | object | yes | {} | Additional metadata. Opaque to the framework. |
auth | unknown | no | -- | Authentication data (typically a JWT or token). Passed through, never inspected. |
client | string | yes | 'unknown' | Human-readable client identifier (e.g. 'MyApp v2.1.0'). |
requestChain | string[] | no | [] | Chain of request IDs for distributed tracing. |
priorRequest | string | no | -- | ID of the request that triggered this one. |
Response Envelope
A response envelope is sent by a service back to the client. It carries the result of processing, along with any messages generated during execution.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | yes | -- | Matches the request id this is responding to. |
messageType | string | yes | -- | Always 'response'. |
context | string | yes | -- | Echoed from the request. |
operation | string | yes | -- | Echoed from the request. |
timestamp | string | yes | -- | ISO 8601 datetime of when the response was created. |
payload | object | yes | {} | Application-specific response data. |
status | string | yes | -- | 'succeeded', 'failed', or 'pending'. |
messages | ResponseMessage[] | yes | [] | Array of messages generated during processing (errors, warnings, etc.). |
service | string | yes | 'unknown' | Human-readable service identifier (e.g. 'UserService v1.0.0'). |
ResponseMessage
Structured messages generated during request processing. Loosely modeled after the Error object but usable for any severity level.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
severity | string | yes | -- | 'error', 'warning', 'info', or 'debug'. |
message | string | yes | -- | Human-readable message text. |
code | string | no | -- | Machine-readable message code. |
type | string | no | 'StrataResponseMessage' | Message type name (typically the error class name). |
details | object | no | {} | Additional structured details. |
stack | string | no | -- | Stack trace, if applicable. Never expose to end users. |
Post Envelope
A post is fire-and-forget. It has messageType: 'post' and the same structure as a request envelope, minus the responseQueue and timeout fields. The service processes it but sends no response. Useful for events, notifications, or any case where the sender doesn't need confirmation.
Queue Naming (Redis Backend)
The Redis backend uses a predictable naming convention for queues:
| Queue | Key Format | Example |
|---|---|---|
| Request queue | Requests:<serviceGroup> | Requests:UserService |
| Response queue | Responses:<clientName>:<clientID> | Responses:MyApp:a1b2c3d4e5f6 |
| Response queue | Responses:<clientID> | Responses:a1b2c3d4e5f6 |
If the client provides a name, the response queue includes it. If not, the queue key is just Responses:<clientID>.
The Flow
Here's what happens when a client sends a request to a service using the Redis lists backend, step by step:
Client builds a request envelope with a nanoid
id, the targetcontextandoperation, apayload, atimestamp, and theclientidentifier.Backend stamps
responseQueueon the envelope. The client's backend knows the response queue name and fills it in before sending.Client sends the request:
RPUSH Requests:<serviceGroup> <JSON envelope>Service receives the request:
BLPOP Requests:<serviceGroup> <timeout>The service pops the next message from the queue and parses the JSON.
Service processes the request. It routes to the correct context and operation, runs middleware, executes the handler, and builds a response envelope with the same
id,context, andoperation, plus thestatus,payload, and anymessages.Service sends the response:
RPUSH <request.responseQueue> <JSON response envelope>Client receives the response:
BLPOP Responses:<clientName>:<clientID> <timeout>The client pops the response and matches it to the pending request by
id.
Interoperability: A Python Example
Because the protocol is just JSON on a queue, any language that can talk to the backend can be a Strata client. Here's a minimal Python client that sends a request to a Strata service and reads the response:
import redis
import json
import uuid
from datetime import datetime, timezone
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
service_group = 'MyService'
client_id = uuid.uuid4().hex[:20]
response_queue = f'Responses:{client_id}'
request_id = uuid.uuid4().hex[:20]
request = {
'id': request_id,
'messageType': 'request',
'context': 'users',
'operation': 'get',
'responseQueue': response_queue,
'timestamp': datetime.now(timezone.utc).isoformat(),
'timeout': 30000,
'payload': {'userId': '12345'},
'metadata': {},
'client': 'python-client',
}
# Send the request
r.rpush(f'Requests:{service_group}', json.dumps(request))
# Wait for response (30 second timeout)
result = r.blpop(response_queue, timeout=30)
if result:
_, raw = result
response = json.loads(raw)
print(f"Status: {response['status']}")
print(f"Payload: {response['payload']}")This Python script is a fully functional Strata client. It builds a valid request envelope, pushes it onto the service's request queue, and waits for a response on its own dedicated queue. No Strata library needed -- just Redis and JSON.
The same approach works in Go, Rust, Java, C#, or anything else that has a Redis client. The protocol is the contract. The official framework is Node.js, but both clients and services can be built in any language that speaks the protocol.