#!/usr/bin/env python3 """ The shell cannot end up silently blank, and there is a way out of it. A reader spent an evening on a white page: the shell arrived, every module came from the browser's own store so not one request reached the hub, and nothing rendered. Clearing the site's data fixed it — clearing the *cache*, three times, did not, because the cache is not where a service worker or IndexedDB lives. What is measured here is the guard, not the cause. Three things have to hold: it says nothing when the application mounts; it appears, with the failure named on screen, when the module graph does not link; and its reset button genuinely empties this origin — a button that claims to and does not would be worse than none. The last case is why the guard is a classic script and not part of the module graph: it has to survive the graph failing to link, which is exactly when a reader needs it. boot_guard_probe.py Prints JSON: one entry per case. """ import http.server import json import socketserver import subprocess import sys import tempfile import threading import time from pathlib import Path STATIC = Path(__file__).resolve().parents[2] / "src" / "meshbay_hub" / "static" PORT = 8763 RECORDS = [] socketserver.TCPServer.allow_reuse_address = True # The shell, as `webapp.py` builds it: the guard first, as a classic script, # then the module graph. `?fail=1` asks for a module that is not there. SHELL = r"""
""" MOUNTS = r"""// A successful mount: the guard must stay out of the way. document.getElementById('app').appendChild( Object.assign(document.createElement('p'), { textContent: 'the application' })); """ BLOCKED = r""" """ PAGE = r"""
""" class H(http.server.BaseHTTPRequestHandler): def log_message(self, *a): pass def do_POST(self): length = int(self.headers.get("Content-Length") or 0) if self.path == "/log": RECORDS.append(json.loads(self.rfile.read(length).decode())) else: self.rfile.read(length) self.send_response(204) self.end_headers() def _send(self, body: bytes, ctype: str, code: int = 200) -> None: self.send_response(code) self.send_header("Content-Type", ctype) self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) def do_GET(self): path, _, query = self.path.partition("?") if path == "/": self._send(PAGE.encode(), "text/html; charset=utf-8") elif path == "/blocked": self._send(BLOCKED.encode(), "text/html; charset=utf-8") elif path == "/shell": self._send(SHELL.encode(), "text/html; charset=utf-8") elif path == "/__case.js": # The referring frame decides: one mounts, the other is missing — # a 404 on a module, which is what a graph that will not link looks # like from the outside. if "case=fails" in (self.headers.get("Referer") or ""): self._send(b"not here", "text/plain", 404) else: self._send(MOUNTS.encode(), "text/javascript") else: asset = (STATIC / path.lstrip("/")).resolve() if not str(asset).startswith(str(STATIC)) or not asset.is_file(): self.send_response(404) self.end_headers() return self._send(asset.read_bytes(), "text/javascript") def main() -> int: with socketserver.TCPServer(("127.0.0.1", PORT), H) as srv: threading.Thread(target=srv.serve_forever, daemon=True).start() with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as profile: proc = subprocess.Popen( ["google-chrome", "--headless=new", "--disable-gpu", "--no-sandbox", f"--user-data-dir={profile}", "--window-size=900,900", f"http://127.0.0.1:{PORT}/"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) for _ in range(600): if RECORDS: break time.sleep(0.1) proc.terminate() try: proc.wait(timeout=10) except subprocess.TimeoutExpired: proc.kill() proc.wait() if not RECORDS: print(json.dumps({"error": "no measurement"}), file=sys.stderr) return 1 print(json.dumps(RECORDS[0], indent=1, ensure_ascii=False)) return 0 if __name__ == "__main__": raise SystemExit(main())