build(agent): molt-by#23c260 iteration
This commit is contained in:
parent
581963cabf
commit
89140c4027
|
|
@ -12,6 +12,7 @@ and a registry as the project evolves.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .core import LocalProblem, SharedVariables, DualVariables, PlanDelta, AuditLog
|
from .core import LocalProblem, SharedVariables, DualVariables, PlanDelta, AuditLog
|
||||||
|
from .transport import TLSChannel
|
||||||
from .registry import GraphOfContracts
|
from .registry import GraphOfContracts
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
|
@ -21,4 +22,5 @@ __all__ = [
|
||||||
"PlanDelta",
|
"PlanDelta",
|
||||||
"AuditLog",
|
"AuditLog",
|
||||||
"GraphOfContracts",
|
"GraphOfContracts",
|
||||||
|
"TLSChannel",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
"""Delta-sync helpers for EnergiaMesh MVP.
|
||||||
|
|
||||||
|
This module provides a tiny, dependency-free toy delta generator that can be
|
||||||
|
used by adapters to evolve local problem state and shared variables in a
|
||||||
|
controlled way. It intentionally keeps the logic small while being a clear
|
||||||
|
public API surface for MVP wiring.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Any, Dict
|
||||||
|
import time
|
||||||
|
|
||||||
|
from energiamesh.core import LocalProblem, SharedVariables, PlanDelta
|
||||||
|
|
||||||
|
|
||||||
|
def compute_delta(lp: LocalProblem, sv: SharedVariables) -> PlanDelta:
|
||||||
|
"""Create a minimal delta from a LocalProblem and SharedVariables.
|
||||||
|
|
||||||
|
This is deliberately simple: it captures the latest forecast-ish signal (if
|
||||||
|
present) and the local problem id to demonstrate how a downstream consumer
|
||||||
|
might apply incremental updates.
|
||||||
|
"""
|
||||||
|
payload: Dict[str, Any] = {}
|
||||||
|
if sv.signals:
|
||||||
|
# Copy a representative signal (best-effort; shallow copy is fine for MVP)
|
||||||
|
for k, v in sv.signals.items():
|
||||||
|
payload[f"forecast:{k}"] = v
|
||||||
|
break # keep payload small for MVP
|
||||||
|
|
||||||
|
delta = PlanDelta(delta=payload, delta_id=f"d-{int(time.time())}")
|
||||||
|
return delta
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import threading
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
|
||||||
|
class TLSChannel:
|
||||||
|
"""Lightweight, test-friendly TLS-like transport channel.
|
||||||
|
|
||||||
|
This is a minimal stand-in for a real TLS transport used in MVP wiring.
|
||||||
|
It provides a secure envelope around messages and a simple replay-protection
|
||||||
|
mechanism via an in-memory nonce store per channel.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, peer_id: str, tls_config: Dict[str, Any] | None = None) -> None:
|
||||||
|
self.peer_id = peer_id
|
||||||
|
self.tls_config = tls_config or {}
|
||||||
|
self._seen_nonces = set()
|
||||||
|
self._lock = threading.Lock()
|
||||||
|
|
||||||
|
def _generate_nonce(self) -> str:
|
||||||
|
# Simple, filesystem-backed randomness for a nonce
|
||||||
|
return os.urandom(16).hex()
|
||||||
|
|
||||||
|
def send(self, payload: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""Wrap payload into a secure envelope for transmission."""
|
||||||
|
nonce = self._generate_nonce()
|
||||||
|
envelope = {
|
||||||
|
"secure": True,
|
||||||
|
"from": self.peer_id,
|
||||||
|
"payload": payload,
|
||||||
|
"nonce": nonce,
|
||||||
|
"ts": time.time(),
|
||||||
|
"tls_meta": {
|
||||||
|
"tls_version": self.tls_config.get("version", "TLS1.3"),
|
||||||
|
"cipher": self.tls_config.get("cipher", "AES-256-GCM"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
with self._lock:
|
||||||
|
self._seen_nonces.add(nonce)
|
||||||
|
return envelope
|
||||||
|
|
||||||
|
def receive(self, envelope: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""Unwrap a secure envelope, performing a minimal replay check."""
|
||||||
|
if not isinstance(envelope, dict) or not envelope.get("secure"):
|
||||||
|
raise ValueError("Invalid envelope: missing secure flag")
|
||||||
|
nonce = envelope.get("nonce")
|
||||||
|
if not nonce:
|
||||||
|
raise ValueError("Invalid envelope: missing nonce")
|
||||||
|
with self._lock:
|
||||||
|
if nonce in self._seen_nonces:
|
||||||
|
raise ValueError("Replay detected for nonce: {}".format(nonce))
|
||||||
|
self._seen_nonces.add(nonce)
|
||||||
|
|
||||||
|
# Return the inner payload for higher-level handling
|
||||||
|
return envelope["payload"]
|
||||||
Loading…
Reference in New Issue