mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-07-06 12:28:05 +02:00
776c89bfcf
Signed peer manifest pull/announce on the seed, immediate hashchain push for gate messages, seed-only Docker defaults, and stale-genesis sync diagnostics. Co-authored-by: Cursor <cursoragent@cursor.com>
153 lines
5.7 KiB
Python
153 lines
5.7 KiB
Python
"""Operator-signed peer registry for private Infonet swarm discovery."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import time
|
|
from dataclasses import asdict, dataclass, field
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from services.mesh.mesh_crypto import normalize_peer_url
|
|
from services.mesh.mesh_router import peer_transport_kind
|
|
|
|
BACKEND_DIR = Path(__file__).resolve().parents[2]
|
|
DATA_DIR = BACKEND_DIR / "data"
|
|
DEFAULT_PEER_REGISTRY_PATH = DATA_DIR / "peer_registry.json"
|
|
REGISTRY_VERSION = 1
|
|
ALLOWED_REGISTRY_ROLES = {"participant", "relay", "seed"}
|
|
|
|
|
|
@dataclass
|
|
class RegistryPeer:
|
|
peer_url: str
|
|
transport: str
|
|
role: str
|
|
node_id: str = ""
|
|
label: str = ""
|
|
announced_at: int = 0
|
|
last_seen_at: int = 0
|
|
failure_count: int = 0
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
def manifest_peer(self) -> dict[str, str]:
|
|
return {
|
|
"peer_url": self.peer_url,
|
|
"transport": self.transport,
|
|
"role": self.role,
|
|
"label": self.label or self.node_id[:16],
|
|
}
|
|
|
|
|
|
class PeerRegistry:
|
|
def __init__(self, path: str | Path = DEFAULT_PEER_REGISTRY_PATH):
|
|
self.path = Path(path)
|
|
self._peers: dict[str, RegistryPeer] = {}
|
|
|
|
def load(self) -> list[RegistryPeer]:
|
|
if not self.path.exists():
|
|
self._peers = {}
|
|
return []
|
|
raw = json.loads(self.path.read_text(encoding="utf-8"))
|
|
if not isinstance(raw, dict):
|
|
raise ValueError("peer registry root must be an object")
|
|
version = int(raw.get("version", 0) or 0)
|
|
if version != REGISTRY_VERSION:
|
|
raise ValueError(f"unsupported peer registry version: {version}")
|
|
entries = raw.get("peers", [])
|
|
if not isinstance(entries, list):
|
|
raise ValueError("peer registry peers must be a list")
|
|
peers: dict[str, RegistryPeer] = {}
|
|
for entry in entries:
|
|
if not isinstance(entry, dict):
|
|
continue
|
|
peer = self._normalize_entry(entry)
|
|
peers[peer.peer_url] = peer
|
|
self._peers = peers
|
|
return self.records()
|
|
|
|
def save(self) -> None:
|
|
self.path.parent.mkdir(parents=True, exist_ok=True)
|
|
payload = {
|
|
"version": REGISTRY_VERSION,
|
|
"updated_at": int(time.time()),
|
|
"peers": [peer.to_dict() for peer in self.records()],
|
|
}
|
|
self.path.write_text(
|
|
json.dumps(payload, sort_keys=True, indent=2) + "\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
def records(self) -> list[RegistryPeer]:
|
|
return sorted(self._peers.values(), key=lambda item: (item.role, item.peer_url))
|
|
|
|
def upsert_announcement(
|
|
self,
|
|
*,
|
|
peer_url: str,
|
|
transport: str,
|
|
role: str,
|
|
node_id: str = "",
|
|
label: str = "",
|
|
now: float | None = None,
|
|
) -> RegistryPeer:
|
|
normalized = normalize_peer_url(peer_url)
|
|
if not normalized:
|
|
raise ValueError("peer_url is required")
|
|
resolved_transport = str(transport or "").strip().lower() or str(peer_transport_kind(normalized) or "")
|
|
if resolved_transport not in {"onion", "clearnet"}:
|
|
raise ValueError("unsupported peer transport")
|
|
resolved_role = str(role or "participant").strip().lower()
|
|
if resolved_role not in ALLOWED_REGISTRY_ROLES:
|
|
raise ValueError("unsupported peer role")
|
|
timestamp = int(now if now is not None else time.time())
|
|
existing = self._peers.get(normalized)
|
|
peer = RegistryPeer(
|
|
peer_url=normalized,
|
|
transport=resolved_transport,
|
|
role=resolved_role,
|
|
node_id=str(node_id or (existing.node_id if existing else "") or "").strip(),
|
|
label=str(label or (existing.label if existing else "") or "").strip(),
|
|
announced_at=int(existing.announced_at if existing and existing.announced_at else timestamp),
|
|
last_seen_at=timestamp,
|
|
failure_count=int(existing.failure_count if existing else 0),
|
|
)
|
|
self._peers[normalized] = peer
|
|
return peer
|
|
|
|
def prune_stale(self, *, max_age_s: int, now: float | None = None) -> int:
|
|
timestamp = int(now if now is not None else time.time())
|
|
removed = 0
|
|
for peer_url, peer in list(self._peers.items()):
|
|
if peer.role == "seed":
|
|
continue
|
|
last_seen = int(peer.last_seen_at or peer.announced_at or 0)
|
|
if last_seen > 0 and timestamp - last_seen > max(60, int(max_age_s or 0)):
|
|
del self._peers[peer_url]
|
|
removed += 1
|
|
return removed
|
|
|
|
def manifest_peers(self) -> list[dict[str, str]]:
|
|
return [peer.manifest_peer() for peer in self.records()]
|
|
|
|
def _normalize_entry(self, entry: dict[str, Any]) -> RegistryPeer:
|
|
peer_url = normalize_peer_url(str(entry.get("peer_url", "") or ""))
|
|
if not peer_url:
|
|
raise ValueError("registry peer_url is required")
|
|
transport = str(entry.get("transport", "") or peer_transport_kind(peer_url) or "").strip().lower()
|
|
role = str(entry.get("role", "participant") or "participant").strip().lower()
|
|
if role not in ALLOWED_REGISTRY_ROLES:
|
|
raise ValueError("registry role unsupported")
|
|
return RegistryPeer(
|
|
peer_url=peer_url,
|
|
transport=transport,
|
|
role=role,
|
|
node_id=str(entry.get("node_id", "") or "").strip(),
|
|
label=str(entry.get("label", "") or "").strip(),
|
|
announced_at=int(entry.get("announced_at", 0) or 0),
|
|
last_seen_at=int(entry.get("last_seen_at", 0) or entry.get("announced_at", 0) or 0),
|
|
failure_count=int(entry.get("failure_count", 0) or 0),
|
|
)
|