Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/mktl/.gitignore

This file was deleted.

96 changes: 94 additions & 2 deletions src/mktl/protocol/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,99 @@

from . import discover
from . import message
from . import publish
from . import request


"""
mKTL Protocol Layer
===================

This package defines the transport-agnostic messaging protocol used by mKTL.
It provides semantic message structures, construction utilities, and a
user-facing facade that sits above transport/session implementations.

The protocol layer MUST NOT depend on any transport implementation
(e.g. ZeroMQ, RabbitMQ, etc).

---------------------------------------------------------------------

Layer Architecture Overview
---------------------------

User Code
Protocol Facade (protocol.py)
High-level semantic API
- get()
- set()
- publish()
- request()
Hides builder + session details

Message Builder (factory.py)
Fluent construction of protocol messages
- Ensures consistent envelope creation
- Applies defaults / metadata
- No transport awareness

Message Model (message.py)
Immutable protocol data structures
- Envelope
- Message
- MsgType
Defines semantic meaning only

Field Vocabulary (fields.py)
Canonical names for envelope/meta keys
Prevents string drift across system

---------------------------------------------------------------------

Below the Protocol Layer (for context)
--------------------------------------

Session Layer
Executes communication semantics
- request()
- publish()
- subscribe()

Codec / Framing Layer
Maps Message <-> wire frames

Transport Layer
Moves bytes
- ZeroMQ
- RabbitMQ
- etc.

---------------------------------------------------------------------

Design Principles
-----------------

1. Transport Agnostic
Protocol must operate identically regardless of backend.

2. Semantic Purity
Message meaning defined here, not by transport behavior.

3. Layer Isolation
Dependencies only flow downward:
Protocol -> Session -> Transport
Never upward.

4. Ergonomic API
Most users interact only with the Protocol facade.

---------------------------------------------------------------------
"""


# vim: set expandtab tabstop=8 softtabstop=4 shiftwidth=4 autoindent:
94 changes: 94 additions & 0 deletions src/mktl/protocol/builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from __future__ import annotations

import uuid
import time
from typing import Optional, Dict, Any

from .message import Message, Envelope, MsgType


class MessageBuilder:

def __init__(self, sourceid: str):
self._source = sourceid

self._type: Optional[MsgType] = None
self._dest: Optional[str] = None
self._key: Optional[str] = None
self._payload: Dict[str, Any] = {}
self._meta: Dict[str, Any] = {}
self._binary: Optional[bytes] = None
self._transid: Optional[str] = None

# Semantic type setters
def get(self, key: str):
self._type = MsgType.GET
self._key = key
return self

def set(self, key: str):
self._type = MsgType.SET
self._key = key
return self

def hash(self, key: str):
self._type = MsgType.HASH
self._key = key
return self

def config(self, key: str):
self._type = MsgType.CONFIG
self._key = key
return self

def pub(self, key: str):
self._type = MsgType.PUB
self._key = key
return self

def ack(self, transid: str):
self._type = MsgType.ACK
self._transid = transid
return self

def rep(self, transid: str):
self._type = MsgType.REP
self._transid = transid
return self

# Routing
def to(self, destid: Optional[str]):
self._dest = destid
return self

# Data
def payload(self, data: Dict[str, Any]):
self._payload = data
return self

def meta(self, data: Dict[str, Any]):
self._meta.update(data)
return self

def binary(self, blob: bytes):
self._binary = blob
return self

# Finalize
def build(self) -> Message:

if self._type is None:
raise ValueError("Message type not specified")

env = Envelope(
type=self._type,
sourceid=self._source,
destid=self._dest,
key=self._key,
payload=self._payload,
meta=self._meta,
transid=self._transid or str(uuid.uuid4()),
time=time.time(),
)

return Message(env=env, binary=self._binary)
Loading
Loading