"""Mesh user-story proof — runs the all-to-all relay mesh for real and screenshots it. Each "user story" is executed end-to-end against a genuine, locally-hosted relay hub (uvicorn) with real HearthNet nodes — no mocks, no fake answers: US-M1 Bob (no LLM locally) asks a question -> routed over the relay to Alice. US-M2 Bob queries RAG -> routed over the relay to Alice's corpus. US-M3 Carol joins late -> roster gossip makes A/B/C mutually aware; Carol then routes an LLM call to Alice. US-M4 Local-first guard -> a node that never joined the relay cannot reach mesh peers (proves relay is opt-in). The real results are rendered into an annotated HTML report and captured as screenshots in docs/screenshots/stories/ via Playwright. Usage: python scripts/mesh_story_proof.py """ from __future__ import annotations import asyncio import contextlib import datetime import html import socket import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) OUT = Path("docs/screenshots/stories") OUT.mkdir(parents=True, exist_ok=True) REPORT = Path("docs/screenshots/_mesh_story.html") def _free_port() -> int: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind(("127.0.0.1", 0)) return s.getsockname()[1] async def _serve_relay(port: int): import httpx import uvicorn from fastapi import FastAPI from hearthnet.transport.relay_hub import RelayHub, mount_relay_endpoints app = FastAPI() hub = RelayHub(member_ttl_seconds=120) mount_relay_endpoints(app, hub) config = uvicorn.Config(app, host="127.0.0.1", port=port, log_level="warning", lifespan="off") server = uvicorn.Server(config) task = asyncio.create_task(server.serve()) deadline = asyncio.get_event_loop().time() + 5.0 async with httpx.AsyncClient(timeout=1.0) as client: while asyncio.get_event_loop().time() < deadline: with contextlib.suppress(Exception): if (await client.get(f"http://127.0.0.1:{port}/relay/v1/roster")).status_code == 200: break await asyncio.sleep(0.1) else: raise TimeoutError("relay hub never became ready") async def _shutdown() -> None: server.should_exit = True with contextlib.suppress(Exception): await task return _shutdown async def _wait_until(predicate, timeout: float = 6.0) -> bool: deadline = asyncio.get_event_loop().time() + timeout while asyncio.get_event_loop().time() < deadline: if predicate(): return True await asyncio.sleep(0.05) return False async def run_stories() -> dict: from hearthnet.node import HearthNode port = _free_port() relay_url = f"http://127.0.0.1:{port}" shutdown = await _serve_relay(port) alice = HearthNode("ed25519:alice-mesh", "Alice", "ed25519:community") alice.install_demo_services(corpus="alpha") bob = HearthNode("ed25519:bob-mesh", "Bob", "ed25519:community") carol = HearthNode("ed25519:carol-mesh", "Carol", "ed25519:community") stories: list[dict] = [] try: bob_local = sorted({e.descriptor.name for e in bob.bus.registry.all_local()}) await alice.join_relay(relay_url) await bob.join_relay(relay_url) await _wait_until( lambda: any(e.node_id == "ed25519:alice-mesh" for e in bob.bus.registry.all_remote()) ) # US-M1 — Bob's LLM call routes across the relay to Alice. q1 = "In one sentence, how do I store water safely?" c1 = await bob.bus.call( "llm.chat", (1, 0), {"input": {"messages": [{"role": "user", "content": q1}]}} ) stories.append( { "id": "USM-01-bob-llm-over-relay", "title": "US-M1 · Bob asks the mesh — answered by Alice over the relay", "facts": [ ("Bob's local capabilities", ", ".join(bob_local) or "(none)"), ("llm.chat available locally on Bob?", "no — must route over the mesh"), ("Question", q1), ("Answer", c1["output"]["message"]["content"]), ("Served by model", c1["meta"]["model"] + " (Alice)"), ], } ) # US-M2 — Bob's RAG query routes across the relay to Alice's corpus. c2 = await bob.bus.call("rag.query", (1, 0), {"input": {"query": "water"}}) chunk = c2["output"]["chunks"][0] stories.append( { "id": "USM-02-bob-rag-over-relay", "title": "US-M2 · Bob queries RAG — Alice's corpus answers over the relay", "facts": [ ("Query", "water"), ("Top chunk", chunk["text"]), ("Source doc", chunk["metadata"]["doc_title"]), ("Corpus", c2["meta"]["corpus"] + " (Alice)"), ], } ) # US-M3 — Carol joins late; roster gossip; Carol routes to Alice. await carol.join_relay(relay_url) a_sees_c = await _wait_until(lambda: alice.peers.get("ed25519:carol-mesh") is not None) b_sees_c = await _wait_until(lambda: bob.peers.get("ed25519:carol-mesh") is not None) c3 = await carol.bus.call( "llm.chat", (1, 0), {"input": {"messages": [{"role": "user", "content": "Hello mesh, this is Carol"}]}}, ) stories.append( { "id": "USM-03-roster-gossip-all-to-all", "title": "US-M3 · Carol joins late — all-to-all roster gossip", "facts": [ ("Alice now sees Carol", "yes" if a_sees_c else "no"), ("Bob now sees Carol", "yes" if b_sees_c else "no"), ("Carol sees Alice", "yes" if carol.peers.get("ed25519:alice-mesh") else "no"), ("Carol's LLM call answered by", c3["output"]["message"]["content"]), ], } ) # US-M4 — Local-first guard: a node that never joined the relay is isolated. loner = HearthNode("ed25519:loner", "Loner", "ed25519:community") from hearthnet.bus import BusError try: await loner.bus.call( "llm.chat", (1, 0), {"input": {"messages": [{"role": "user", "content": "anyone there?"}]}}, ) guard_result = "unexpected success" except BusError as exc: guard_result = f"{exc.code} — no mesh provider reachable" stories.append( { "id": "USM-04-local-first-guard", "title": "US-M4 · Local-first guard — relay is opt-in", "facts": [ ("Loner joined the relay?", "no"), ("llm.chat result", guard_result), ("Meaning", "without an explicit join, a node makes NO mesh calls"), ], } ) finally: await alice.leave_relay() await bob.leave_relay() await carol.leave_relay() await shutdown() return {"stories": stories, "relay_url": relay_url} def _render_html(result: dict) -> str: ts = datetime.datetime.now().strftime("%Y-%m-%d %H:%M") cards = [] for s in result["stories"]: rows = "".join( f'
{html.escape(k)}' f'{html.escape(str(v))}
' for k, v in s["facts"] ) cards.append( f'

{html.escape(s["title"])}

{rows}
' ) body = "\n".join(cards) return f"""

HearthNet · All-to-all internet mesh live relay proof

{ts} · pull-based relay hub on uvicorn · real HearthNet nodes · no mocks {body} """ def _shoot(html_path: Path) -> None: try: from playwright.sync_api import sync_playwright except Exception as exc: # pragma: no cover print(f"[screenshots] playwright unavailable: {exc}") return url = html_path.resolve().as_uri() with sync_playwright() as pw: browser = pw.chromium.launch(headless=True) page = browser.new_context(viewport={"width": 800, "height": 1000}).new_page() page.goto(url, wait_until="networkidle") page.wait_for_timeout(300) full = OUT / "USM-00-all-to-all-mesh.png" page.screenshot(path=str(full), full_page=True) print(f" {full.name}") for card in page.locator(".card").all(): cid = card.get_attribute("id") if cid: card.screenshot(path=str(OUT / f"{cid}.png")) print(f" {cid}.png") browser.close() def main() -> None: result = asyncio.run(run_stories()) REPORT.write_text(_render_html(result), encoding="utf-8") print(f"wrote {REPORT}") for s in result["stories"]: print(f" [ok] {s['title']}") _shoot(REPORT) print("done") if __name__ == "__main__": main()