Spaces:
Running
Running
chore(sync): mirror backend .py + Dockerfile to Space (hf-sync-backend)
Browse filesAutomated backend sync from szl-holdings/a11oy main via hf-sync-backend.
Updated (differed from the Space): Dockerfile, a11oy_orbital_page.py
Deleted (gone from the repo + Dockerfile COPY set): (none)
Keeps the Space-built backend (serve.py + the Dockerfile-COPY'd .py
modules) identical to GitHub main so the Space never rebuilds from a
stale backend, new endpoints don't 404 there, and orphaned modules
removed from the repo don't linger in the Space tree.
- Dockerfile +5 -0
- a11oy_orbital_page.py +418 -0
Dockerfile
CHANGED
|
@@ -156,6 +156,11 @@ COPY joule_billing.py szl_energy_ledger.py szl_energy_operator.py szl_energy_pro
|
|
| 156 |
# /api/a11oy/v1/orbital/{topology,projection} 404 live. szl_orbital_projection reuses
|
| 157 |
# szl_energy_operator (the REAL measured ground J/token coefficient), already COPY'd above.
|
| 158 |
COPY szl_orbital_topology.py szl_orbital_projection.py ./
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
# K-Verify governed-inference benchmark — imported by serve.py (guarded). MUST be
|
| 160 |
# per-file COPY'd (this Dockerfile uses no `COPY . .`) or the import falls back and
|
| 161 |
# /api/a11oy/v1/kverify/* 404s. Reuses szl_energy_operator (inference + NVML joules)
|
|
|
|
| 156 |
# /api/a11oy/v1/orbital/{topology,projection} 404 live. szl_orbital_projection reuses
|
| 157 |
# szl_energy_operator (the REAL measured ground J/token coefficient), already COPY'd above.
|
| 158 |
COPY szl_orbital_topology.py szl_orbital_projection.py ./
|
| 159 |
+
# Orbital PAGE (frontend demo surface) — serve.py imports a11oy_orbital_page (guarded)
|
| 160 |
+
# to mount GET /orbital (the MODELED constellation demo, banner-labeled, 0 CDN via the
|
| 161 |
+
# vendor3d three.js COPY'd below). MUST be per-file COPY'd or /orbital falls back to the
|
| 162 |
+
# SPA catch-all (white screen / no orbital surface).
|
| 163 |
+
COPY a11oy_orbital_page.py ./
|
| 164 |
# K-Verify governed-inference benchmark — imported by serve.py (guarded). MUST be
|
| 165 |
# per-file COPY'd (this Dockerfile uses no `COPY . .`) or the import falls back and
|
| 166 |
# /api/a11oy/v1/kverify/* 404s. Reuses szl_energy_operator (inference + NVML joules)
|
a11oy_orbital_page.py
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 Lutar, Stephen P. — SZL Holdings · ORCID 0009-0001-0110-4173 · Doctrine v11
|
| 3 |
+
"""a11oy_orbital_page — ADDITIVE /orbital demo surface (page + 3D/viz).
|
| 4 |
+
|
| 5 |
+
Renders the MODELED SZL orbital-compute constellation against the already-live
|
| 6 |
+
backend (PR #468): GET /api/a11oy/v1/orbital/topology and .../orbital/projection.
|
| 7 |
+
|
| 8 |
+
What it draws (all MODELED — SZL has NO on-orbit hardware):
|
| 9 |
+
* LEO edge-compute shell, MEO aggregation ring, GEO backhaul tier (nodes),
|
| 10 |
+
* OISL inter-satellite links + ground-space downlinks to the REAL ground fabric,
|
| 11 |
+
* a governed-receipt overlay: each MODELED orbital job shows a MODELED energy
|
| 12 |
+
figure (joules/kWh) derived from the REAL ground-measured J/token coefficient
|
| 13 |
+
(cited) PLUS a would-be signed receipt — the SZL moat (governed energy-receipt
|
| 14 |
+
+ signed provenance) applied to space compute. The receipt is explicitly a
|
| 15 |
+
MODELED would-be artifact, never presented as a real signed receipt.
|
| 16 |
+
|
| 17 |
+
HONESTY (doctrine v11, non-negotiable):
|
| 18 |
+
* The ENTIRE surface is visibly + persistently labeled
|
| 19 |
+
"MODELED — Orbital Roadmap (no on-orbit hardware yet)". It can never be mistaken
|
| 20 |
+
for live telemetry.
|
| 21 |
+
* No orbital node/joule/receipt is fabricated. Nodes come straight from the live
|
| 22 |
+
topology endpoint (modeled:true / reachable:false). The energy figure comes from
|
| 23 |
+
the live projection endpoint, whose ONLY MEASURED input is the ground J/token
|
| 24 |
+
coefficient; the orbital joules are MODELED and labeled MODELED. A MODELED
|
| 25 |
+
orbital joule is NEVER labeled MEASURED here.
|
| 26 |
+
* 0 runtime CDN: three.js r160 (MIT) is loaded from the in-image vendored path
|
| 27 |
+
/hero/vendor3d/ (the same proven, allowlisted, runtime-served route the
|
| 28 |
+
cathedral hero uses). No external host is ever fetched.
|
| 29 |
+
* If an endpoint is unreachable the page degrades honestly (shows the error,
|
| 30 |
+
draws nothing fabricated) — honest BLOCKED beats fake green.
|
| 31 |
+
|
| 32 |
+
Pattern mirrors a11oy_formulas_page.register(app, ns): mounts
|
| 33 |
+
GET /orbital (self-contained HTML, 0 CDN)
|
| 34 |
+
GET /api/<ns>/v1/orbital/page-manifest (JSON nav descriptor)
|
| 35 |
+
Registered BEFORE the SPA /{full_path:path} catch-all; try/except-guarded in
|
| 36 |
+
serve.py so a missing dep can never take down the Space. Λ = Conjecture 1;
|
| 37 |
+
sovereign=false on this path. Doctrine v11 LOCKED.
|
| 38 |
+
"""
|
| 39 |
+
from __future__ import annotations
|
| 40 |
+
|
| 41 |
+
from fastapi import FastAPI
|
| 42 |
+
from fastapi.responses import HTMLResponse, JSONResponse
|
| 43 |
+
|
| 44 |
+
DOCTRINE = {"version": "v11", "lambda": "Conjecture 1", "sovereign": False}
|
| 45 |
+
MODELED_BANNER = "MODELED — Orbital Roadmap (no on-orbit hardware yet)"
|
| 46 |
+
|
| 47 |
+
# The two LIVE backend endpoints this surface renders (PR #468). Wired client-side.
|
| 48 |
+
_TOPOLOGY_EP = "/api/{ns}/v1/orbital/topology"
|
| 49 |
+
_PROJECTION_EP = "/api/{ns}/v1/orbital/projection"
|
| 50 |
+
|
| 51 |
+
# 0-CDN vendored three.js r160 — the proven, allowlisted, runtime-served hero path
|
| 52 |
+
# (serve.py GET /hero/vendor3d/{fname}; identical to cathedral_genius.html importmap).
|
| 53 |
+
_THREE_MAIN = "/hero/vendor3d/three.module.min.js"
|
| 54 |
+
_THREE_ADDONS = "/hero/vendor3d/"
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def _page_html(ns: str) -> str:
|
| 58 |
+
topology_ep = _TOPOLOGY_EP.format(ns=ns)
|
| 59 |
+
projection_ep = _PROJECTION_EP.format(ns=ns)
|
| 60 |
+
return f"""<!doctype html>
|
| 61 |
+
<html lang="en"><head>
|
| 62 |
+
<meta charset="UTF-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
| 63 |
+
<title>A11oy — Orbital Compute Tier (MODELED Roadmap)</title>
|
| 64 |
+
<!-- Sovereign importmap: ONLY vendored local files. 0 runtime CDN. Doctrine v11. -->
|
| 65 |
+
<script type="importmap">
|
| 66 |
+
{{
|
| 67 |
+
"imports": {{
|
| 68 |
+
"three": "{_THREE_MAIN}",
|
| 69 |
+
"three/addons/": "{_THREE_ADDONS}"
|
| 70 |
+
}}
|
| 71 |
+
}}
|
| 72 |
+
</script>
|
| 73 |
+
<style>
|
| 74 |
+
:root {{ --bg:#070b16; --panel:#101a2e; --ink:#e8eef7; --muted:#8aa0bd;
|
| 75 |
+
--indigo:#4d8fcc; --terra:#c8643c; --gold:#d8a23c; --amber:#e8c074;
|
| 76 |
+
--green:#2fd07a; --warn:#c8893c; }}
|
| 77 |
+
* {{ box-sizing:border-box; }}
|
| 78 |
+
html,body {{ margin:0; height:100%; }}
|
| 79 |
+
body {{ font-family:ui-sans-serif,system-ui,Segoe UI,Roboto,sans-serif;
|
| 80 |
+
background:radial-gradient(1200px 700px at 70% -10%, #14213b, var(--bg));
|
| 81 |
+
color:var(--ink); overflow:hidden; }}
|
| 82 |
+
/* Persistent, unmissable MODELED banner — top of every viewport. */
|
| 83 |
+
#modeled-banner {{ position:fixed; top:0; left:0; right:0; z-index:50;
|
| 84 |
+
background:linear-gradient(90deg, rgba(216,162,60,.22), rgba(200,100,60,.18));
|
| 85 |
+
border-bottom:1px solid rgba(232,192,116,.5);
|
| 86 |
+
color:var(--amber); font-family:ui-monospace,monospace; font-size:.82rem;
|
| 87 |
+
letter-spacing:.06em; text-transform:uppercase; padding:.55rem 1rem;
|
| 88 |
+
display:flex; align-items:center; gap:.6rem; backdrop-filter:blur(4px); }}
|
| 89 |
+
#modeled-banner .dot {{ width:.6rem; height:.6rem; border-radius:50%;
|
| 90 |
+
background:var(--amber); box-shadow:0 0 8px var(--amber); flex:0 0 auto; }}
|
| 91 |
+
#modeled-banner b {{ color:var(--gold); }}
|
| 92 |
+
#scene {{ position:fixed; inset:0; }}
|
| 93 |
+
#hud {{ position:fixed; top:3rem; left:0; right:0; bottom:0; pointer-events:none;
|
| 94 |
+
padding:1rem 1.1rem; display:flex; flex-direction:column; gap:.8rem; }}
|
| 95 |
+
.plaque {{ font-family:ui-monospace,monospace; font-size:.7rem; letter-spacing:.12em;
|
| 96 |
+
color:var(--muted); text-transform:uppercase; }}
|
| 97 |
+
.plaque b {{ color:var(--gold); }}
|
| 98 |
+
h1 {{ font-size:clamp(1.4rem,3vw,2.1rem); margin:.2rem 0 0; }}
|
| 99 |
+
h1 .accent {{ color:var(--terra); }}
|
| 100 |
+
.sub {{ color:var(--muted); max-width:64ch; line-height:1.5; font-size:.86rem; margin:.3rem 0 0; }}
|
| 101 |
+
.panels {{ display:flex; gap:.8rem; flex-wrap:wrap; margin-top:auto; }}
|
| 102 |
+
.panel {{ pointer-events:auto; background:rgba(16,26,46,.86); border:1px solid #21304d;
|
| 103 |
+
border-radius:12px; padding:.85rem 1rem; min-width:240px; max-width:340px;
|
| 104 |
+
box-shadow:0 18px 40px -28px #000; backdrop-filter:blur(6px); }}
|
| 105 |
+
.panel h3 {{ margin:0 0 .5rem; font-family:ui-monospace,monospace; color:var(--indigo);
|
| 106 |
+
font-size:.9rem; display:flex; align-items:center; gap:.45rem; }}
|
| 107 |
+
.kv {{ display:flex; justify-content:space-between; gap:1rem; font-size:.78rem;
|
| 108 |
+
padding:.18rem 0; border-bottom:1px dashed rgba(138,160,189,.14); }}
|
| 109 |
+
.kv span:first-child {{ color:var(--muted); }}
|
| 110 |
+
.kv span:last-child {{ font-family:ui-monospace,monospace; }}
|
| 111 |
+
.badge {{ font-size:.6rem; padding:.14rem .45rem; border-radius:999px; letter-spacing:.06em;
|
| 112 |
+
text-transform:uppercase; font-family:ui-monospace,monospace; }}
|
| 113 |
+
.badge.modeled {{ background:rgba(232,192,116,.16); color:var(--amber); border:1px solid rgba(232,192,116,.45); }}
|
| 114 |
+
.badge.measured {{ background:rgba(47,208,122,.16); color:var(--green); border:1px solid rgba(47,208,122,.45); }}
|
| 115 |
+
.receipt {{ font-family:ui-monospace,monospace; font-size:.7rem; color:#9fb1bf;
|
| 116 |
+
background:#070c17; border:1px solid #1a2742; border-radius:8px;
|
| 117 |
+
padding:.5rem .6rem; margin-top:.5rem; word-break:break-all; line-height:1.45; }}
|
| 118 |
+
.receipt .lbl {{ color:var(--amber); }}
|
| 119 |
+
.status {{ font-size:.74rem; color:var(--muted); }}
|
| 120 |
+
.status.err {{ color:var(--warn); }}
|
| 121 |
+
a.back {{ color:var(--muted); text-decoration:none; font-size:.8rem; pointer-events:auto; }}
|
| 122 |
+
#legend {{ display:flex; gap:1rem; flex-wrap:wrap; font-size:.72rem; color:var(--muted);
|
| 123 |
+
font-family:ui-monospace,monospace; }}
|
| 124 |
+
#legend i {{ width:.7rem; height:.7rem; border-radius:2px; display:inline-block;
|
| 125 |
+
margin-right:.35rem; vertical-align:middle; }}
|
| 126 |
+
noscript {{ color:var(--amber); display:block; padding:4rem 1.5rem; }}
|
| 127 |
+
</style></head>
|
| 128 |
+
<body>
|
| 129 |
+
<div id="modeled-banner">
|
| 130 |
+
<span class="dot"></span>
|
| 131 |
+
<span><b>{MODELED_BANNER}</b> · SZL operates a REAL ground GPU fabric today;
|
| 132 |
+
every orbital node/link/joule below is a MODELED design artifact — not reachable,
|
| 133 |
+
never serving a real job. Λ = Conjecture 1.</span>
|
| 134 |
+
</div>
|
| 135 |
+
|
| 136 |
+
<canvas id="scene"></canvas>
|
| 137 |
+
|
| 138 |
+
<div id="hud">
|
| 139 |
+
<div>
|
| 140 |
+
<div class="plaque">SZL HOLDINGS / A11OY / DOCTRINE <b>V11 · LOCKED</b> / Λ = CONJECTURE 1</div>
|
| 141 |
+
<h1>The orbital <span class="accent">moat</span>, modeled.</h1>
|
| 142 |
+
<p class="sub" id="subline">Loading the MODELED constellation from the live
|
| 143 |
+
<code>/orbital/*</code> endpoints… Governed energy-receipts + signed provenance,
|
| 144 |
+
applied to space compute — a FORWARD design, not a deployed system.</p>
|
| 145 |
+
<div id="legend"></div>
|
| 146 |
+
</div>
|
| 147 |
+
|
| 148 |
+
<div class="panels">
|
| 149 |
+
<div class="panel" id="panel-topology">
|
| 150 |
+
<h3>◇ Constellation <span class="badge modeled">MODELED</span></h3>
|
| 151 |
+
<div id="topo-body"><div class="status" id="topo-status">fetching topology…</div></div>
|
| 152 |
+
</div>
|
| 153 |
+
<div class="panel" id="panel-receipt">
|
| 154 |
+
<h3>⛓ Governed receipt <span class="badge modeled">MODELED</span></h3>
|
| 155 |
+
<div id="receipt-body"><div class="status" id="proj-status">fetching projection…</div></div>
|
| 156 |
+
</div>
|
| 157 |
+
<div style="align-self:flex-end"><a class="back" href="/">← back to console</a></div>
|
| 158 |
+
</div>
|
| 159 |
+
</div>
|
| 160 |
+
|
| 161 |
+
<noscript>This MODELED orbital roadmap surface requires JavaScript to render its 3D
|
| 162 |
+
constellation. No live telemetry is shown — SZL has no on-orbit hardware.</noscript>
|
| 163 |
+
|
| 164 |
+
<script type="module">
|
| 165 |
+
import * as THREE from 'three';
|
| 166 |
+
import {{ OrbitControls }} from 'three/addons/OrbitControls.js';
|
| 167 |
+
|
| 168 |
+
const TOPOLOGY_EP = {topology_ep!r};
|
| 169 |
+
const PROJECTION_EP = {projection_ep!r};
|
| 170 |
+
|
| 171 |
+
// ---- honesty helpers --------------------------------------------------------
|
| 172 |
+
const TIER_COLOR = {{
|
| 173 |
+
'LEO-edge': 0x4d8fcc, // indigo
|
| 174 |
+
'MEO-aggregation': 0xd8a23c, // gold
|
| 175 |
+
'GEO-backhaul': 0xc8643c, // terra
|
| 176 |
+
}};
|
| 177 |
+
const TIER_RADIUS = {{ 'LEO-edge': 9, 'MEO-aggregation': 15, 'GEO-backhaul': 22 }};
|
| 178 |
+
const AMBER = '#e8c074', GREEN = '#2fd07a';
|
| 179 |
+
|
| 180 |
+
function esc(s) {{ return String(s).replace(/[&<>"']/g, c =>
|
| 181 |
+
({{'&':'&','<':'<','>':'>','"':'"',"'":'''}}[c])); }}
|
| 182 |
+
|
| 183 |
+
// ---- three.js scene (r160, vendored, 0 CDN) --------------------------------
|
| 184 |
+
const canvas = document.getElementById('scene');
|
| 185 |
+
const renderer = new THREE.WebGLRenderer({{ canvas, antialias:true, alpha:true }});
|
| 186 |
+
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
|
| 187 |
+
const scene = new THREE.Scene();
|
| 188 |
+
const camera = new THREE.PerspectiveCamera(55, 1, 0.1, 2000);
|
| 189 |
+
camera.position.set(0, 18, 64);
|
| 190 |
+
const controls = new OrbitControls(camera, canvas);
|
| 191 |
+
controls.enableDamping = true; controls.dampingFactor = 0.06;
|
| 192 |
+
controls.autoRotate = true; controls.autoRotateSpeed = 0.35;
|
| 193 |
+
|
| 194 |
+
scene.add(new THREE.AmbientLight(0x88aacc, 0.7));
|
| 195 |
+
const key = new THREE.PointLight(0xffffff, 1.2); key.position.set(40, 50, 50); scene.add(key);
|
| 196 |
+
|
| 197 |
+
// Earth proxy (MODELED globe — purely a visual reference, not a data claim).
|
| 198 |
+
const earth = new THREE.Mesh(
|
| 199 |
+
new THREE.SphereGeometry(5.6, 48, 48),
|
| 200 |
+
new THREE.MeshStandardMaterial({{ color:0x0d2238, emissive:0x06121f, roughness:0.9, metalness:0.1, wireframe:false }})
|
| 201 |
+
);
|
| 202 |
+
scene.add(earth);
|
| 203 |
+
const grid = new THREE.Mesh(
|
| 204 |
+
new THREE.SphereGeometry(5.7, 24, 24),
|
| 205 |
+
new THREE.MeshBasicMaterial({{ color:0x1d3a5c, wireframe:true, transparent:true, opacity:0.35 }})
|
| 206 |
+
);
|
| 207 |
+
scene.add(grid);
|
| 208 |
+
|
| 209 |
+
const nodeGroup = new THREE.Group(); scene.add(nodeGroup);
|
| 210 |
+
const linkGroup = new THREE.Group(); scene.add(linkGroup);
|
| 211 |
+
|
| 212 |
+
function resize() {{
|
| 213 |
+
const w = innerWidth, h = innerHeight;
|
| 214 |
+
renderer.setSize(w, h, false);
|
| 215 |
+
camera.aspect = w / h; camera.updateProjectionMatrix();
|
| 216 |
+
}}
|
| 217 |
+
addEventListener('resize', resize); resize();
|
| 218 |
+
|
| 219 |
+
(function loop() {{
|
| 220 |
+
requestAnimationFrame(loop);
|
| 221 |
+
controls.update();
|
| 222 |
+
earth.rotation.y += 0.0008; grid.rotation.y += 0.0008;
|
| 223 |
+
renderer.render(scene, camera);
|
| 224 |
+
}})();
|
| 225 |
+
|
| 226 |
+
// Deterministic placement: spread a tier's nodes evenly around a ring, tilted per plane.
|
| 227 |
+
function placeNodes(nodes) {{
|
| 228 |
+
const byTier = {{}};
|
| 229 |
+
for (const n of nodes) (byTier[n.tier] = byTier[n.tier] || []).push(n);
|
| 230 |
+
const pos = {{}};
|
| 231 |
+
for (const [tier, arr] of Object.entries(byTier)) {{
|
| 232 |
+
const R = TIER_RADIUS[tier] || 12;
|
| 233 |
+
arr.forEach((n, i) => {{
|
| 234 |
+
const a = (i / arr.length) * Math.PI * 2;
|
| 235 |
+
const tilt = ((n.orbital_plane || 0) * 0.5);
|
| 236 |
+
pos[n.id] = new THREE.Vector3(
|
| 237 |
+
Math.cos(a) * R,
|
| 238 |
+
Math.sin(tilt) * R * 0.35 + Math.sin(a) * 1.5,
|
| 239 |
+
Math.sin(a) * R
|
| 240 |
+
);
|
| 241 |
+
}});
|
| 242 |
+
}}
|
| 243 |
+
return pos;
|
| 244 |
+
}}
|
| 245 |
+
|
| 246 |
+
function drawTopology(t) {{
|
| 247 |
+
nodeGroup.clear(); linkGroup.clear();
|
| 248 |
+
const nodes = t.orbital_nodes || [];
|
| 249 |
+
const pos = placeNodes(nodes);
|
| 250 |
+
// ground stations sit just outside the globe surface
|
| 251 |
+
const gsList = t.ground_stations || [];
|
| 252 |
+
gsList.forEach((gs, i) => {{
|
| 253 |
+
const a = (i / Math.max(gsList.length,1)) * Math.PI * 2;
|
| 254 |
+
pos[gs.id] = new THREE.Vector3(Math.cos(a) * 6.2, -1.5, Math.sin(a) * 6.2);
|
| 255 |
+
}});
|
| 256 |
+
|
| 257 |
+
for (const n of nodes) {{
|
| 258 |
+
const color = TIER_COLOR[n.tier] || 0x9fb1bf;
|
| 259 |
+
// reachable is REAL-PROBE-ONLY; topology always reports false — render as hollow.
|
| 260 |
+
const mat = new THREE.MeshStandardMaterial({{
|
| 261 |
+
color, emissive:color, emissiveIntensity:0.35, roughness:0.5,
|
| 262 |
+
transparent:true, opacity:0.92
|
| 263 |
+
}});
|
| 264 |
+
const m = new THREE.Mesh(new THREE.SphereGeometry(0.55, 20, 20), mat);
|
| 265 |
+
m.position.copy(pos[n.id]);
|
| 266 |
+
nodeGroup.add(m);
|
| 267 |
+
}}
|
| 268 |
+
// ground stations: small green markers (REAL ground side; the downlink is MODELED)
|
| 269 |
+
for (const gs of gsList) {{
|
| 270 |
+
const m = new THREE.Mesh(new THREE.SphereGeometry(0.4, 16, 16),
|
| 271 |
+
new THREE.MeshStandardMaterial({{ color:0x2fd07a, emissive:0x2fd07a, emissiveIntensity:0.4 }}));
|
| 272 |
+
m.position.copy(pos[gs.id]); nodeGroup.add(m);
|
| 273 |
+
}}
|
| 274 |
+
|
| 275 |
+
for (const l of (t.links || [])) {{
|
| 276 |
+
const a = pos[l.src], b = pos[l.dst];
|
| 277 |
+
if (!a || !b) continue;
|
| 278 |
+
const isDown = l.kind === 'ground-space-downlink';
|
| 279 |
+
const geo = new THREE.BufferGeometry().setFromPoints([a, b]);
|
| 280 |
+
const mat = new THREE.LineBasicMaterial({{
|
| 281 |
+
color: isDown ? 0x2fd07a : 0x6fb1ff, transparent:true, opacity: isDown ? 0.7 : 0.4
|
| 282 |
+
}});
|
| 283 |
+
linkGroup.add(new THREE.Line(geo, mat));
|
| 284 |
+
}}
|
| 285 |
+
|
| 286 |
+
// legend
|
| 287 |
+
document.getElementById('legend').innerHTML = [
|
| 288 |
+
['#4d8fcc','LEO edge (MODELED)'], ['#d8a23c','MEO aggregation (MODELED)'],
|
| 289 |
+
['#c8643c','GEO backhaul (MODELED)'], ['#6fb1ff','OISL link (MODELED)'],
|
| 290 |
+
['#2fd07a','ground downlink (MODELED) → REAL ground node'],
|
| 291 |
+
].map(([c,t]) => `<span><i style="background:${{c}}"></i>${{esc(t)}}</span>`).join('');
|
| 292 |
+
|
| 293 |
+
// topology panel
|
| 294 |
+
const s = t.summary || {{}};
|
| 295 |
+
document.getElementById('topo-body').innerHTML = [
|
| 296 |
+
['data_kind', t.data_kind], ['status', t.status],
|
| 297 |
+
['on-orbit hardware', String(t.on_orbit_hardware)],
|
| 298 |
+
['LEO / MEO / GEO', `${{s.leo_edge_nodes}} / ${{s.meo_aggregation_nodes}} / ${{s.geo_backhaul_nodes}}`],
|
| 299 |
+
['total nodes / links', `${{s.total_nodes}} / ${{s.total_links}}`],
|
| 300 |
+
['reachable nodes', String(s.reachable_nodes)],
|
| 301 |
+
].map(([k,v]) => `<div class="kv"><span>${{esc(k)}}</span><span>${{esc(v)}}</span></div>`).join('');
|
| 302 |
+
}}
|
| 303 |
+
|
| 304 |
+
// Build a would-be (MODELED) signed receipt from the MODELED projection. This is
|
| 305 |
+
// the SZL moat illustrated — NOT a real signed receipt; explicitly labeled MODELED.
|
| 306 |
+
async function modeledReceiptId(proj) {{
|
| 307 |
+
const op = proj.orbital_projection || {{}};
|
| 308 |
+
const j = (op.orbital_joules && op.orbital_joules.value) || 0;
|
| 309 |
+
const basis = JSON.stringify({{ j, ts: proj.timestamp_utc, node: (proj.orbital_node||{{}}).id }});
|
| 310 |
+
let hex = 'unavailable';
|
| 311 |
+
try {{
|
| 312 |
+
const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(basis));
|
| 313 |
+
hex = Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2,'0')).join('').slice(0, 32);
|
| 314 |
+
}} catch (e) {{ hex = 'sha256-unavailable-(no-subtlecrypto)'; }}
|
| 315 |
+
return hex;
|
| 316 |
+
}}
|
| 317 |
+
|
| 318 |
+
async function drawProjection(p) {{
|
| 319 |
+
const coeff = p.ground_measured_coefficient || {{}};
|
| 320 |
+
const op = p.orbital_projection || {{}};
|
| 321 |
+
const jpt = (coeff.j_per_token && coeff.j_per_token.value);
|
| 322 |
+
const ojoules = (op.orbital_joules && op.orbital_joules.value);
|
| 323 |
+
const okwh = (op.orbital_kwh && op.orbital_kwh.value);
|
| 324 |
+
const rid = await modeledReceiptId(p);
|
| 325 |
+
|
| 326 |
+
document.getElementById('receipt-body').innerHTML = `
|
| 327 |
+
<div class="kv"><span>ground J/token</span><span>${{esc(jpt)}} <span class="badge measured">MEASURED</span></span></div>
|
| 328 |
+
<div class="kv"><span>coeff source</span><span>${{esc(coeff.measured_source || '—')}}</span></div>
|
| 329 |
+
<div class="kv"><span>orbital joules</span><span>${{esc(ojoules)}} <span class="badge modeled">MODELED</span></span></div>
|
| 330 |
+
<div class="kv"><span>orbital kWh</span><span>${{esc(okwh)}} <span class="badge modeled">MODELED</span></span></div>
|
| 331 |
+
<div class="receipt">
|
| 332 |
+
<span class="lbl">would-be signed receipt (MODELED — not a real signature):</span><br/>
|
| 333 |
+
governed-energy-receipt:${{esc(rid)}}<br/>
|
| 334 |
+
<span class="lbl">basis:</span> MEASURED ground J/token × MODELED workload × MODELED space-overhead<br/>
|
| 335 |
+
<span class="lbl">honesty:</span> sovereign=false · Λ=Conjecture 1 · no on-orbit hardware
|
| 336 |
+
</div>`;
|
| 337 |
+
document.getElementById('subline').innerHTML =
|
| 338 |
+
'Each MODELED orbital job carries a MODELED energy figure (derived from the REAL ' +
|
| 339 |
+
'ground-measured J/token coefficient, cited above) and a <b>would-be signed ' +
|
| 340 |
+
'governed-energy-receipt</b> — the SZL moat, applied to space compute. Forward design, not deployed.';
|
| 341 |
+
}}
|
| 342 |
+
|
| 343 |
+
function fail(elId, msg) {{
|
| 344 |
+
const el = document.getElementById(elId);
|
| 345 |
+
if (el) {{ el.className = 'status err'; el.textContent = msg; }}
|
| 346 |
+
}}
|
| 347 |
+
|
| 348 |
+
(async function load() {{
|
| 349 |
+
try {{
|
| 350 |
+
const r = await fetch(TOPOLOGY_EP, {{ headers:{{Accept:'application/json'}} }});
|
| 351 |
+
const t = await r.json();
|
| 352 |
+
if (!r.ok || t.ok === false) throw new Error('topology ' + r.status);
|
| 353 |
+
drawTopology(t);
|
| 354 |
+
}} catch (e) {{ fail('topo-status', 'topology unavailable: ' + e + ' (nothing fabricated)'); }}
|
| 355 |
+
|
| 356 |
+
try {{
|
| 357 |
+
const r = await fetch(PROJECTION_EP, {{ headers:{{Accept:'application/json'}} }});
|
| 358 |
+
const p = await r.json();
|
| 359 |
+
if (!r.ok || p.ok === false) throw new Error('projection ' + r.status);
|
| 360 |
+
await drawProjection(p);
|
| 361 |
+
}} catch (e) {{ fail('proj-status', 'projection unavailable: ' + e + ' (no fabricated joule)'); }}
|
| 362 |
+
}})();
|
| 363 |
+
</script>
|
| 364 |
+
</body></html>"""
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
def register(app: FastAPI, ns: str = "a11oy") -> str:
|
| 368 |
+
"""Mount GET /orbital (HTML) + GET /api/<ns>/v1/orbital/page-manifest (JSON).
|
| 369 |
+
ADDITIVE — registered before the SPA catch-all; touches no existing route."""
|
| 370 |
+
|
| 371 |
+
@app.get("/orbital", include_in_schema=False)
|
| 372 |
+
async def orbital_page() -> HTMLResponse: # noqa: ANN202
|
| 373 |
+
return HTMLResponse(_page_html(ns))
|
| 374 |
+
|
| 375 |
+
@app.get(f"/api/{ns}/v1/orbital/page-manifest", include_in_schema=False)
|
| 376 |
+
async def orbital_page_manifest() -> JSONResponse: # noqa: ANN202
|
| 377 |
+
return JSONResponse({
|
| 378 |
+
"section": "Orbital",
|
| 379 |
+
"page": "/orbital",
|
| 380 |
+
"data_kind": "MODELED-roadmap",
|
| 381 |
+
"banner": MODELED_BANNER,
|
| 382 |
+
"on_orbit_hardware": False,
|
| 383 |
+
"doctrine": DOCTRINE,
|
| 384 |
+
"renders_endpoints": [
|
| 385 |
+
_TOPOLOGY_EP.format(ns=ns),
|
| 386 |
+
_PROJECTION_EP.format(ns=ns),
|
| 387 |
+
],
|
| 388 |
+
"vendored_3d": {"three": _THREE_MAIN, "addons": _THREE_ADDONS, "revision": "r160", "runtime_cdn": 0},
|
| 389 |
+
"note": ("MODELED orbital roadmap surface — no on-orbit hardware. Nodes/joules "
|
| 390 |
+
"come from the live MODELED endpoints; the signed receipt shown is a "
|
| 391 |
+
"MODELED would-be artifact, never a real signature."),
|
| 392 |
+
})
|
| 393 |
+
|
| 394 |
+
return f"orbital-page mounted: GET /orbital + page-manifest (renders MODELED topology+projection, 0 CDN)"
|
| 395 |
+
|
| 396 |
+
|
| 397 |
+
def _selftest() -> None:
|
| 398 |
+
html = _page_html("a11oy")
|
| 399 |
+
# 1) the persistent MODELED banner is present, verbatim
|
| 400 |
+
assert MODELED_BANNER in html, "MODELED banner missing"
|
| 401 |
+
assert "no on-orbit hardware" in html, "no-hardware honesty note missing"
|
| 402 |
+
# 2) both live endpoints are wired client-side
|
| 403 |
+
assert "/api/a11oy/v1/orbital/topology" in html, "topology endpoint not wired"
|
| 404 |
+
assert "/api/a11oy/v1/orbital/projection" in html, "projection endpoint not wired"
|
| 405 |
+
# 3) 0 runtime CDN — three.js loads from the vendored hero path, no external host
|
| 406 |
+
assert _THREE_MAIN in html and "http://" not in html and "https://" not in html, \
|
| 407 |
+
"external URL / CDN reference found (0-CDN doctrine)"
|
| 408 |
+
# 4) a renderable marker exists (canvas + heading) so a live grep proves real content
|
| 409 |
+
assert 'id="scene"' in html and "orbital" in html.lower(), "render marker missing"
|
| 410 |
+
# 5) honesty: a MODELED orbital joule is never relabeled MEASURED; only the
|
| 411 |
+
# ground coefficient carries MEASURED. The receipt is explicitly MODELED.
|
| 412 |
+
assert "would-be signed receipt (MODELED" in html, "receipt not labeled MODELED"
|
| 413 |
+
assert "Conjecture 1" in html, "Λ label missing"
|
| 414 |
+
print("a11oy_orbital_page: ALL OK (MODELED banner, endpoints wired, 0 CDN, render marker, honest receipt)")
|
| 415 |
+
|
| 416 |
+
|
| 417 |
+
if __name__ == "__main__":
|
| 418 |
+
_selftest()
|