betterwithage commited on
Commit
0cd8e94
·
verified ·
1 Parent(s): 2f56dce

chore(sync): mirror backend .py + Dockerfile to Space (hf-sync-backend)

Browse files

Automated 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.

Files changed (2) hide show
  1. Dockerfile +5 -0
  2. 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> &nbsp;·&nbsp; 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
+ ({{'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}}[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()