Skip to content

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.

FieldTypeRequiredDefaultDescription
idstringyes--Unique identifier. Generated with nanoid (20 chars, alphanumeric).
messageTypestringyes--Always 'request'.
contextstringyes--The context to route to (e.g. 'users').
operationstringyes--The operation within the context (e.g. 'get').
responseQueuestringyes--Where to send the response. Set by the backend before sending.
timestampstringyes--ISO 8601 datetime (e.g. '2024-06-25T04:08:22.000Z').
timeoutnumberno0Milliseconds the client will wait for a response. 0 uses the service default.
payloadobjectyes{}Application-specific request data. Opaque to the framework.
metadataobjectyes{}Additional metadata. Opaque to the framework.
authunknownno--Authentication data (typically a JWT or token). Passed through, never inspected.
clientstringyes'unknown'Human-readable client identifier (e.g. 'MyApp v2.1.0').
requestChainstring[]no[]Chain of request IDs for distributed tracing.
priorRequeststringno--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.

FieldTypeRequiredDefaultDescription
idstringyes--Matches the request id this is responding to.
messageTypestringyes--Always 'response'.
contextstringyes--Echoed from the request.
operationstringyes--Echoed from the request.
timestampstringyes--ISO 8601 datetime of when the response was created.
payloadobjectyes{}Application-specific response data.
statusstringyes--'succeeded', 'failed', or 'pending'.
messagesResponseMessage[]yes[]Array of messages generated during processing (errors, warnings, etc.).
servicestringyes'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.

FieldTypeRequiredDefaultDescription
severitystringyes--'error', 'warning', 'info', or 'debug'.
messagestringyes--Human-readable message text.
codestringno--Machine-readable message code.
typestringno'StrataResponseMessage'Message type name (typically the error class name).
detailsobjectno{}Additional structured details.
stackstringno--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:

QueueKey FormatExample
Request queueRequests:<serviceGroup>Requests:UserService
Response queueResponses:<clientName>:<clientID>Responses:MyApp:a1b2c3d4e5f6
Response queueResponses:<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:

  1. Client builds a request envelope with a nanoid id, the target context and operation, a payload, a timestamp, and the client identifier.

  2. Backend stamps responseQueue on the envelope. The client's backend knows the response queue name and fills it in before sending.

  3. Client sends the request:

    RPUSH Requests:<serviceGroup> <JSON envelope>
  4. Service receives the request:

    BLPOP Requests:<serviceGroup> <timeout>

    The service pops the next message from the queue and parses the JSON.

  5. 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, and operation, plus the status, payload, and any messages.

  6. Service sends the response:

    RPUSH <request.responseQueue> <JSON response envelope>
  7. 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:

python
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.