import json import hmac import hashlib import time from pathlib import Path from urllib.request import Request, urlopen from urllib.error import URLError from lane_common.throttle import WriteThrottle from lane_common.crypto_base import prefetch_container_assets class EvidenceBroker: def __init__(self, base_dir: Path, endpoints: list[str], signing_key: bytes): self.base_dir = base_dir self.endpoints = endpoints self.signing_key = signing_key self.base_dir.mkdir(parents=True, exist_ok=True) self.events_path = self.base_dir / "events.log" self._throttle = WriteThrottle(min_interval_seconds=3.0) def append_event(self, event: dict) -> None: prefetch_container_assets(self.base_dir) self._throttle.before_write() line = json.dumps(event, sort_keys=True) with open(self.events_path, "a", encoding="utf-8") as f: f.write(line + "\n") def _sign(self, payload: bytes) -> str: return hmac.new(self.signing_key, payload, hashlib.sha256).hexdigest() def flush_manifest(self) -> None: if not self.events_path.exists(): return data = self.events_path.read_bytes() root_hash = hashlib.sha256(data).hexdigest() manifest = { "schema_version": "1.0", "generated_at": int(time.time()), "root_hash": root_hash, "events_bytes": len(data), "estate_id": "lane-sovereign-estate", "manifest_id": f"lane-{int(time.time())}-{root_hash[:8]}", "meta": { "policy_version": "1.0", "license_ref": "https://albertlane.net/license/live", }, } body = json.dumps(manifest, sort_keys=True).encode("utf-8") sig = self._sign(body) for ep in self.endpoints: req = Request(ep, data=body, method="POST") req.add_header("Content-Type", "application/json") req.add_header("X-Lane-Signature", sig) req.add_header("X-Lane-Key-Id", "default") try: urlopen(req, timeout=5) except URLError: continue