Spaces:
Running
Running
Warhacker: expand demo tab to full 5x5 = 25 demos (per-problem demos, real tamper, honest formula panels, sovereign canvas globe fallback)
Browse filesBackend: 25-demo registry reusing KhipuChain/Merkle/RFC-6962 inclusion/point-in-polygon/conformal/CPA; real perf_counter timeline, always-on single-byte tamper test breaking the signed SHA-256 Merkle chain, honest formula statuses (locked-proven {F11,M2,CP1,G1,S2,B1}; conformal=W5-3/CP1; Lambda=Conjecture 1). register25() mounts /warhacker/index + /warhacker/run/{problem}/{demo}; /wh-demo backward-compat kept. Frontend: 5x5 picker, nominal/tamper, full panels, 23 vendored-lib viz renderers (zero CDN), sovereign 2D-canvas orbital fallback, mobile-responsive.
- pages/warhacker.html +796 -281
- szl_warhacker_demos.py +0 -0
pages/warhacker.html
CHANGED
|
@@ -3,9 +3,9 @@
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8"/>
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
| 6 |
-
<title>a11oy — Warhacker
|
| 7 |
-
<meta name="description" content="
|
| 8 |
-
<!-- SOVEREIGN: zero runtime CDN. System font stack only
|
| 9 |
<style>
|
| 10 |
:root{
|
| 11 |
--ground:#0a0a0a; --panel:#0c0c0c; --panel2:#070707;
|
|
@@ -20,12 +20,12 @@
|
|
| 20 |
--display:'Segoe UI','Helvetica Neue',Arial,system-ui,-apple-system,sans-serif;
|
| 21 |
}
|
| 22 |
*{box-sizing:border-box;}
|
| 23 |
-
html,body{margin:0;padding:0;background:var(--ground);color:var(--cream);
|
| 24 |
font-family:var(--display);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;}
|
| 25 |
.mono{font-family:var(--mono);}
|
| 26 |
a{color:inherit;}
|
| 27 |
:focus-visible{outline:2px solid var(--gold);outline-offset:3px;border-radius:3px;}
|
| 28 |
-
.ribbon{position:sticky;top:0;z-index:
|
| 29 |
flex-wrap:wrap;padding:0.5rem 1.25rem;font-family:var(--mono);font-size:10px;
|
| 30 |
letter-spacing:0.12em;text-transform:uppercase;color:var(--gold);
|
| 31 |
background:rgba(10,10,10,0.92);backdrop-filter:blur(10px);
|
|
@@ -38,7 +38,7 @@
|
|
| 38 |
.live-dot{width:6px;height:6px;border-radius:50%;background:var(--live);
|
| 39 |
box-shadow:0 0 6px var(--live);animation:pulseDot 2.2s ease-in-out infinite;}
|
| 40 |
@keyframes pulseDot{0%,100%{opacity:1;}50%{opacity:.35;}}
|
| 41 |
-
.hero{position:relative;overflow:hidden;padding:
|
| 42 |
.grid-bg{position:absolute;inset:0;pointer-events:none;
|
| 43 |
background-image:
|
| 44 |
linear-gradient(rgba(255,255,255,.025) 1px,transparent 1px),
|
|
@@ -48,91 +48,97 @@
|
|
| 48 |
mask-image:radial-gradient(ellipse at center,#000 30%,transparent 75%);}
|
| 49 |
.hero-inner{position:relative;z-index:2;max-width:64rem;margin:0 auto;}
|
| 50 |
.eyebrow{display:inline-flex;align-items:center;gap:.5rem;padding:.3rem .85rem;
|
| 51 |
-
border-radius:999px;border:1px solid var(--gold-line);background:var(--gold-soft);margin-bottom:1.
|
| 52 |
.eyebrow-dot{width:6px;height:6px;border-radius:50%;background:var(--gold);
|
| 53 |
animation:pulseDot 2.2s ease-in-out infinite;}
|
| 54 |
.eyebrow-text{font-family:var(--mono);font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--gold);}
|
| 55 |
-
.headline{font-size:clamp(
|
| 56 |
-
letter-spacing:-.03em;margin:0 0
|
| 57 |
.headline .accent{background:linear-gradient(120deg,var(--cream) 20%,var(--gold) 90%);
|
| 58 |
-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:transparent;}
|
| 59 |
-
.support{font-size:
|
| 60 |
-
.
|
| 61 |
-
|
| 62 |
-
background:var(--
|
| 63 |
-
.runall:hover{background:var(--gold-bright);}
|
| 64 |
-
.runall:disabled{opacity:.5;cursor:not-allowed;}
|
| 65 |
-
.stats{display:flex;flex-wrap:wrap;justify-content:center;gap:.75rem;max-width:60rem;margin:1.8rem auto 0;}
|
| 66 |
-
.stat{flex:1 1 150px;min-width:140px;border:1px solid var(--gold-line);border-radius:8px;
|
| 67 |
-
background:var(--panel);padding:.85rem 1rem;text-align:left;}
|
| 68 |
.stat .k{font-family:var(--mono);font-size:9px;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);}
|
| 69 |
-
.stat .v{font-size:1.
|
| 70 |
.stat .v.teal{color:var(--teal);}
|
| 71 |
-
.stat .v.err{color:var(--err-bright);}
|
| 72 |
|
| 73 |
-
/* tabs */
|
| 74 |
-
.tabs-bar{position:sticky;top:
|
| 75 |
-
padding:.
|
| 76 |
-
.tab-btn{font-family:var(--mono);font-size:11px;letter-spacing:.
|
| 77 |
border:1px solid var(--gold-line);background:transparent;color:var(--paragraph);cursor:pointer;transition:all .18s ease;}
|
| 78 |
.tab-btn:hover{color:var(--gold);border-color:rgba(201,183,135,.35);}
|
| 79 |
.tab-btn.active{background:var(--gold);color:var(--ground);border-color:var(--gold);font-weight:600;}
|
| 80 |
-
.tab-btn .dot{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--dim);margin-right:.4rem;vertical-align:middle;}
|
| 81 |
-
.tab-btn .dot.ok{background:var(--live-bright);box-shadow:0 0 5px var(--live);}
|
| 82 |
-
.tab-btn .dot.err{background:var(--err-bright);box-shadow:0 0 5px var(--err);}
|
| 83 |
-
.tab-btn .dot.loading{background:var(--gold);animation:pulseDot 1s ease-in-out infinite;}
|
| 84 |
|
| 85 |
-
.panel-wrap{max-width:
|
| 86 |
.panel{display:none;}
|
| 87 |
.panel.active{display:block;animation:fade .3s ease;}
|
| 88 |
@keyframes fade{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:none;}}
|
| 89 |
.p-head{margin-bottom:.9rem;}
|
| 90 |
.p-titlerow{display:flex;align-items:center;gap:.7rem;flex-wrap:wrap;margin-bottom:.4rem;}
|
| 91 |
-
.p-title{font-size:1.
|
| 92 |
.label-real,.label-roadmap{font-family:var(--mono);font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;
|
| 93 |
padding:.2rem .55rem;border-radius:5px;font-weight:600;}
|
| 94 |
.label-real{color:var(--live-bright);border:1px solid rgba(121,199,154,.45);background:rgba(90,138,110,.12);}
|
| 95 |
.label-roadmap{color:var(--amber);border:1px solid rgba(214,178,74,.4);background:rgba(214,178,74,.08);}
|
| 96 |
-
.p-blurb{font-size:
|
| 97 |
.roadmap-note{font-size:11.5px;line-height:1.55;color:var(--amber);background:rgba(214,178,74,.06);
|
| 98 |
border:1px solid rgba(214,178,74,.25);border-radius:7px;padding:.55rem .8rem;margin:.7rem 0 0;}
|
| 99 |
|
| 100 |
-
|
| 101 |
-
.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
.r-btn{display:inline-flex;align-items:center;gap:.4rem;padding:.5rem 1rem;font-size:11.5px;
|
| 103 |
font-weight:500;font-family:var(--mono);border-radius:6px;border:1px solid var(--gold-line);
|
| 104 |
background:transparent;color:var(--gold);cursor:pointer;letter-spacing:.04em;
|
| 105 |
transition:background .18s ease,border-color .18s ease;}
|
| 106 |
.r-btn:hover{background:rgba(201,183,135,.08);border-color:rgba(201,183,135,.35);}
|
| 107 |
-
.r-btn:active{opacity:.75;}
|
| 108 |
.r-btn:disabled{opacity:.4;cursor:not-allowed;}
|
| 109 |
.r-btn-primary{background:var(--gold);color:var(--ground);border-color:var(--gold);}
|
| 110 |
.r-btn-primary:hover{background:var(--gold-bright);border-color:var(--gold-bright);}
|
| 111 |
.r-btn-danger{color:var(--err-bright);border-color:rgba(176,106,90,.45);}
|
| 112 |
.r-btn-danger:hover{background:rgba(176,106,90,.1);}
|
| 113 |
-
.seg .r-btn.sel{background:var(--gold);color:var(--ground);border-color:var(--gold);}
|
| 114 |
|
| 115 |
.verdict{display:flex;align-items:center;gap:.5rem;font-family:var(--mono);font-size:12.5px;
|
| 116 |
-
padding:.5rem .8rem;border-radius:7px;margin-bottom:.8rem;
|
| 117 |
-
.verdict.show{visibility:visible;}
|
| 118 |
.verdict.ok{color:var(--live-bright);border:1px solid rgba(90,138,110,.45);background:rgba(90,138,110,.1);}
|
| 119 |
.verdict.err{color:var(--err-bright);border:1px solid rgba(176,106,90,.5);background:rgba(176,106,90,.1);}
|
| 120 |
|
| 121 |
-
/* section blocks */
|
| 122 |
.block{border:1px solid var(--gold-line);border-radius:10px;background:var(--panel);
|
| 123 |
padding:.9rem 1rem 1rem;margin-bottom:1rem;}
|
| 124 |
.block-h{font-family:var(--mono);font-size:9.5px;letter-spacing:.13em;text-transform:uppercase;
|
| 125 |
-
color:var(--muted);margin:0 0 .65rem;display:flex;align-items:center;gap:.5rem;}
|
| 126 |
.block-h .pillc{color:var(--teal);border:1px solid var(--teal-line);background:var(--teal-soft);
|
| 127 |
border-radius:4px;padding:.05rem .35rem;font-size:9px;}
|
| 128 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
/* step timeline */
|
| 130 |
.tl{list-style:none;margin:0;padding:0;}
|
| 131 |
.tl li{display:flex;gap:.65rem;align-items:flex-start;padding:.5rem 0;border-bottom:1px solid var(--gold-soft);}
|
| 132 |
.tl li:last-child{border-bottom:none;}
|
| 133 |
.tl .ic{flex:0 0 auto;width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;
|
| 134 |
font-family:var(--mono);font-size:10px;margin-top:1px;border:1px solid var(--dim);color:var(--dim);background:transparent;}
|
| 135 |
-
.tl li.pending .ic{border-color:var(--dim);color:var(--dim);}
|
| 136 |
.tl li.running .ic{border-color:var(--gold);color:var(--gold);animation:pulseDot 1s ease-in-out infinite;}
|
| 137 |
.tl li.complete .ic{background:rgba(90,138,110,.18);color:var(--live-bright);border-color:rgba(90,138,110,.5);}
|
| 138 |
.tl li.failed .ic{background:rgba(176,106,90,.18);color:var(--err-bright);border-color:rgba(176,106,90,.55);}
|
|
@@ -147,7 +153,7 @@
|
|
| 147 |
/* catch tree */
|
| 148 |
.ct{list-style:none;margin:0;padding:0;}
|
| 149 |
.ct li{font-family:var(--mono);font-size:11.5px;padding:.32rem .6rem;border-radius:5px;margin-bottom:.25rem;
|
| 150 |
-
display:flex;align-items:flex-start;gap:.5rem;border:1px solid transparent;}
|
| 151 |
.ct li.pass{color:var(--paragraph);}
|
| 152 |
.ct li.pass .mark{color:var(--live-bright);}
|
| 153 |
.ct li.fail{color:var(--err-bright);background:rgba(176,106,90,.1);border-color:rgba(176,106,90,.4);}
|
|
@@ -160,10 +166,10 @@
|
|
| 160 |
.ct .firstbadge{font-size:8.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--err-bright);
|
| 161 |
border:1px solid rgba(224,138,118,.5);border-radius:3px;padding:.02rem .3rem;margin-left:.45rem;}
|
| 162 |
|
| 163 |
-
/* tamper
|
| 164 |
.tamper-grid{display:grid;grid-template-columns:1fr 1fr;gap:.5rem;}
|
| 165 |
@media(max-width:560px){.tamper-grid{grid-template-columns:1fr;}}
|
| 166 |
-
.tamper-cell{border:1px solid var(--gold-soft);border-radius:6px;padding:.5rem .65rem;}
|
| 167 |
.tamper-cell .tk{font-family:var(--mono);font-size:8.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);}
|
| 168 |
.tamper-cell .tv{font-family:var(--mono);font-size:11px;margin-top:.2rem;word-break:break-all;}
|
| 169 |
.tv.bad{color:var(--err-bright);} .tv.good{color:var(--live-bright);}
|
|
@@ -174,6 +180,13 @@
|
|
| 174 |
.bytediff .b4{color:var(--paragraph);} .bytediff .af{color:var(--err-bright);}
|
| 175 |
.bytediff .hl{background:rgba(224,138,118,.35);color:#fff;border-radius:2px;padding:0 1px;}
|
| 176 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
/* formula panel */
|
| 178 |
.fp{list-style:none;margin:0;padding:0;}
|
| 179 |
.fp li{border:1px solid var(--gold-soft);border-radius:7px;padding:.55rem .7rem;margin-bottom:.5rem;}
|
|
@@ -186,27 +199,20 @@
|
|
| 186 |
.fp .status b.roadmap{color:var(--amber);}
|
| 187 |
.fp .where{font-family:var(--mono);font-size:9.5px;color:var(--muted);margin-top:.3rem;}
|
| 188 |
|
| 189 |
-
/* raw out */
|
| 190 |
.out{position:relative;border:1px solid var(--gold-line);border-radius:8px;background:var(--panel2);overflow:hidden;}
|
| 191 |
.out-label{display:flex;align-items:center;justify-content:space-between;padding:.45rem .85rem;
|
| 192 |
border-bottom:1px solid var(--gold-line);font-family:var(--mono);font-size:9px;
|
| 193 |
letter-spacing:.1em;text-transform:uppercase;color:var(--muted);cursor:pointer;}
|
| 194 |
-
.out-status{display:inline-flex;align-items:center;gap:.4rem;}
|
| 195 |
-
.status-dot{width:6px;height:6px;border-radius:50%;background:var(--dim);transition:background .3s;}
|
| 196 |
-
.status-dot.ok{background:var(--live-bright);box-shadow:0 0 6px var(--live);}
|
| 197 |
-
.status-dot.err{background:var(--err-bright);box-shadow:0 0 6px var(--err);}
|
| 198 |
-
.status-dot.loading{background:var(--gold);animation:pulseDot 1s ease-in-out infinite;}
|
| 199 |
.out-pre{margin:0;padding:.85rem;font-family:var(--mono);font-size:10px;line-height:1.55;
|
| 200 |
color:var(--paragraph);white-space:pre-wrap;word-break:break-word;max-height:340px;overflow-y:auto;
|
| 201 |
scrollbar-width:thin;scrollbar-color:var(--dim) transparent;display:none;}
|
| 202 |
.out-pre.show{display:block;}
|
| 203 |
-
.out-pre:empty::before{content:"— run this demo to call the live mechanism —";color:var(--dim);font-style:italic;}
|
| 204 |
.honesty-note{font-family:var(--mono);font-size:10.5px;color:var(--teal);line-height:1.55;
|
| 205 |
margin-top:.8rem;border-left:2px solid var(--teal-line);padding-left:.7rem;}
|
| 206 |
|
| 207 |
.footer{border-top:1px solid var(--gold-line);padding:2rem 1.5rem;text-align:center;}
|
| 208 |
.footer .note{font-family:var(--mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--dim);}
|
| 209 |
-
.honest{max-width:
|
| 210 |
.honest-box{border:1px solid var(--teal-line);border-radius:8px;background:var(--teal-soft);
|
| 211 |
padding:.9rem 1.1rem;font-size:12px;color:var(--paragraph);line-height:1.6;}
|
| 212 |
.honest-box b{color:var(--teal);}
|
|
@@ -217,66 +223,56 @@
|
|
| 217 |
<body>
|
| 218 |
<div class="ribbon">
|
| 219 |
<span class="live"><span class="live-dot"></span>a11oy</span>
|
| 220 |
-
<span class="sep">·</span><span>Warhacker
|
| 221 |
-
<span class="sep">·</span><span class="teal">in-image · real mechanism · signed · re-verifiable</span>
|
| 222 |
<a href="/console">console →</a>
|
| 223 |
</div>
|
| 224 |
|
| 225 |
<section class="hero">
|
| 226 |
<div class="grid-bg"></div>
|
| 227 |
<div class="hero-inner">
|
| 228 |
-
<span class="eyebrow"><span class="eyebrow-dot"></span><span class="eyebrow-text">
|
| 229 |
-
<h1 class="headline">
|
| 230 |
-
<p class="support">
|
| 231 |
-
an ordered step timeline with real durations and computed values, the catch tree that caught the exact
|
| 232 |
-
line-crossing, a single-byte tamper test that visibly breaks the signed chain,
|
| 233 |
-
behind the fix. CANNONICO is real today; the other four run
|
| 234 |
-
|
| 235 |
-
<button class="runall" id="runAll">▶ Run all five (nominal)</button>
|
| 236 |
<div class="stats">
|
| 237 |
-
<div class="stat"><div class="k">
|
| 238 |
-
<div class="stat"><div class="k">
|
|
|
|
| 239 |
<div class="stat"><div class="k">Tamper caught</div><div class="v teal" id="stTamper">0 / 0</div></div>
|
| 240 |
-
<div class="stat"><div class="k">Backend</div><div class="v teal" id="stBackend">
|
| 241 |
</div>
|
| 242 |
</div>
|
| 243 |
</section>
|
| 244 |
|
| 245 |
-
<div class="tabs-bar" id="tabsBar">
|
| 246 |
-
|
| 247 |
-
<button class="tab-btn" data-tab="tychee"><span class="dot" id="dot-tychee"></span>2 · TYCHEE</button>
|
| 248 |
-
<button class="tab-btn" data-tab="hangar2apps"><span class="dot" id="dot-hangar2apps"></span>3 · HANGAR2APPS</button>
|
| 249 |
-
<button class="tab-btn" data-tab="cyber_rts"><span class="dot" id="dot-cyber_rts"></span>4 · CYBER RTS</button>
|
| 250 |
-
<button class="tab-btn" data-tab="raven"><span class="dot" id="dot-raven"></span>5 · RAVEN</button>
|
| 251 |
-
<button class="tab-btn" data-tab="ai-sbom"><span class="dot" id="dot-ai-sbom"></span>6 · AI-SBOM</button>
|
| 252 |
-
<button class="tab-btn" data-tab="agentic-provenance"><span class="dot" id="dot-agentic-provenance"></span>7 · AGENTIC PROV</button>
|
| 253 |
-
</div>
|
| 254 |
-
|
| 255 |
-
<div class="panel-wrap" id="panelWrap">
|
| 256 |
-
<!-- panels injected by JS from a template, one per problem -->
|
| 257 |
-
</div>
|
| 258 |
|
| 259 |
<div class="honest">
|
| 260 |
<div class="honest-box">
|
| 261 |
-
<b>What is real here.</b> Every demo runs live in this container. CANNONICO is
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
(conditional/CI-green; unconditional is false).
|
|
|
|
| 271 |
</div>
|
| 272 |
</div>
|
| 273 |
|
| 274 |
-
<footer class="footer"><span class="note">a11oy · SZL Holdings · Warhacker demos · in-image · signed · re-verifiable · zero CDN</span></footer>
|
| 275 |
|
|
|
|
| 276 |
<script>
|
| 277 |
(function(){
|
| 278 |
"use strict";
|
| 279 |
-
// SOVEREIGN: no external scripts. API base: /api/a11oy/v1 (Node proxy) with /v1 fallback.
|
| 280 |
var BASES = ["/api/a11oy/v1", "/v1"];
|
| 281 |
function api(path, opts){
|
| 282 |
var i = 0;
|
|
@@ -284,130 +280,544 @@
|
|
| 284 |
return fetch(BASES[i] + path, opts).then(function(r){
|
| 285 |
if(!r.ok && r.status === 404 && i < BASES.length-1){ i++; return attempt(); }
|
| 286 |
return r.json().then(function(j){ return {ok:r.ok, status:r.status, json:j}; });
|
| 287 |
-
}).catch(function(e){
|
| 288 |
-
if(i < BASES.length-1){ i++; return attempt(); }
|
| 289 |
-
throw e;
|
| 290 |
-
});
|
| 291 |
}
|
| 292 |
return attempt();
|
| 293 |
}
|
| 294 |
function getJSON(p){ return api(p, {method:"GET"}); }
|
| 295 |
-
function postJSON(p, body){
|
| 296 |
-
return api(p, {method:"POST", headers:{"Content-Type":"application/json"}, body:JSON.stringify(body||{})});
|
| 297 |
-
}
|
| 298 |
var $ = function(id){ return document.getElementById(id); };
|
| 299 |
function esc(s){ return String(s==null?"":s).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); }
|
| 300 |
function pretty(o){ try{ return JSON.stringify(o, null, 2); }catch(e){ return String(o); } }
|
| 301 |
function compactVal(v){
|
| 302 |
if(v==null) return "";
|
| 303 |
if(typeof v !== "object") return String(v);
|
| 304 |
-
// one-line compact JSON, trimmed
|
| 305 |
var s = JSON.stringify(v);
|
| 306 |
-
return s.length>
|
| 307 |
-
}
|
| 308 |
-
|
| 309 |
-
// ---- the five problems (tab key -> meta) ----
|
| 310 |
-
var PROBLEMS = [
|
| 311 |
-
{key:"cannonico", title:"CANNONICO — AI oversight for autonomous drones",
|
| 312 |
-
blurb:"An autonomous (lost-contact) drone is checked against its full authorized envelope. The 13-axis conjunctive ROE gate, the STL robustness value, the PolyCARP-style geofence and the conformal confidence interval are all computed live, then sealed into a signed, tamper-evident record. Run it nominal, then run the tamper test to see one changed byte break the chain.",
|
| 313 |
-
nominalLabel:"Nominal flight (inside envelope)", tamperLabel:"Line-crossing flight"},
|
| 314 |
-
{key:"tychee", title:"TYCHEE — air-gapped ground-software deploy",
|
| 315 |
-
blurb:"A satellite ground-software bundle (UDS-style, Zarf-packaged) is admitted into an air-gapped cluster only if every OCI layer digest matches the cosign attestation and the Merkle bundle root verifies. The tamper test flips one byte in a layer — the digest no longer matches and admission is blocked.",
|
| 316 |
-
nominalLabel:"Clean bundle", tamperLabel:"Tampered layer"},
|
| 317 |
-
{key:"hangar2apps", title:"HANGAR2APPS — readiness dashboard + audit",
|
| 318 |
-
blurb:"A service member's FHIR R4 record (sample, no PHI) is parsed and run through a 5-axis conjunctive readiness gate. A signed Task attestation is chained for the audit trail. The tamper test deletes the influenza immunization — the same gate flips the member to NON-DEPLOYABLE.",
|
| 319 |
-
nominalLabel:"Medically ready", tamperLabel:"Missing immunization"},
|
| 320 |
-
{key:"cyber_rts", title:"CYBER RTS — trajectory/orbit operational context",
|
| 321 |
-
blurb:"A real ISS TLE is parsed and propagated by our reimplemented SGP4 mean-element propagator; CPA/TCPA against a labelled synthetic object is computed live and run through the collision/accuracy gate. The tamper test ages the TLE epoch by 21 days — the accuracy gate flags it before operational use.",
|
| 322 |
-
nominalLabel:"Fresh TLE", tamperLabel:"Stale epoch (+21d)"},
|
| 323 |
-
{key:"raven", title:"RAVEN — authorize a workload at the disconnected edge",
|
| 324 |
-
blurb:"A workload is admitted on a disconnected edge node only if the TPM quote matches the known-good measurement, the image is cosign-verified, and the mission is authorized (conjunctive admission). The tamper test injects an unknown binary into the IMA PCR — the quote no longer matches and admission is denied.",
|
| 325 |
-
nominalLabel:"Trusted node", tamperLabel:"PCR drift (implant)"},
|
| 326 |
-
{key:"ai-sbom", title:"AI-SBOM — air-gap binding of running model weights",
|
| 327 |
-
blurb:"A CycloneDX 1.6 MLBOM is generated in-image and the running model weights are SHA-256'd. The conjunctive admission gate admits the deployment only if the running weight hash equals the authorized MLBOM hash (one of 5 conjunctive axes) and the MLBOM is DSSE-signed. The tamper test swaps the weights — the running hash no longer matches the authorized MLBOM hash, axis S1 flips FALSE, the deployment is REJECTED and the rejection is logged indelibly to the append-only Merkle chain.",
|
| 328 |
-
nominalLabel:"Authorized weights", tamperLabel:"Shadow model (weight swap)"},
|
| 329 |
-
{key:"agentic-provenance", title:"AGENTIC PROVENANCE — who authorized that action?",
|
| 330 |
-
blurb:"A per-action provenance chain binds an autonomous action to (a) the exact agent version hash, (b) the operator delegation receipt, (c) the inputs hash, and (d) the policy-gate Λ vector — each a DSSE-signed leaf in the append-only chain. Four provenance links are then verified plus an inclusion proof. The tamper test forges the operator delegation — the delegation_chain_valid link FAILS and the action is shown to be non-attributable.",
|
| 331 |
-
nominalLabel:"Authorized delegation", tamperLabel:"Forged delegation"}
|
| 332 |
-
];
|
| 333 |
-
|
| 334 |
-
var state = { run:{}, signed:0, tamperCaught:0, tamperRun:0 };
|
| 335 |
-
|
| 336 |
-
// ---- build panel DOM for each problem ----
|
| 337 |
-
var wrap = $("panelWrap");
|
| 338 |
-
PROBLEMS.forEach(function(p, idx){
|
| 339 |
-
var sec = document.createElement("section");
|
| 340 |
-
sec.className = "panel" + (idx===0?" active":"");
|
| 341 |
-
sec.id = "panel-"+p.key;
|
| 342 |
-
sec.innerHTML =
|
| 343 |
-
'<div class="p-head">'+
|
| 344 |
-
'<div class="p-titlerow">'+
|
| 345 |
-
'<h2 class="p-title">'+esc(p.title)+'</h2>'+
|
| 346 |
-
'<span id="lbl-'+p.key+'"></span>'+
|
| 347 |
-
'</div>'+
|
| 348 |
-
'<p class="p-blurb">'+esc(p.blurb)+'</p>'+
|
| 349 |
-
'<div id="rmnote-'+p.key+'"></div>'+
|
| 350 |
-
'</div>'+
|
| 351 |
-
'<div class="card-controls">'+
|
| 352 |
-
'<button class="r-btn r-btn-primary" data-act="nominal" data-k="'+p.key+'">▶ '+esc(p.nominalLabel)+'</button>'+
|
| 353 |
-
'<button class="r-btn r-btn-danger" data-act="tamper" data-k="'+p.key+'">⚠ Tamper test — '+esc(p.tamperLabel)+'</button>'+
|
| 354 |
-
'</div>'+
|
| 355 |
-
'<div id="verdict-'+p.key+'" class="verdict"></div>'+
|
| 356 |
-
'<div class="block"><div class="block-h"><span class="pillc">step timeline</span> real wall-clock durations · computed values</div>'+
|
| 357 |
-
'<ul class="tl" id="tl-'+p.key+'"><li class="empty">— run a demo to populate the timeline —</li></ul></div>'+
|
| 358 |
-
'<div class="block" id="ctblock-'+p.key+'"><div class="block-h"><span class="pillc">catch tree</span> first failing condition auto-expanded</div>'+
|
| 359 |
-
'<ul class="ct" id="ct-'+p.key+'"><li class="empty">— run a demo —</li></ul></div>'+
|
| 360 |
-
'<div class="block" id="chainselfblock-'+p.key+'"><div class="block-h"><span class="pillc">live chain</span> this run\u2019s own signed chain — re-verified intact</div>'+
|
| 361 |
-
'<div id="chainself-'+p.key+'"><div class="empty">— run a demo to verify the live signed chain —</div></div></div>'+
|
| 362 |
-
'<div class="block" id="tamperblock-'+p.key+'"><div class="block-h"><span class="pillc">tamper test</span> one byte flipped → chain breaks</div>'+
|
| 363 |
-
'<div id="tamper-'+p.key+'"><div class="empty">— run the tamper test to flip one byte and watch the signed chain break —</div></div></div>'+
|
| 364 |
-
'<div class="block"><div class="block-h"><span class="pillc">formula-proof panel</span> exact formulas + honest status</div>'+
|
| 365 |
-
'<ul class="fp" id="fp-'+p.key+'"><li class="empty">— run a demo —</li></ul></div>'+
|
| 366 |
-
'<div class="out">'+
|
| 367 |
-
'<div class="out-label" data-toggle="raw-'+p.key+'"><span>raw signed record (click to expand)</span>'+
|
| 368 |
-
'<span class="out-status"><span class="status-dot" id="dotr-'+p.key+'"></span><span id="status-'+p.key+'">idle</span></span></div>'+
|
| 369 |
-
'<pre class="out-pre" id="raw-'+p.key+'"></pre></div>'+
|
| 370 |
-
'<div class="honesty-note" id="honesty-'+p.key+'"></div>';
|
| 371 |
-
wrap.appendChild(sec);
|
| 372 |
-
});
|
| 373 |
|
| 374 |
-
/
|
| 375 |
-
var
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
|
|
|
|
|
|
| 382 |
});
|
| 383 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 384 |
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 389 |
|
| 390 |
-
function
|
| 391 |
-
|
| 392 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 393 |
}
|
| 394 |
-
function setTabDot(key, st){ var d=$("dot-"+key); if(d) d.className="dot"+(st?(" "+st):""); }
|
| 395 |
|
| 396 |
-
function
|
| 397 |
-
var
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 401 |
}
|
| 402 |
|
| 403 |
-
/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 404 |
function renderTimeline(key, timeline, animate){
|
| 405 |
-
var ul = $("tl-"+key); ul.innerHTML = "";
|
| 406 |
var lis = [];
|
| 407 |
(timeline||[]).forEach(function(t){
|
| 408 |
var li = document.createElement("li");
|
| 409 |
-
|
| 410 |
-
li.className = status;
|
| 411 |
var icon = animate ? "•" : (t.ok ? "✓" : "✕");
|
| 412 |
li.innerHTML =
|
| 413 |
'<span class="ic">'+icon+'</span>'+
|
|
@@ -418,49 +828,37 @@
|
|
| 418 |
ul.appendChild(li); lis.push({li:li, t:t});
|
| 419 |
});
|
| 420 |
if(!animate) return Promise.resolve();
|
| 421 |
-
// animate: pending -> running -> complete/failed in sequence
|
| 422 |
return new Promise(function(resolve){
|
| 423 |
var i = 0;
|
| 424 |
function tick(){
|
| 425 |
if(i >= lis.length){ resolve(); return; }
|
| 426 |
-
var o = lis[i];
|
| 427 |
-
o.li.className
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
var ok = o.t.ok;
|
| 431 |
-
o.li.className = ok ? "complete" : "failed";
|
| 432 |
-
o.li.querySelector(".ic").textContent = ok ? "✓" : "✕";
|
| 433 |
-
i++; tick();
|
| 434 |
-
}, Math.min(380, 120 + (o.t.duration_ms||0)*4));
|
| 435 |
}
|
| 436 |
tick();
|
| 437 |
});
|
| 438 |
}
|
| 439 |
-
|
| 440 |
function renderCatchTree(key, tree, firstFail){
|
| 441 |
-
var ul = $("ct-"+key); ul.innerHTML = "";
|
| 442 |
if(!tree || !tree.length){ ul.innerHTML = '<li class="empty">— no catch tree —</li>'; return; }
|
| 443 |
tree.forEach(function(c){
|
| 444 |
var pass = !!c.pass;
|
| 445 |
var li = document.createElement("li");
|
| 446 |
-
li.className = pass ? "pass" : "fail" + (c.node===firstFail ? " firstfail" : "");
|
| 447 |
var marginTxt = (c.margin!=null) ? ("margin "+c.margin) : (c.detail||"");
|
| 448 |
li.innerHTML =
|
| 449 |
'<span class="mark">'+(pass?"✓":"✕")+'</span>'+
|
| 450 |
'<span class="nm">'+esc(c.node)+'</span>'+
|
| 451 |
(c.node===firstFail ? '<span class="firstbadge">caught here</span>' : '')+
|
| 452 |
'<span class="margin">'+esc(marginTxt)+'</span>';
|
| 453 |
-
|
| 454 |
-
if(c.label){
|
| 455 |
-
var sub = document.createElement("div");
|
| 456 |
sub.style.cssText="font-size:10px;color:var(--muted);width:100%;margin-left:1.6rem;margin-top:-.1rem;";
|
| 457 |
-
sub.textContent
|
| 458 |
-
li.appendChild(sub);
|
| 459 |
-
}
|
| 460 |
ul.appendChild(li);
|
| 461 |
});
|
| 462 |
}
|
| 463 |
-
|
| 464 |
function flipDiff(before, after){
|
| 465 |
if(before==null || after==null) return esc(String(after));
|
| 466 |
var b=String(before), a=String(after), i=0;
|
|
@@ -468,11 +866,9 @@
|
|
| 468 |
var pre=esc(a.slice(0,i)), ch=esc(a.slice(i,i+1)), post=esc(a.slice(i+1));
|
| 469 |
return '<span class="b4">before: '+esc(b)+'</span><br><span class="af">after: '+pre+'<span class="hl">'+ch+'</span>'+post+'</span>';
|
| 470 |
}
|
| 471 |
-
|
| 472 |
function renderChainSelf(key, cs){
|
| 473 |
-
var box = $("chainself-"+key);
|
| 474 |
-
if(!
|
| 475 |
-
if(!cs){ box.innerHTML = ""; return; }
|
| 476 |
var intact = (cs.chain_intact===true) && (cs.merkle_root_matches===true);
|
| 477 |
box.innerHTML =
|
| 478 |
'<div class="tamper-grid">'+
|
|
@@ -482,9 +878,8 @@
|
|
| 482 |
'<div class="tamper-cell"><div class="tk">chain depth</div><div class="tv">'+esc(cs.depth)+'</div></div>'+
|
| 483 |
'</div>';
|
| 484 |
}
|
| 485 |
-
|
| 486 |
-
|
| 487 |
-
var box = $("tamper-"+key);
|
| 488 |
if(!tt){ box.innerHTML = '<div class="empty">— no tamper data —</div>'; return false; }
|
| 489 |
var incl = tt.inclusion || {};
|
| 490 |
var caught = (tt.chain_intact===false) || (tt.merkle_root_matches===false) || (incl.inclusion_valid===false);
|
|
@@ -493,10 +888,10 @@
|
|
| 493 |
? ('TAMPER CAUGHT — flipped 1 byte in receipt #'+(t.tampered_seq!=null?t.tampered_seq:"?")+' field "'+esc(t.field||"?")+'". '+
|
| 494 |
'Chain intact: false (break at receipt #'+(tt.chain_break_at_seq!=null?tt.chain_break_at_seq:"?")+'). '+
|
| 495 |
'Merkle root match: false. Inclusion proof valid: false.')
|
| 496 |
-
: 'No break detected
|
| 497 |
box.innerHTML =
|
| 498 |
'<div class="tamper-summary">'+summary+'</div>'+
|
| 499 |
-
(t.before!=null ? '<div class="tamper-cell"><div class="tk">single-byte flip
|
| 500 |
'<div class="tamper-grid" style="margin-top:.5rem">'+
|
| 501 |
'<div class="tamper-cell"><div class="tk">chain intact</div><div class="tv '+(tt.chain_intact?"good":"bad")+'">'+esc(tt.chain_intact)+'</div></div>'+
|
| 502 |
'<div class="tamper-cell"><div class="tk">chain break at receipt</div><div class="tv '+(tt.chain_break_at_seq!=null?"bad":"good")+'">#'+esc(tt.chain_break_at_seq)+'</div></div>'+
|
|
@@ -507,18 +902,35 @@
|
|
| 507 |
'</div>';
|
| 508 |
return caught;
|
| 509 |
}
|
| 510 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 511 |
function renderFormula(key, panel){
|
| 512 |
-
var ul = $("fp-"+key); ul.innerHTML="";
|
| 513 |
if(!panel || !panel.length){ ul.innerHTML='<li class="empty">— no formula panel —</li>'; return; }
|
| 514 |
panel.forEach(function(f){
|
| 515 |
var li=document.createElement("li");
|
| 516 |
-
var
|
| 517 |
-
|
| 518 |
-
var stHtml = esc(st)
|
| 519 |
.replace(/PROVEN/g,'<b class="proven">PROVEN</b>')
|
| 520 |
.replace(/roadmap/gi,'<b class="roadmap">roadmap</b>')
|
| 521 |
-
.replace(/Conjecture 1/g,'<b class="roadmap">Conjecture 1</b>')
|
|
|
|
| 522 |
li.innerHTML =
|
| 523 |
'<div class="fn">'+esc(f.formula)+(f.role?'<span class="role">'+esc(f.role)+'</span>':'')+'</div>'+
|
| 524 |
(f.expr?'<div class="expr">'+esc(f.expr)+'</div>':'')+
|
|
@@ -527,87 +939,190 @@
|
|
| 527 |
ul.appendChild(li);
|
| 528 |
});
|
| 529 |
}
|
| 530 |
-
|
| 531 |
function setLabel(key, realOrRoadmap){
|
| 532 |
var el=$("lbl-"+key); if(!el) return;
|
| 533 |
var isReal = /REAL TODAY/i.test(realOrRoadmap||"");
|
| 534 |
el.className = isReal ? "label-real" : "label-roadmap";
|
| 535 |
el.textContent = isReal ? "REAL TODAY" : "ROADMAP · substrate real";
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
|
| 539 |
-
else { note.className="roadmap-note"; note.textContent = realOrRoadmap; }
|
| 540 |
-
}
|
| 541 |
}
|
| 542 |
|
| 543 |
-
/
|
| 544 |
-
function runDemo(
|
| 545 |
-
|
| 546 |
-
|
| 547 |
-
|
| 548 |
-
return postJSON("/
|
| 549 |
var d = r.json;
|
|
|
|
| 550 |
if(!d || d.ok===false){
|
| 551 |
-
|
| 552 |
-
$("
|
|
|
|
| 553 |
return d;
|
| 554 |
}
|
| 555 |
-
setLabel(
|
| 556 |
-
|
| 557 |
-
var v=$("verdict-"+
|
| 558 |
var ok = !!d.authorized;
|
| 559 |
-
v.className = "verdict
|
| 560 |
v.innerHTML = '<span>'+(ok?"✓":"✕")+'</span><span>'+esc(d.decision)+' — '+esc(d.headline)+'</span>';
|
| 561 |
-
|
| 562 |
-
|
| 563 |
-
|
| 564 |
-
|
| 565 |
-
|
| 566 |
-
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
// raw
|
| 572 |
-
$("raw-"+key).textContent = pretty(d);
|
| 573 |
-
// honesty
|
| 574 |
-
$("honesty-"+key).textContent = d.honesty || "";
|
| 575 |
// stats
|
| 576 |
-
|
| 577 |
-
if(d.sealed && d.sealed.signed) state.signed++;
|
| 578 |
state.tamperRun++; if(caught) state.tamperCaught++;
|
| 579 |
refreshStats();
|
| 580 |
anim.then(function(){
|
| 581 |
-
|
| 582 |
-
|
| 583 |
});
|
| 584 |
return d;
|
| 585 |
}).catch(function(e){
|
| 586 |
-
|
| 587 |
-
$("raw-"+
|
| 588 |
});
|
| 589 |
}
|
| 590 |
|
| 591 |
-
|
| 592 |
-
|
| 593 |
-
|
| 594 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 595 |
|
| 596 |
-
|
| 597 |
-
|
| 598 |
-
|
| 599 |
-
|
| 600 |
-
var chain = Promise.resolve();
|
| 601 |
-
seq.forEach(function(k){ chain = chain.then(function(){ return runDemo(k,"nominal"); }); });
|
| 602 |
-
chain.then(function(){ btn.disabled=false; btn.textContent="✓ all five ran — open any tab, then try a tamper test"; })
|
| 603 |
-
.catch(function(){ btn.disabled=false; btn.textContent="▶ Run all five (nominal)"; });
|
| 604 |
-
});
|
| 605 |
|
| 606 |
-
/
|
| 607 |
-
|
| 608 |
-
|
| 609 |
-
|
| 610 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 611 |
|
| 612 |
refreshStats();
|
| 613 |
})();
|
|
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8"/>
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
| 6 |
+
<title>a11oy — Warhacker · 25 demos · SZL Holdings</title>
|
| 7 |
+
<meta name="description" content="Twenty-five step-by-step Warhacker demos across five official problems (CANNONICO, TYCHEE, HANGAR2APPS, CYBER-RTS, RAVEN). Each runs a real mechanism in-image: step timeline with real perf_counter durations and computed values, a single-byte tamper test that breaks the signed SHA-256 Merkle chain, an honest formula-proof panel, a signed-receipt verify, and a unique 3D/graph visualization. CANNONICO is real today; the other four run the proven horizontal substrate on clearly labelled sample data."/>
|
| 8 |
+
<!-- SOVEREIGN: zero runtime CDN. System font stack only. Viz libs vendored at /vendor/*. -->
|
| 9 |
<style>
|
| 10 |
:root{
|
| 11 |
--ground:#0a0a0a; --panel:#0c0c0c; --panel2:#070707;
|
|
|
|
| 20 |
--display:'Segoe UI','Helvetica Neue',Arial,system-ui,-apple-system,sans-serif;
|
| 21 |
}
|
| 22 |
*{box-sizing:border-box;}
|
| 23 |
+
html,body{margin:0;padding:0;background:var(--ground);color:var(--cream);overflow-x:hidden;
|
| 24 |
font-family:var(--display);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;}
|
| 25 |
.mono{font-family:var(--mono);}
|
| 26 |
a{color:inherit;}
|
| 27 |
:focus-visible{outline:2px solid var(--gold);outline-offset:3px;border-radius:3px;}
|
| 28 |
+
.ribbon{position:sticky;top:0;z-index:60;display:flex;align-items:center;gap:1.1rem;
|
| 29 |
flex-wrap:wrap;padding:0.5rem 1.25rem;font-family:var(--mono);font-size:10px;
|
| 30 |
letter-spacing:0.12em;text-transform:uppercase;color:var(--gold);
|
| 31 |
background:rgba(10,10,10,0.92);backdrop-filter:blur(10px);
|
|
|
|
| 38 |
.live-dot{width:6px;height:6px;border-radius:50%;background:var(--live);
|
| 39 |
box-shadow:0 0 6px var(--live);animation:pulseDot 2.2s ease-in-out infinite;}
|
| 40 |
@keyframes pulseDot{0%,100%{opacity:1;}50%{opacity:.35;}}
|
| 41 |
+
.hero{position:relative;overflow:hidden;padding:2.6rem 1.5rem 1.6rem;text-align:center;}
|
| 42 |
.grid-bg{position:absolute;inset:0;pointer-events:none;
|
| 43 |
background-image:
|
| 44 |
linear-gradient(rgba(255,255,255,.025) 1px,transparent 1px),
|
|
|
|
| 48 |
mask-image:radial-gradient(ellipse at center,#000 30%,transparent 75%);}
|
| 49 |
.hero-inner{position:relative;z-index:2;max-width:64rem;margin:0 auto;}
|
| 50 |
.eyebrow{display:inline-flex;align-items:center;gap:.5rem;padding:.3rem .85rem;
|
| 51 |
+
border-radius:999px;border:1px solid var(--gold-line);background:var(--gold-soft);margin-bottom:1.2rem;}
|
| 52 |
.eyebrow-dot{width:6px;height:6px;border-radius:50%;background:var(--gold);
|
| 53 |
animation:pulseDot 2.2s ease-in-out infinite;}
|
| 54 |
.eyebrow-text{font-family:var(--mono);font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--gold);}
|
| 55 |
+
.headline{font-size:clamp(2rem,5vw,3.5rem);font-weight:300;line-height:1.05;
|
| 56 |
+
letter-spacing:-.03em;margin:0 0 1rem;color:var(--cream);}
|
| 57 |
.headline .accent{background:linear-gradient(120deg,var(--cream) 20%,var(--gold) 90%);
|
| 58 |
-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:transparent;}
|
| 59 |
+
.support{font-size:15px;line-height:1.62;color:var(--paragraph);max-width:46rem;margin:0 auto 1.4rem;}
|
| 60 |
+
.stats{display:flex;flex-wrap:wrap;justify-content:center;gap:.7rem;max-width:62rem;margin:1.4rem auto 0;}
|
| 61 |
+
.stat{flex:1 1 140px;min-width:130px;border:1px solid var(--gold-line);border-radius:8px;
|
| 62 |
+
background:var(--panel);padding:.8rem 1rem;text-align:left;}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
.stat .k{font-family:var(--mono);font-size:9px;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);}
|
| 64 |
+
.stat .v{font-size:1.25rem;font-weight:600;color:var(--gold);margin-top:.25rem;}
|
| 65 |
.stat .v.teal{color:var(--teal);}
|
|
|
|
| 66 |
|
| 67 |
+
/* problem tabs */
|
| 68 |
+
.tabs-bar{position:sticky;top:33px;z-index:50;display:flex;gap:.35rem;flex-wrap:wrap;justify-content:center;
|
| 69 |
+
padding:.55rem 1rem;background:rgba(10,10,10,.96);backdrop-filter:blur(10px);border-bottom:1px solid var(--gold-line);}
|
| 70 |
+
.tab-btn{font-family:var(--mono);font-size:11px;letter-spacing:.02em;padding:.45rem .8rem;border-radius:7px;
|
| 71 |
border:1px solid var(--gold-line);background:transparent;color:var(--paragraph);cursor:pointer;transition:all .18s ease;}
|
| 72 |
.tab-btn:hover{color:var(--gold);border-color:rgba(201,183,135,.35);}
|
| 73 |
.tab-btn.active{background:var(--gold);color:var(--ground);border-color:var(--gold);font-weight:600;}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
+
.panel-wrap{max-width:64rem;margin:1.4rem auto 3rem;padding:0 1.25rem;}
|
| 76 |
.panel{display:none;}
|
| 77 |
.panel.active{display:block;animation:fade .3s ease;}
|
| 78 |
@keyframes fade{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:none;}}
|
| 79 |
.p-head{margin-bottom:.9rem;}
|
| 80 |
.p-titlerow{display:flex;align-items:center;gap:.7rem;flex-wrap:wrap;margin-bottom:.4rem;}
|
| 81 |
+
.p-title{font-size:1.45rem;font-weight:400;letter-spacing:-.02em;margin:0;color:var(--cream);}
|
| 82 |
.label-real,.label-roadmap{font-family:var(--mono);font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;
|
| 83 |
padding:.2rem .55rem;border-radius:5px;font-weight:600;}
|
| 84 |
.label-real{color:var(--live-bright);border:1px solid rgba(121,199,154,.45);background:rgba(90,138,110,.12);}
|
| 85 |
.label-roadmap{color:var(--amber);border:1px solid rgba(214,178,74,.4);background:rgba(214,178,74,.08);}
|
| 86 |
+
.p-blurb{font-size:12.5px;line-height:1.6;color:var(--paragraph);max-width:48rem;margin:0;}
|
| 87 |
.roadmap-note{font-size:11.5px;line-height:1.55;color:var(--amber);background:rgba(214,178,74,.06);
|
| 88 |
border:1px solid rgba(214,178,74,.25);border-radius:7px;padding:.55rem .8rem;margin:.7rem 0 0;}
|
| 89 |
|
| 90 |
+
/* demo picker (5 per problem) */
|
| 91 |
+
.demo-pick{display:flex;gap:.4rem;flex-wrap:wrap;margin:.9rem 0 .2rem;}
|
| 92 |
+
.demo-chip{flex:1 1 auto;min-width:0;font-family:var(--mono);font-size:10.5px;letter-spacing:.02em;
|
| 93 |
+
padding:.5rem .55rem;border-radius:7px;border:1px solid var(--gold-line);background:transparent;
|
| 94 |
+
color:var(--paragraph);cursor:pointer;transition:all .16s ease;text-align:left;line-height:1.3;}
|
| 95 |
+
.demo-chip:hover{color:var(--gold);border-color:rgba(201,183,135,.35);}
|
| 96 |
+
.demo-chip.sel{background:rgba(201,183,135,.1);border-color:var(--gold);color:var(--gold);font-weight:600;}
|
| 97 |
+
.demo-chip .cid{display:inline-block;color:var(--teal);font-weight:700;margin-right:.35rem;}
|
| 98 |
+
.demo-chip .cdot{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--dim);margin-left:.35rem;vertical-align:middle;}
|
| 99 |
+
.demo-chip .cdot.ok{background:var(--live-bright);box-shadow:0 0 5px var(--live);}
|
| 100 |
+
.demo-chip .cdot.err{background:var(--err-bright);box-shadow:0 0 5px var(--err);}
|
| 101 |
+
|
| 102 |
+
.demo-title{font-size:1.05rem;font-weight:500;color:var(--cream);margin:1.1rem 0 .15rem;letter-spacing:-.01em;}
|
| 103 |
+
.card-controls{display:flex;flex-wrap:wrap;gap:.5rem;margin:.55rem 0 .9rem;}
|
| 104 |
.r-btn{display:inline-flex;align-items:center;gap:.4rem;padding:.5rem 1rem;font-size:11.5px;
|
| 105 |
font-weight:500;font-family:var(--mono);border-radius:6px;border:1px solid var(--gold-line);
|
| 106 |
background:transparent;color:var(--gold);cursor:pointer;letter-spacing:.04em;
|
| 107 |
transition:background .18s ease,border-color .18s ease;}
|
| 108 |
.r-btn:hover{background:rgba(201,183,135,.08);border-color:rgba(201,183,135,.35);}
|
|
|
|
| 109 |
.r-btn:disabled{opacity:.4;cursor:not-allowed;}
|
| 110 |
.r-btn-primary{background:var(--gold);color:var(--ground);border-color:var(--gold);}
|
| 111 |
.r-btn-primary:hover{background:var(--gold-bright);border-color:var(--gold-bright);}
|
| 112 |
.r-btn-danger{color:var(--err-bright);border-color:rgba(176,106,90,.45);}
|
| 113 |
.r-btn-danger:hover{background:rgba(176,106,90,.1);}
|
|
|
|
| 114 |
|
| 115 |
.verdict{display:flex;align-items:center;gap:.5rem;font-family:var(--mono);font-size:12.5px;
|
| 116 |
+
padding:.5rem .8rem;border-radius:7px;margin-bottom:.8rem;font-weight:600;line-height:1.45;}
|
|
|
|
| 117 |
.verdict.ok{color:var(--live-bright);border:1px solid rgba(90,138,110,.45);background:rgba(90,138,110,.1);}
|
| 118 |
.verdict.err{color:var(--err-bright);border:1px solid rgba(176,106,90,.5);background:rgba(176,106,90,.1);}
|
| 119 |
|
|
|
|
| 120 |
.block{border:1px solid var(--gold-line);border-radius:10px;background:var(--panel);
|
| 121 |
padding:.9rem 1rem 1rem;margin-bottom:1rem;}
|
| 122 |
.block-h{font-family:var(--mono);font-size:9.5px;letter-spacing:.13em;text-transform:uppercase;
|
| 123 |
+
color:var(--muted);margin:0 0 .65rem;display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;}
|
| 124 |
.block-h .pillc{color:var(--teal);border:1px solid var(--teal-line);background:var(--teal-soft);
|
| 125 |
border-radius:4px;padding:.05rem .35rem;font-size:9px;}
|
| 126 |
|
| 127 |
+
/* viz */
|
| 128 |
+
.viz-title{font-family:var(--mono);font-size:11px;color:var(--gold);margin:0 0 .55rem;line-height:1.45;}
|
| 129 |
+
.viz-canvas{width:100%;height:300px;max-width:100%;border-radius:8px;background:var(--panel2);
|
| 130 |
+
border:1px solid var(--gold-soft);overflow:hidden;position:relative;}
|
| 131 |
+
@media(max-width:560px){.viz-canvas{height:240px;}}
|
| 132 |
+
.viz-fallback{font-family:var(--mono);font-size:11px;color:var(--paragraph);line-height:1.6;padding:.6rem;}
|
| 133 |
+
.viz-legend{font-family:var(--mono);font-size:9.5px;color:var(--muted);margin-top:.45rem;display:flex;gap:1rem;flex-wrap:wrap;}
|
| 134 |
+
.viz-legend .sw{display:inline-block;width:10px;height:10px;border-radius:2px;margin-right:.3rem;vertical-align:middle;}
|
| 135 |
+
|
| 136 |
/* step timeline */
|
| 137 |
.tl{list-style:none;margin:0;padding:0;}
|
| 138 |
.tl li{display:flex;gap:.65rem;align-items:flex-start;padding:.5rem 0;border-bottom:1px solid var(--gold-soft);}
|
| 139 |
.tl li:last-child{border-bottom:none;}
|
| 140 |
.tl .ic{flex:0 0 auto;width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;
|
| 141 |
font-family:var(--mono);font-size:10px;margin-top:1px;border:1px solid var(--dim);color:var(--dim);background:transparent;}
|
|
|
|
| 142 |
.tl li.running .ic{border-color:var(--gold);color:var(--gold);animation:pulseDot 1s ease-in-out infinite;}
|
| 143 |
.tl li.complete .ic{background:rgba(90,138,110,.18);color:var(--live-bright);border-color:rgba(90,138,110,.5);}
|
| 144 |
.tl li.failed .ic{background:rgba(176,106,90,.18);color:var(--err-bright);border-color:rgba(176,106,90,.55);}
|
|
|
|
| 153 |
/* catch tree */
|
| 154 |
.ct{list-style:none;margin:0;padding:0;}
|
| 155 |
.ct li{font-family:var(--mono);font-size:11.5px;padding:.32rem .6rem;border-radius:5px;margin-bottom:.25rem;
|
| 156 |
+
display:flex;align-items:flex-start;gap:.5rem;border:1px solid transparent;flex-wrap:wrap;}
|
| 157 |
.ct li.pass{color:var(--paragraph);}
|
| 158 |
.ct li.pass .mark{color:var(--live-bright);}
|
| 159 |
.ct li.fail{color:var(--err-bright);background:rgba(176,106,90,.1);border-color:rgba(176,106,90,.4);}
|
|
|
|
| 166 |
.ct .firstbadge{font-size:8.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--err-bright);
|
| 167 |
border:1px solid rgba(224,138,118,.5);border-radius:3px;padding:.02rem .3rem;margin-left:.45rem;}
|
| 168 |
|
| 169 |
+
/* tamper / chain cells */
|
| 170 |
.tamper-grid{display:grid;grid-template-columns:1fr 1fr;gap:.5rem;}
|
| 171 |
@media(max-width:560px){.tamper-grid{grid-template-columns:1fr;}}
|
| 172 |
+
.tamper-cell{border:1px solid var(--gold-soft);border-radius:6px;padding:.5rem .65rem;min-width:0;}
|
| 173 |
.tamper-cell .tk{font-family:var(--mono);font-size:8.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);}
|
| 174 |
.tamper-cell .tv{font-family:var(--mono);font-size:11px;margin-top:.2rem;word-break:break-all;}
|
| 175 |
.tv.bad{color:var(--err-bright);} .tv.good{color:var(--live-bright);}
|
|
|
|
| 180 |
.bytediff .b4{color:var(--paragraph);} .bytediff .af{color:var(--err-bright);}
|
| 181 |
.bytediff .hl{background:rgba(224,138,118,.35);color:#fff;border-radius:2px;padding:0 1px;}
|
| 182 |
|
| 183 |
+
/* receipt verify */
|
| 184 |
+
.receipt{display:flex;align-items:center;gap:.6rem;font-family:var(--mono);font-size:11.5px;flex-wrap:wrap;}
|
| 185 |
+
.receipt .badge{padding:.25rem .6rem;border-radius:5px;font-weight:600;letter-spacing:.05em;}
|
| 186 |
+
.receipt .badge.pass{color:var(--live-bright);border:1px solid rgba(90,138,110,.5);background:rgba(90,138,110,.12);}
|
| 187 |
+
.receipt .badge.fail{color:var(--err-bright);border:1px solid rgba(176,106,90,.5);background:rgba(176,106,90,.12);}
|
| 188 |
+
.receipt .kv{color:var(--muted);}
|
| 189 |
+
|
| 190 |
/* formula panel */
|
| 191 |
.fp{list-style:none;margin:0;padding:0;}
|
| 192 |
.fp li{border:1px solid var(--gold-soft);border-radius:7px;padding:.55rem .7rem;margin-bottom:.5rem;}
|
|
|
|
| 199 |
.fp .status b.roadmap{color:var(--amber);}
|
| 200 |
.fp .where{font-family:var(--mono);font-size:9.5px;color:var(--muted);margin-top:.3rem;}
|
| 201 |
|
|
|
|
| 202 |
.out{position:relative;border:1px solid var(--gold-line);border-radius:8px;background:var(--panel2);overflow:hidden;}
|
| 203 |
.out-label{display:flex;align-items:center;justify-content:space-between;padding:.45rem .85rem;
|
| 204 |
border-bottom:1px solid var(--gold-line);font-family:var(--mono);font-size:9px;
|
| 205 |
letter-spacing:.1em;text-transform:uppercase;color:var(--muted);cursor:pointer;}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
.out-pre{margin:0;padding:.85rem;font-family:var(--mono);font-size:10px;line-height:1.55;
|
| 207 |
color:var(--paragraph);white-space:pre-wrap;word-break:break-word;max-height:340px;overflow-y:auto;
|
| 208 |
scrollbar-width:thin;scrollbar-color:var(--dim) transparent;display:none;}
|
| 209 |
.out-pre.show{display:block;}
|
|
|
|
| 210 |
.honesty-note{font-family:var(--mono);font-size:10.5px;color:var(--teal);line-height:1.55;
|
| 211 |
margin-top:.8rem;border-left:2px solid var(--teal-line);padding-left:.7rem;}
|
| 212 |
|
| 213 |
.footer{border-top:1px solid var(--gold-line);padding:2rem 1.5rem;text-align:center;}
|
| 214 |
.footer .note{font-family:var(--mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--dim);}
|
| 215 |
+
.honest{max-width:64rem;margin:0 auto 2rem;padding:0 1.25rem;}
|
| 216 |
.honest-box{border:1px solid var(--teal-line);border-radius:8px;background:var(--teal-soft);
|
| 217 |
padding:.9rem 1.1rem;font-size:12px;color:var(--paragraph);line-height:1.6;}
|
| 218 |
.honest-box b{color:var(--teal);}
|
|
|
|
| 223 |
<body>
|
| 224 |
<div class="ribbon">
|
| 225 |
<span class="live"><span class="live-dot"></span>a11oy</span>
|
| 226 |
+
<span class="sep">·</span><span>Warhacker · 25 demos</span>
|
| 227 |
+
<span class="sep">·</span><span class="teal">in-image · real mechanism · signed · re-verifiable · zero CDN</span>
|
| 228 |
<a href="/console">console →</a>
|
| 229 |
</div>
|
| 230 |
|
| 231 |
<section class="hero">
|
| 232 |
<div class="grid-bg"></div>
|
| 233 |
<div class="hero-inner">
|
| 234 |
+
<span class="eyebrow"><span class="eyebrow-dot"></span><span class="eyebrow-text">5 problems × 5 demos · live on this Space</span></span>
|
| 235 |
+
<h1 class="headline">Twenty-five proofs.<br/><span class="accent">Step by step. No hollow PASS.</span></h1>
|
| 236 |
+
<p class="support">Pick any of the 25 demos. Each runs a <b>real mechanism</b> inside this container and shows its work:
|
| 237 |
+
an ordered step timeline with real wall-clock durations and computed values, the catch tree that caught the exact
|
| 238 |
+
line-crossing, a single-byte tamper test that visibly breaks the signed SHA-256 Merkle chain, the proven formulas
|
| 239 |
+
behind the fix, a signed-receipt verify, and a unique visualization. CANNONICO is real today; the other four run
|
| 240 |
+
the proven horizontal substrate on clearly labelled sample data.</p>
|
|
|
|
| 241 |
<div class="stats">
|
| 242 |
+
<div class="stat"><div class="k">Problems</div><div class="v" id="stProblems">5</div></div>
|
| 243 |
+
<div class="stat"><div class="k">Demos</div><div class="v" id="stDemos">25</div></div>
|
| 244 |
+
<div class="stat"><div class="k">Demos run</div><div class="v teal" id="stRun">0</div></div>
|
| 245 |
<div class="stat"><div class="k">Tamper caught</div><div class="v teal" id="stTamper">0 / 0</div></div>
|
| 246 |
+
<div class="stat"><div class="k">Backend</div><div class="v teal" id="stBackend">…</div></div>
|
| 247 |
</div>
|
| 248 |
</div>
|
| 249 |
</section>
|
| 250 |
|
| 251 |
+
<div class="tabs-bar" id="tabsBar"></div>
|
| 252 |
+
<div class="panel-wrap" id="panelWrap"></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
|
| 254 |
<div class="honest">
|
| 255 |
<div class="honest-box">
|
| 256 |
+
<b>What is real here.</b> Every demo runs live in this container. <b>CANNONICO is real today</b>: STL robustness,
|
| 257 |
+
PolyCARP-style geofence containment, A* safe-return and a real SHA-256 Merkle/Khipu hash chain with a Rekor-style
|
| 258 |
+
inclusion proof — all computed live, sealed into a DSSE envelope signed by an in-image ECDSA-P256 key (verifiable
|
| 259 |
+
against <a href="/cosign.pub" style="color:var(--teal)">/cosign.pub</a>). Change one byte and the chain breaks on
|
| 260 |
+
screen. The other four problems run the <b>proven horizontal substrate</b> — SHA-256/Merkle integrity, conjunctive
|
| 261 |
+
admission, conformal coverage, reimplemented SGP4 propagation, Byzantine quorum — on clearly labelled sample data;
|
| 262 |
+
labelled ROADMAP because the operational vertical (real registry, real feed, real TPM hardware, production ATO) is
|
| 263 |
+
a fast stand-up, not claimed as done. Locked-proven (zero-sorry Lean): F11 STL, M2 Hash-Chain, CP1 Conformal,
|
| 264 |
+
G1 CPA, S2 Simplex, B1 Byzantine. The trust score (Λ) uniqueness is <b>Conjecture 1</b>
|
| 265 |
+
(conditional/CI-green; unconditional is false). SLSA L1 honest; L2 roadmap. GIN-GRU / PEGASUS are external,
|
| 266 |
+
experimental predictors shown on sample data.
|
| 267 |
</div>
|
| 268 |
</div>
|
| 269 |
|
| 270 |
+
<footer class="footer"><span class="note">a11oy · SZL Holdings · Warhacker · 25 demos · in-image · signed · re-verifiable · zero CDN</span></footer>
|
| 271 |
|
| 272 |
+
<!-- SOVEREIGN: viz libraries vendored locally (NO CDN). Loaded lazily on first use. -->
|
| 273 |
<script>
|
| 274 |
(function(){
|
| 275 |
"use strict";
|
|
|
|
| 276 |
var BASES = ["/api/a11oy/v1", "/v1"];
|
| 277 |
function api(path, opts){
|
| 278 |
var i = 0;
|
|
|
|
| 280 |
return fetch(BASES[i] + path, opts).then(function(r){
|
| 281 |
if(!r.ok && r.status === 404 && i < BASES.length-1){ i++; return attempt(); }
|
| 282 |
return r.json().then(function(j){ return {ok:r.ok, status:r.status, json:j}; });
|
| 283 |
+
}).catch(function(e){ if(i < BASES.length-1){ i++; return attempt(); } throw e; });
|
|
|
|
|
|
|
|
|
|
| 284 |
}
|
| 285 |
return attempt();
|
| 286 |
}
|
| 287 |
function getJSON(p){ return api(p, {method:"GET"}); }
|
| 288 |
+
function postJSON(p, body){ return api(p, {method:"POST", headers:{"Content-Type":"application/json"}, body:JSON.stringify(body||{})}); }
|
|
|
|
|
|
|
| 289 |
var $ = function(id){ return document.getElementById(id); };
|
| 290 |
function esc(s){ return String(s==null?"":s).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); }
|
| 291 |
function pretty(o){ try{ return JSON.stringify(o, null, 2); }catch(e){ return String(o); } }
|
| 292 |
function compactVal(v){
|
| 293 |
if(v==null) return "";
|
| 294 |
if(typeof v !== "object") return String(v);
|
|
|
|
| 295 |
var s = JSON.stringify(v);
|
| 296 |
+
return s.length>240 ? s.slice(0,237)+"…" : s;
|
| 297 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 298 |
|
| 299 |
+
/* ---------- lazy vendored-script loader (zero CDN; from /vendor/*) ---------- */
|
| 300 |
+
var _loaded = {};
|
| 301 |
+
function loadScript(file){
|
| 302 |
+
if(_loaded[file]) return _loaded[file];
|
| 303 |
+
_loaded[file] = new Promise(function(res, rej){
|
| 304 |
+
var s = document.createElement("script");
|
| 305 |
+
s.src = "/vendor/" + file; s.async = true;
|
| 306 |
+
s.onload = function(){ res(true); };
|
| 307 |
+
s.onerror = function(){ rej(new Error("vendor load failed: "+file)); };
|
| 308 |
+
document.head.appendChild(s);
|
| 309 |
});
|
| 310 |
+
return _loaded[file];
|
| 311 |
+
}
|
| 312 |
+
// which vendor files each viz kind needs
|
| 313 |
+
var VIZ_DEPS = {
|
| 314 |
+
vectorfield:["echarts.min.js"], geofence:["echarts.min.js"], scatter_trails:["echarts.min.js"],
|
| 315 |
+
thermal_field:["echarts.min.js"], byzantine:["echarts.min.js"], fusion_ellipse:["echarts.min.js"],
|
| 316 |
+
iq_scatter:["echarts.min.js"], vital_band:["echarts.min.js"], scatter_simplex:["echarts.min.js"],
|
| 317 |
+
httpvfs_console:["echarts.min.js"],
|
| 318 |
+
tamper_dag:["cytoscape.min.js"], cascade_tree:["cytoscape.min.js"],
|
| 319 |
+
globe_conjunction:["three.min.js","globe.gl.min.js"], globe_tube:["three.min.js","globe.gl.min.js"],
|
| 320 |
+
killchain_globe:["three.min.js","globe.gl.min.js"],
|
| 321 |
+
sankey_stl:["d3.min.js","d3-sankey.min.js"], sankey_info:["d3.min.js","d3-sankey.min.js"]
|
| 322 |
+
// boids -> canvas (no dep); waterfall -> svg (no dep);
|
| 323 |
+
// pathgraph/galaxy/mesh_graph/tactical_galaxy -> built-in canvas graph (no dep)
|
| 324 |
+
};
|
| 325 |
+
function ensureDeps(kind){
|
| 326 |
+
var deps = VIZ_DEPS[kind] || [];
|
| 327 |
+
var chain = Promise.resolve();
|
| 328 |
+
deps.forEach(function(f){ chain = chain.then(function(){ return loadScript(f); }); });
|
| 329 |
+
return chain;
|
| 330 |
+
}
|
| 331 |
|
| 332 |
+
var GOLD="#c9b787", TEAL="#5fb3a3", ERR="#e08a76", LIVE="#79c79a", DIM="#555", CREAM="#f5f5f5", PANEL="#070707";
|
| 333 |
+
|
| 334 |
+
/* ---------- the 5 problems / 25 demos (filled from /warhacker/index) ---------- */
|
| 335 |
+
var PROBLEMS = []; // [{key,title,blurb,real_or_roadmap,demos:[{id,title,...}]}]
|
| 336 |
+
var state = { run:{}, tamperCaught:0, tamperRun:0 };
|
| 337 |
+
var sel = {}; // problemKey -> selected demo id
|
| 338 |
+
|
| 339 |
+
/* ============================ VIZ RENDERERS ============================ */
|
| 340 |
+
var _charts = {}; // containerId -> echarts instance / cleanup
|
| 341 |
+
function clearViz(cid){
|
| 342 |
+
var c = $(cid); if(!c) return;
|
| 343 |
+
if(_charts[cid]){ try{ _charts[cid](); }catch(e){} delete _charts[cid]; }
|
| 344 |
+
c.innerHTML = "";
|
| 345 |
+
}
|
| 346 |
+
function vizFallback(cid, viz){
|
| 347 |
+
var c=$(cid); if(!c) return;
|
| 348 |
+
var rows = Object.keys(viz).filter(function(k){return k!=="kind"&&k!=="lib"&&k!=="title";})
|
| 349 |
+
.map(function(k){ return '<div><span style="color:'+TEAL+'">'+esc(k)+'</span>: '+esc(compactVal(viz[k]))+'</div>'; }).join("");
|
| 350 |
+
c.innerHTML = '<div class="viz-fallback">'+rows+'</div>';
|
| 351 |
+
}
|
| 352 |
+
function echartInit(cid){
|
| 353 |
+
var c=$(cid); if(!c||!window.echarts) return null;
|
| 354 |
+
var inst = window.echarts.init(c, null, {renderer:"canvas"});
|
| 355 |
+
_charts[cid] = function(){ try{ inst.dispose(); }catch(e){} window.removeEventListener("resize", rs); };
|
| 356 |
+
var rs = function(){ try{ inst.resize(); }catch(e){} };
|
| 357 |
+
window.addEventListener("resize", rs);
|
| 358 |
+
return inst;
|
| 359 |
+
}
|
| 360 |
+
var ECBASE = { backgroundColor:"transparent", textStyle:{color:"#9a9a9a",fontFamily:"monospace"},
|
| 361 |
+
grid:{left:48,right:18,top:30,bottom:38}, animationDuration:600 };
|
| 362 |
|
| 363 |
+
function renderViz(cid, viz){
|
| 364 |
+
clearViz(cid);
|
| 365 |
+
if(!viz){ return; }
|
| 366 |
+
var kind = viz.kind;
|
| 367 |
+
ensureDeps(kind).then(function(){
|
| 368 |
+
try{ _renderVizImpl(cid, viz); }
|
| 369 |
+
catch(e){ vizFallback(cid, viz); }
|
| 370 |
+
}).catch(function(){ vizFallback(cid, viz); });
|
| 371 |
}
|
|
|
|
| 372 |
|
| 373 |
+
function _renderVizImpl(cid, viz){
|
| 374 |
+
var k = viz.kind;
|
| 375 |
+
switch(k){
|
| 376 |
+
case "vectorfield": return vizVectorField(cid, viz);
|
| 377 |
+
case "geofence": return vizGeofence(cid, viz);
|
| 378 |
+
case "scatter_trails": return vizTrails(cid, viz, "AI confidence", "floor");
|
| 379 |
+
case "scatter_simplex": return vizTrails(cid, viz, "deterioration score", "thr", true);
|
| 380 |
+
case "thermal_field": return vizThermal(cid, viz);
|
| 381 |
+
case "vital_band": return vizVitalBand(cid, viz);
|
| 382 |
+
case "byzantine": return vizByzantine(cid, viz);
|
| 383 |
+
case "fusion_ellipse": return vizFusion(cid, viz);
|
| 384 |
+
case "iq_scatter": return vizIQ(cid, viz);
|
| 385 |
+
case "httpvfs_console": return vizHttpvfs(cid, viz);
|
| 386 |
+
case "tamper_dag": return vizDag(cid, viz);
|
| 387 |
+
case "cascade_tree": return vizCascade(cid, viz);
|
| 388 |
+
case "globe_conjunction":
|
| 389 |
+
case "killchain_globe":
|
| 390 |
+
case "globe_tube": return vizGlobe(cid, viz);
|
| 391 |
+
case "sankey_stl": return vizSankeySTL(cid, viz);
|
| 392 |
+
case "sankey_info": return vizSankeyInfo(cid, viz);
|
| 393 |
+
case "boids": return vizBoids(cid, viz);
|
| 394 |
+
case "waterfall": return vizWaterfall(cid, viz);
|
| 395 |
+
case "pathgraph":
|
| 396 |
+
case "galaxy":
|
| 397 |
+
case "mesh_graph":
|
| 398 |
+
case "tactical_galaxy": return vizGraph(cid, viz);
|
| 399 |
+
default: return vizFallback(cid, viz);
|
| 400 |
+
}
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
/* --- ECharts-based --- */
|
| 404 |
+
function vizVectorField(cid, v){
|
| 405 |
+
var ec = echartInit(cid); if(!ec) return vizFallback(cid, v);
|
| 406 |
+
var pts = (v.series||[]).map(function(s){ return [s.t, s.alt]; });
|
| 407 |
+
var danger = (v.series||[]).filter(function(s){return s.alt>(v.threshold_x||1e9);}).map(function(s){return [s.t,s.alt];});
|
| 408 |
+
ec.setOption(Object.assign({}, ECBASE, {
|
| 409 |
+
xAxis:{name:"t (10Hz)", type:"value", axisLine:{lineStyle:{color:DIM}}, splitLine:{show:false}},
|
| 410 |
+
yAxis:{name:v.y_label||"alt", type:"value", axisLine:{lineStyle:{color:DIM}}, splitLine:{lineStyle:{color:"rgba(255,255,255,.04)"}}},
|
| 411 |
+
series:[
|
| 412 |
+
{type:"line", data:pts, smooth:true, symbol:"circle", symbolSize:7, lineStyle:{color:GOLD,width:2},
|
| 413 |
+
itemStyle:{color:GOLD}, areaStyle:{color:"rgba(201,183,135,.06)"},
|
| 414 |
+
markLine:{silent:true, data:[{yAxis:v.threshold_x, lineStyle:{color:ERR,type:"dashed"}, label:{formatter:"ceiling "+v.threshold_x, color:ERR}}]}},
|
| 415 |
+
{type:"scatter", data:danger, symbolSize:12, itemStyle:{color:ERR}}
|
| 416 |
+
]
|
| 417 |
+
}));
|
| 418 |
+
}
|
| 419 |
+
function vizGeofence(cid, v){
|
| 420 |
+
var ec = echartInit(cid); if(!ec) return vizFallback(cid, v);
|
| 421 |
+
var poly = (v.polygon||[]).map(function(p){return [p[1],p[0]];}); // [lon,lat]
|
| 422 |
+
if(poly.length) poly.push(poly[0]);
|
| 423 |
+
var d = v.drone||{};
|
| 424 |
+
ec.setOption(Object.assign({}, ECBASE, {
|
| 425 |
+
xAxis:{name:"lon", type:"value", scale:true, axisLine:{lineStyle:{color:DIM}}, splitLine:{show:false}},
|
| 426 |
+
yAxis:{name:"lat", type:"value", scale:true, axisLine:{lineStyle:{color:DIM}}, splitLine:{show:false}},
|
| 427 |
+
series:[
|
| 428 |
+
{type:"line", data:poly, lineStyle:{color:ERR,width:2}, areaStyle:{color:"rgba(176,106,90,.12)"}, symbol:"none", name:"keep-out"},
|
| 429 |
+
{type:"scatter", data:[[d.lon,d.lat]], symbolSize:16, symbol:"diamond",
|
| 430 |
+
itemStyle:{color:v.inside?ERR:LIVE}, label:{show:true, formatter:v.inside?"INSIDE (incursion)":"clear", color:v.inside?ERR:LIVE, position:"right"}}
|
| 431 |
+
]
|
| 432 |
+
}));
|
| 433 |
+
}
|
| 434 |
+
function vizTrails(cid, v, yname, thrKey, rising){
|
| 435 |
+
var ec = echartInit(cid); if(!ec) return vizFallback(cid, v);
|
| 436 |
+
var arr = v.conf || v.score || [];
|
| 437 |
+
var data = arr.map(function(y,i){ return [i,y]; });
|
| 438 |
+
var thr = v[thrKey];
|
| 439 |
+
var marks = [];
|
| 440 |
+
if(v.warn_idx!=null) marks.push({xAxis:v.warn_idx, lineStyle:{color:TEAL,type:"dashed"}, label:{formatter:"warn τ", color:TEAL}});
|
| 441 |
+
if(v.actual_idx!=null) marks.push({xAxis:v.actual_idx, lineStyle:{color:ERR}, label:{formatter:"crossing", color:ERR}});
|
| 442 |
+
ec.setOption(Object.assign({}, ECBASE, {
|
| 443 |
+
xAxis:{name:"t", type:"value", axisLine:{lineStyle:{color:DIM}}, splitLine:{show:false}},
|
| 444 |
+
yAxis:{name:yname, type:"value", scale:true, axisLine:{lineStyle:{color:DIM}}, splitLine:{lineStyle:{color:"rgba(255,255,255,.04)"}}},
|
| 445 |
+
series:[{type:"line", data:data, smooth:true, symbol:"circle", symbolSize:6, lineStyle:{color:GOLD,width:2}, itemStyle:{color:GOLD},
|
| 446 |
+
markLine:{silent:true, data:[{yAxis:thr, lineStyle:{color:ERR,type:"dashed"}, label:{formatter:(rising?"face ":"floor ")+thr, color:ERR}}].concat(marks)}}]
|
| 447 |
+
}));
|
| 448 |
+
}
|
| 449 |
+
function vizThermal(cid, v){
|
| 450 |
+
var ec = echartInit(cid); if(!ec) return vizFallback(cid, v);
|
| 451 |
+
var data = (v.temp||[]).map(function(y,i){return [i,y];});
|
| 452 |
+
var marks=[{yAxis:v.limit, lineStyle:{color:ERR,type:"dashed"}, label:{formatter:"limit "+v.limit, color:ERR}}];
|
| 453 |
+
if(v.warn_idx!=null) marks.push({xAxis:v.warn_idx, lineStyle:{color:TEAL,type:"dashed"}, label:{formatter:"W5-3 warn", color:TEAL}});
|
| 454 |
+
ec.setOption(Object.assign({}, ECBASE, {
|
| 455 |
+
xAxis:{name:"t", type:"value", axisLine:{lineStyle:{color:DIM}}, splitLine:{show:false}},
|
| 456 |
+
yAxis:{name:"°C", type:"value", scale:true, axisLine:{lineStyle:{color:DIM}}, splitLine:{lineStyle:{color:"rgba(255,255,255,.04)"}}},
|
| 457 |
+
series:[{type:"line", data:data, smooth:true, symbol:"circle", symbolSize:6, lineStyle:{color:GOLD,width:2}, itemStyle:{color:GOLD},
|
| 458 |
+
areaStyle:{color:"rgba(214,178,74,.06)"}, markLine:{silent:true, data:marks}}]
|
| 459 |
+
}));
|
| 460 |
+
}
|
| 461 |
+
function vizVitalBand(cid, v){
|
| 462 |
+
var ec = echartInit(cid); if(!ec) return vizFallback(cid, v);
|
| 463 |
+
var data=(v.spo2||[]).map(function(y,i){return [i,y];});
|
| 464 |
+
var lo=(v.band||[])[0], hi=(v.band||[])[1];
|
| 465 |
+
ec.setOption(Object.assign({}, ECBASE, {
|
| 466 |
+
xAxis:{name:"session", type:"value", axisLine:{lineStyle:{color:DIM}}, splitLine:{show:false}},
|
| 467 |
+
yAxis:{name:"SpO₂ %", type:"value", scale:true, axisLine:{lineStyle:{color:DIM}}, splitLine:{lineStyle:{color:"rgba(255,255,255,.04)"}}},
|
| 468 |
+
series:[{type:"line", data:data, smooth:true, symbol:"circle", symbolSize:7,
|
| 469 |
+
lineStyle:{color:v.in_interval?GOLD:ERR,width:2}, itemStyle:{color:v.in_interval?GOLD:ERR},
|
| 470 |
+
markLine:{silent:true, data:[
|
| 471 |
+
{yAxis:hi, lineStyle:{color:TEAL,type:"dashed"}, label:{formatter:"CP1 hi "+hi, color:TEAL}},
|
| 472 |
+
{yAxis:lo, lineStyle:{color:TEAL,type:"dashed"}, label:{formatter:"CP1 lo "+lo, color:TEAL}}]}}]
|
| 473 |
+
}));
|
| 474 |
+
}
|
| 475 |
+
function vizByzantine(cid, v){
|
| 476 |
+
var ec = echartInit(cid); if(!ec) return vizFallback(cid, v);
|
| 477 |
+
var w=v.witnesses||[];
|
| 478 |
+
var data=w.map(function(x){return {value:1, name:x.id, itemStyle:{color:x.valid?LIVE:ERR}}; });
|
| 479 |
+
ec.setOption({backgroundColor:"transparent", textStyle:{color:"#9a9a9a",fontFamily:"monospace"},
|
| 480 |
+
grid:{left:90,right:20,top:20,bottom:30},
|
| 481 |
+
xAxis:{type:"value", max:1.4, axisLine:{lineStyle:{color:DIM}}, splitLine:{show:false},
|
| 482 |
+
markLine:{}},
|
| 483 |
+
yAxis:{type:"category", data:w.map(function(x){return x.id;}), axisLine:{lineStyle:{color:DIM}}},
|
| 484 |
+
series:[{type:"bar", data:data, barWidth:"50%",
|
| 485 |
+
markLine:{silent:true, symbol:"none", data:[{xAxis:0}]} }],
|
| 486 |
+
graphic:[{type:"text", right:18, top:8, style:{text:"quorum "+v.quorum+"/"+w.length+" · valid "+v.valid+" · "+(v.reached?"REACHED":"NOT REACHED"),
|
| 487 |
+
fill:v.reached?LIVE:ERR, font:"11px monospace"}}]
|
| 488 |
+
});
|
| 489 |
+
}
|
| 490 |
+
function vizFusion(cid, v){
|
| 491 |
+
var ec = echartInit(cid); if(!ec) return vizFallback(cid, v);
|
| 492 |
+
var sens=v.sensors||[]; var f=v.fused||[0,0]; var a=(v.semi_axes||[1,1]);
|
| 493 |
+
// covariance ellipse (2-sigma) as a polyline
|
| 494 |
+
var ell=[]; for(var th=0; th<=360; th+=12){ var r=th*Math.PI/180;
|
| 495 |
+
ell.push([f[0]+2*a[0]*Math.cos(r), f[1]+2*a[1]*Math.sin(r)]); }
|
| 496 |
+
var pts=sens.map(function(s){ return {value:[s.x,s.y], itemStyle:{color:(s.maha>2?ERR:TEAL)},
|
| 497 |
+
label:{show:true, formatter:s.id+" (m="+s.maha+")", color:(s.maha>2?ERR:"#9a9a9a"), position:"top", fontSize:9}}; });
|
| 498 |
+
ec.setOption(Object.assign({}, ECBASE, {
|
| 499 |
+
xAxis:{type:"value", scale:true, axisLine:{lineStyle:{color:DIM}}, splitLine:{show:false}},
|
| 500 |
+
yAxis:{type:"value", scale:true, axisLine:{lineStyle:{color:DIM}}, splitLine:{show:false}},
|
| 501 |
+
series:[
|
| 502 |
+
{type:"line", data:ell, symbol:"none", lineStyle:{color:GOLD,type:"dashed",width:1.5}, name:"2σ ellipse"},
|
| 503 |
+
{type:"scatter", data:[{value:f, itemStyle:{color:GOLD}, symbolSize:14, symbol:"diamond", label:{show:true,formatter:"fused",color:GOLD,position:"bottom",fontSize:9}}], symbolSize:14},
|
| 504 |
+
{type:"scatter", data:pts, symbolSize:12}
|
| 505 |
+
]
|
| 506 |
+
}));
|
| 507 |
+
}
|
| 508 |
+
function vizIQ(cid, v){
|
| 509 |
+
var ec = echartInit(cid); if(!ec) return vizFallback(cid, v);
|
| 510 |
+
function map(arr){ return (arr||[]).map(function(p){ return Array.isArray(p)?p:[p.i,p.q]; }); }
|
| 511 |
+
ec.setOption(Object.assign({}, ECBASE, {
|
| 512 |
+
legend:{top:4, textStyle:{color:"#9a9a9a"}, data:[v.labelA||"A", v.labelB||"B"]},
|
| 513 |
+
xAxis:{name:"I", type:"value", scale:true, axisLine:{lineStyle:{color:DIM}}, splitLine:{show:false}},
|
| 514 |
+
yAxis:{name:"Q", type:"value", scale:true, axisLine:{lineStyle:{color:DIM}}, splitLine:{show:false}},
|
| 515 |
+
series:[
|
| 516 |
+
{name:v.labelA||"A", type:"scatter", data:map(v.iqA), symbolSize:7, itemStyle:{color:TEAL}},
|
| 517 |
+
{name:v.labelB||"B", type:"scatter", data:map(v.iqB), symbolSize:7, itemStyle:{color:ERR}}
|
| 518 |
+
]
|
| 519 |
+
}));
|
| 520 |
+
}
|
| 521 |
+
function vizHttpvfs(cid, v){
|
| 522 |
+
var ec = echartInit(cid); if(!ec) return vizFallback(cid, v);
|
| 523 |
+
ec.setOption({backgroundColor:"transparent", textStyle:{color:"#9a9a9a",fontFamily:"monospace"},
|
| 524 |
+
grid:{left:50,right:20,top:30,bottom:34},
|
| 525 |
+
xAxis:{type:"category", data:["DB pages","range reqs (O(log n))"], axisLine:{lineStyle:{color:DIM}}},
|
| 526 |
+
yAxis:{type:"value", axisLine:{lineStyle:{color:DIM}}, splitLine:{lineStyle:{color:"rgba(255,255,255,.04)"}}},
|
| 527 |
+
series:[{type:"bar", barWidth:"45%", data:[
|
| 528 |
+
{value:v.db_pages, itemStyle:{color:TEAL}}, {value:v.range_requests, itemStyle:{color:GOLD}}]}],
|
| 529 |
+
graphic:[{type:"text", right:18, top:8, style:{text:"integrity "+(v.integrity?"OK":"FAIL"),
|
| 530 |
+
fill:v.integrity?LIVE:ERR, font:"11px monospace"}}]
|
| 531 |
+
});
|
| 532 |
+
}
|
| 533 |
+
|
| 534 |
+
/* --- cytoscape DAGs --- */
|
| 535 |
+
function vizDag(cid, v){
|
| 536 |
+
var c=$(cid); if(!c||!window.cytoscape) return vizFallback(cid, v);
|
| 537 |
+
var els=[];
|
| 538 |
+
(v.nodes||[]).forEach(function(n){ els.push({data:{id:n.id, label:n.label||n.id,
|
| 539 |
+
cls:(n.tampered?"tamp":(n.cascade?"casc":"ok"))}}); });
|
| 540 |
+
(v.edges||[]).forEach(function(e){ els.push({data:{source:e.s, target:e.t}}); });
|
| 541 |
+
var cy = window.cytoscape({ container:c, elements:els, userZoomingEnabled:false, autoungrabify:true,
|
| 542 |
+
layout:{name:"breadthfirst", directed:true, spacingFactor:1.15, padding:14},
|
| 543 |
+
style:[
|
| 544 |
+
{selector:"node", style:{"background-color":TEAL,"label":"data(label)","color":CREAM,"font-size":"9px",
|
| 545 |
+
"font-family":"monospace","text-valign":"bottom","text-margin-y":4,"width":22,"height":22}},
|
| 546 |
+
{selector:'node[cls="tamp"]', style:{"background-color":ERR,"border-width":3,"border-color":"#fff","width":28,"height":28}},
|
| 547 |
+
{selector:'node[cls="casc"]', style:{"background-color":"#b06a5a"}},
|
| 548 |
+
{selector:"edge", style:{"width":2,"line-color":"#444","target-arrow-color":"#444","target-arrow-shape":"triangle","curve-style":"bezier"}}
|
| 549 |
+
]});
|
| 550 |
+
cy.fit(undefined, 18);
|
| 551 |
+
_charts[cid]=function(){ try{cy.destroy();}catch(e){} };
|
| 552 |
+
}
|
| 553 |
+
function vizCascade(cid, v){
|
| 554 |
+
var c=$(cid); if(!c||!window.cytoscape) return vizFallback(cid, v);
|
| 555 |
+
var gates=v.gates||[]; var halt=v.halted_at;
|
| 556 |
+
var els=[]; var prev=null;
|
| 557 |
+
gates.forEach(function(g,i){
|
| 558 |
+
var id="g"+i; var failed = (g.pass===false)||(halt!=null && (g.name===halt||i===halt));
|
| 559 |
+
els.push({data:{id:id, label:(g.name||("gate"+i)), cls:(failed?"fail":"pass")}});
|
| 560 |
+
if(prev) els.push({data:{source:prev, target:id}});
|
| 561 |
+
prev=id;
|
| 562 |
+
});
|
| 563 |
+
var cy=window.cytoscape({container:c, elements:els, userZoomingEnabled:false, autoungrabify:true,
|
| 564 |
+
layout:{name:"breadthfirst", directed:true, spacingFactor:1.1, padding:14},
|
| 565 |
+
style:[
|
| 566 |
+
{selector:"node", style:{"background-color":LIVE,"label":"data(label)","color":CREAM,"font-size":"9px",
|
| 567 |
+
"font-family":"monospace","text-valign":"bottom","text-margin-y":4,"width":22,"height":22}},
|
| 568 |
+
{selector:'node[cls="fail"]', style:{"background-color":ERR,"border-width":3,"border-color":"#fff","width":28,"height":28}},
|
| 569 |
+
{selector:"edge", style:{"width":2,"line-color":"#444","target-arrow-color":"#444","target-arrow-shape":"triangle","curve-style":"bezier"}}
|
| 570 |
+
]});
|
| 571 |
+
cy.fit(undefined,18);
|
| 572 |
+
_charts[cid]=function(){ try{cy.destroy();}catch(e){} };
|
| 573 |
+
}
|
| 574 |
+
|
| 575 |
+
/* --- globe.gl globes --- */
|
| 576 |
+
function vizGlobe(cid, v){
|
| 577 |
+
var c=$(cid); if(!c||!window.Globe) return vizGlobeCanvas(cid, v);
|
| 578 |
+
var w=c.clientWidth||300, h=c.clientHeight||300;
|
| 579 |
+
var g;
|
| 580 |
+
try{
|
| 581 |
+
g = window.Globe()(c)
|
| 582 |
+
.width(w).height(h)
|
| 583 |
+
.backgroundColor("rgba(0,0,0,0)")
|
| 584 |
+
.showGlobe(true).showAtmosphere(true).atmosphereColor(TEAL).atmosphereAltitude(0.18)
|
| 585 |
+
.globeImageUrl("/vendor/earth-night.jpg");
|
| 586 |
+
}catch(e){ try{c.innerHTML="";}catch(_){}; return vizGlobeCanvas(cid, v); }
|
| 587 |
+
try{ g.controls().autoRotate = true; g.controls().autoRotateSpeed = 0.6; g.controls().enableZoom=false; }catch(e){}
|
| 588 |
+
try{
|
| 589 |
+
function ring(seed, color){
|
| 590 |
+
var pts=[]; for(var i=0;i<=40;i++){ var t=i/40*Math.PI*2;
|
| 591 |
+
pts.push({lat: 45*Math.sin(t+seed), lng: (i*9 - 180), color:color}); }
|
| 592 |
+
return pts;
|
| 593 |
+
}
|
| 594 |
+
var pathsData = [ring(0.3, TEAL), ring(1.1, ERR)];
|
| 595 |
+
g.pathsData(pathsData).pathPointLat("lat").pathPointLng("lng")
|
| 596 |
+
.pathColor(function(p){ return p[0]&&p[0].color?p[0].color:TEAL; })
|
| 597 |
+
.pathStroke(1.5).pathDashLength(0.3).pathDashGap(0.1).pathDashAnimateTime(6000);
|
| 598 |
+
var label = (v.d_cpa_km!=null) ? ("CPA "+v.d_cpa_km+" km @ t+"+(v.t_cpa_s||0)+"s") : (v.title||"");
|
| 599 |
+
var alert = (v.conjunction===true) || (v.blocked && v.blocked.length>0);
|
| 600 |
+
g.pointsData([{lat:20, lng:-30, size:0.6}]).pointColor(function(){return alert?ERR:GOLD;})
|
| 601 |
+
.pointAltitude(0.12).pointRadius(0.6);
|
| 602 |
+
c.setAttribute("data-cpa", label);
|
| 603 |
+
}catch(e){ try{c.innerHTML="";}catch(_){}; return vizGlobeCanvas(cid, v); }
|
| 604 |
+
var rs=function(){ try{ g.width(c.clientWidth).height(c.clientHeight); }catch(e){} };
|
| 605 |
+
window.addEventListener("resize", rs);
|
| 606 |
+
_charts[cid]=function(){ window.removeEventListener("resize",rs); try{ c.innerHTML=""; }catch(e){} };
|
| 607 |
+
}
|
| 608 |
+
|
| 609 |
+
/* --- 2D canvas orbital fallback (sovereign, no WebGL/globe.gl dependency) --- */
|
| 610 |
+
function vizGlobeCanvas(cid, v){
|
| 611 |
+
var c=$(cid); if(!c) return;
|
| 612 |
+
c.innerHTML="";
|
| 613 |
+
var w=c.clientWidth||300, h=c.clientHeight||300;
|
| 614 |
+
var cv=document.createElement("canvas"); cv.width=w; cv.height=h;
|
| 615 |
+
cv.style.width="100%"; cv.style.height="100%"; cv.style.display="block";
|
| 616 |
+
c.appendChild(cv);
|
| 617 |
+
var ctx=cv.getContext("2d");
|
| 618 |
+
var cx=w/2, cy=h/2, R=Math.min(w,h)*0.32;
|
| 619 |
+
var alert = (v.conjunction===true) || (v.blocked && v.blocked.length>0);
|
| 620 |
+
var label = (v.d_cpa_km!=null) ? ("CPA "+v.d_cpa_km+" km @ t+"+(v.t_cpa_s||0)+"s")
|
| 621 |
+
: (v.title||"orbital geometry");
|
| 622 |
+
var ang=0, raf=null;
|
| 623 |
+
function draw(){
|
| 624 |
+
ctx.clearRect(0,0,w,h);
|
| 625 |
+
// earth disc
|
| 626 |
+
var grd=ctx.createRadialGradient(cx-R*0.3,cy-R*0.3,R*0.2,cx,cy,R);
|
| 627 |
+
grd.addColorStop(0,"#13343a"); grd.addColorStop(1,"#05131a");
|
| 628 |
+
ctx.beginPath(); ctx.arc(cx,cy,R,0,Math.PI*2); ctx.fillStyle=grd; ctx.fill();
|
| 629 |
+
ctx.strokeStyle="rgba(95,179,163,0.5)"; ctx.lineWidth=1; ctx.stroke();
|
| 630 |
+
// atmosphere
|
| 631 |
+
ctx.beginPath(); ctx.arc(cx,cy,R+5,0,Math.PI*2);
|
| 632 |
+
ctx.strokeStyle="rgba(95,179,163,0.18)"; ctx.lineWidth=6; ctx.stroke();
|
| 633 |
+
// graticule
|
| 634 |
+
ctx.strokeStyle="rgba(95,179,163,0.12)"; ctx.lineWidth=0.6;
|
| 635 |
+
for(var k=1;k<3;k++){ ctx.beginPath(); ctx.ellipse(cx,cy,R*(k/3),R,0,0,Math.PI*2); ctx.stroke(); }
|
| 636 |
+
ctx.beginPath(); ctx.ellipse(cx,cy,R,R*0.45,0,0,Math.PI*2); ctx.stroke();
|
| 637 |
+
// two orbit rings (tilted ellipses)
|
| 638 |
+
function orbit(rx,ry,rot,color,off){
|
| 639 |
+
ctx.save(); ctx.translate(cx,cy); ctx.rotate(rot);
|
| 640 |
+
ctx.beginPath(); ctx.ellipse(0,0,rx,ry,0,0,Math.PI*2);
|
| 641 |
+
ctx.strokeStyle=color; ctx.lineWidth=1.4; ctx.setLineDash([5,4]); ctx.stroke();
|
| 642 |
+
ctx.setLineDash([]);
|
| 643 |
+
// moving satellite
|
| 644 |
+
var a=ang+off; var sx=Math.cos(a)*rx, sy=Math.sin(a)*ry;
|
| 645 |
+
ctx.beginPath(); ctx.arc(sx,sy,3.2,0,Math.PI*2); ctx.fillStyle=color; ctx.fill();
|
| 646 |
+
ctx.restore();
|
| 647 |
+
}
|
| 648 |
+
orbit(R*1.45,R*0.7,0.4,TEAL,0);
|
| 649 |
+
orbit(R*1.25,R*0.95,-0.6,(alert?ERR:GOLD),Math.PI*0.7);
|
| 650 |
+
// CPA marker
|
| 651 |
+
ctx.fillStyle=alert?ERR:GOLD;
|
| 652 |
+
ctx.font="11px ui-monospace,monospace"; ctx.textAlign="center";
|
| 653 |
+
ctx.fillText(label, cx, h-12);
|
| 654 |
+
if(alert){ ctx.beginPath(); ctx.arc(cx+R*0.5,cy-R*0.2,5,0,Math.PI*2); ctx.fillStyle=ERR; ctx.fill(); }
|
| 655 |
+
ang+=0.012; raf=requestAnimationFrame(draw);
|
| 656 |
+
}
|
| 657 |
+
draw();
|
| 658 |
+
c.setAttribute("data-cpa", label);
|
| 659 |
+
_charts[cid]=function(){ if(raf) cancelAnimationFrame(raf); try{c.innerHTML="";}catch(e){} };
|
| 660 |
+
}
|
| 661 |
+
|
| 662 |
+
/* --- d3-sankey --- */
|
| 663 |
+
function vizSankeySTL(cid, v){
|
| 664 |
+
var c=$(cid); if(!c||!window.d3||!window.d3.sankey) return vizFallback(cid, v);
|
| 665 |
+
var stages=v.stages||[];
|
| 666 |
+
var nodes=stages.map(function(s){return {name:s.name};});
|
| 667 |
+
var links=[]; for(var i=0;i<stages.length-1;i++){
|
| 668 |
+
links.push({source:i, target:i+1, value:Math.max(0.4, Math.abs(stages[i].rho)||1), violated:(stages[i].rho<0)}); }
|
| 669 |
+
drawSankey(c, nodes, links, function(l){ return l.violated?ERR:TEAL; });
|
| 670 |
+
}
|
| 671 |
+
function vizSankeyInfo(cid, v){
|
| 672 |
+
var c=$(cid); if(!c||!window.d3||!window.d3.sankey) return vizFallback(cid, v);
|
| 673 |
+
var fnd=v.findings||[]; var summ=v.summary||[];
|
| 674 |
+
var nodes=[{name:"raw_vitals"},{name:"extractor"},{name:"summary"}];
|
| 675 |
+
var links=[{source:0,target:1,value:Math.max(1,fnd.length)},{source:1,target:2,value:Math.max(1,summ.length)}];
|
| 676 |
+
drawSankey(c, nodes, links, function(){ return TEAL; });
|
| 677 |
+
}
|
| 678 |
+
function drawSankey(c, nodes, links, colorFn){
|
| 679 |
+
var w=c.clientWidth||300, h=c.clientHeight||280;
|
| 680 |
+
var d3=window.d3;
|
| 681 |
+
c.innerHTML="";
|
| 682 |
+
var svg=d3.select(c).append("svg").attr("width",w).attr("height",h).style("display","block");
|
| 683 |
+
var sankey=d3.sankey().nodeWidth(14).nodePadding(16).extent([[8,12],[w-8,h-12]]);
|
| 684 |
+
var graph=sankey({nodes:nodes.map(function(d){return Object.assign({},d);}),
|
| 685 |
+
links:links.map(function(d){return Object.assign({},d);})});
|
| 686 |
+
svg.append("g").selectAll("path").data(graph.links).join("path")
|
| 687 |
+
.attr("d", d3.sankeyLinkHorizontal())
|
| 688 |
+
.attr("stroke", colorFn).attr("stroke-opacity",0.4)
|
| 689 |
+
.attr("stroke-width", function(d){return Math.max(2,d.width);}).attr("fill","none");
|
| 690 |
+
var node=svg.append("g").selectAll("g").data(graph.nodes).join("g");
|
| 691 |
+
node.append("rect").attr("x",function(d){return d.x0;}).attr("y",function(d){return d.y0;})
|
| 692 |
+
.attr("height",function(d){return Math.max(2,d.y1-d.y0);}).attr("width",function(d){return d.x1-d.x0;})
|
| 693 |
+
.attr("fill",GOLD).attr("rx",2);
|
| 694 |
+
node.append("text").attr("x",function(d){return d.x0<w/2?d.x1+5:d.x0-5;})
|
| 695 |
+
.attr("y",function(d){return (d.y0+d.y1)/2;}).attr("dy","0.35em")
|
| 696 |
+
.attr("text-anchor",function(d){return d.x0<w/2?"start":"end";})
|
| 697 |
+
.attr("fill",CREAM).attr("font-size","9px").attr("font-family","monospace")
|
| 698 |
+
.text(function(d){return d.name;});
|
| 699 |
+
}
|
| 700 |
+
|
| 701 |
+
/* --- built-in canvas force graph (pathgraph / galaxy / mesh / tactical) --- */
|
| 702 |
+
function vizGraph(cid, v){
|
| 703 |
+
var c=$(cid); if(!c) return;
|
| 704 |
+
var w=c.clientWidth||300, h=c.clientHeight||300;
|
| 705 |
+
var cv=document.createElement("canvas"); cv.width=w; cv.height=h; cv.style.width="100%"; cv.style.height="100%";
|
| 706 |
+
c.appendChild(cv); var ctx=cv.getContext("2d");
|
| 707 |
+
// normalize nodes/edges from various shapes
|
| 708 |
+
var rawNodes = v.nodes || v.points || v.files || [];
|
| 709 |
+
var nodes = rawNodes.map(function(n,i){
|
| 710 |
+
if(typeof n==="string") return {id:n};
|
| 711 |
+
return Object.assign({id:(n.id||n.name||("n"+i))}, n);
|
| 712 |
+
});
|
| 713 |
+
var idIndex={}; nodes.forEach(function(n,i){ idIndex[n.id]=i; });
|
| 714 |
+
var rawEdges = v.edges || v.links || [];
|
| 715 |
+
var edges = rawEdges.map(function(e){ return {s:(e.s||e.source||e.from), t:(e.t||e.target||e.to)}; })
|
| 716 |
+
.filter(function(e){ return idIndex[e.s]!=null && idIndex[e.t]!=null; });
|
| 717 |
+
var pathSet={}; (v.path||[]).forEach(function(p,i,arr){ if(i<arr.length-1){ pathSet[p+"->"+arr[i+1]]=1; pathSet[arr[i+1]+"->"+p]=1; } });
|
| 718 |
+
var pathNodes={}; (v.path||[]).forEach(function(p){ pathNodes[p]=1; });
|
| 719 |
+
var outlier = v.outlier;
|
| 720 |
+
// circular / seeded layout
|
| 721 |
+
nodes.forEach(function(n,i){
|
| 722 |
+
if(n.lat!=null && n.lon!=null){ n._x=n.lon; n._y=n.lat; }
|
| 723 |
+
else { var a=i/nodes.length*Math.PI*2; n._x=Math.cos(a); n._y=Math.sin(a); }
|
| 724 |
+
});
|
| 725 |
+
// scale to canvas
|
| 726 |
+
var xs=nodes.map(function(n){return n._x;}), ys=nodes.map(function(n){return n._y;});
|
| 727 |
+
var minx=Math.min.apply(null,xs), maxx=Math.max.apply(null,xs);
|
| 728 |
+
var miny=Math.min.apply(null,ys), maxy=Math.max.apply(null,ys);
|
| 729 |
+
var pad=36;
|
| 730 |
+
function sx(x){ return pad + (maxx-minx? (x-minx)/(maxx-minx):0.5)*(w-2*pad); }
|
| 731 |
+
function sy(y){ return (h-pad) - (maxy-miny? (y-miny)/(maxy-miny):0.5)*(h-2*pad); }
|
| 732 |
+
ctx.clearRect(0,0,w,h);
|
| 733 |
+
// edges
|
| 734 |
+
edges.forEach(function(e){
|
| 735 |
+
var a=nodes[idIndex[e.s]], b=nodes[idIndex[e.t]];
|
| 736 |
+
var onPath = pathSet[e.s+"->"+e.t];
|
| 737 |
+
ctx.beginPath(); ctx.moveTo(sx(a._x),sy(a._y)); ctx.lineTo(sx(b._x),sy(b._y));
|
| 738 |
+
ctx.strokeStyle = onPath?GOLD:"rgba(120,120,120,.35)"; ctx.lineWidth = onPath?2.4:1; ctx.stroke();
|
| 739 |
+
});
|
| 740 |
+
// nodes
|
| 741 |
+
nodes.forEach(function(n){
|
| 742 |
+
var col = (n.id===outlier)?ERR : (pathNodes[n.id]?GOLD : TEAL);
|
| 743 |
+
var r = (n.id===outlier||pathNodes[n.id])?6:4;
|
| 744 |
+
ctx.beginPath(); ctx.arc(sx(n._x),sy(n._y),r,0,Math.PI*2); ctx.fillStyle=col; ctx.fill();
|
| 745 |
+
ctx.fillStyle="#9a9a9a"; ctx.font="8px monospace"; ctx.fillText(String(n.id).slice(0,8), sx(n._x)+7, sy(n._y)+3);
|
| 746 |
+
});
|
| 747 |
}
|
| 748 |
|
| 749 |
+
/* --- boids canvas animation --- */
|
| 750 |
+
function vizBoids(cid, v){
|
| 751 |
+
var c=$(cid); if(!c) return;
|
| 752 |
+
var w=c.clientWidth||300, h=c.clientHeight||300;
|
| 753 |
+
var cv=document.createElement("canvas"); cv.width=w; cv.height=h; cv.style.width="100%"; cv.style.height="100%";
|
| 754 |
+
c.appendChild(cv); var ctx=cv.getContext("2d");
|
| 755 |
+
var drones=(v.drones||[]).map(function(d,i){
|
| 756 |
+
return {x:(d.x!=null?d.x:Math.random()*w), y:(d.y!=null?d.y:Math.random()*h),
|
| 757 |
+
vx:(Math.random()-0.5)*1.2, vy:(Math.random()-0.5)*1.2, id:(d.id!=null?d.id:i)};
|
| 758 |
+
});
|
| 759 |
+
if(!drones.length){ for(var i=0;i<8;i++) drones.push({x:Math.random()*w,y:Math.random()*h,vx:(Math.random()-0.5),vy:(Math.random()-0.5),id:i}); }
|
| 760 |
+
// place inside canvas if coords look like world units
|
| 761 |
+
var maxc=Math.max.apply(null, drones.map(function(d){return Math.max(Math.abs(d.x),Math.abs(d.y));}).concat([1]));
|
| 762 |
+
if(maxc>1) drones.forEach(function(d){ d.x=(d.x/maxc)*0.7*w+0.15*w; d.y=(d.y/maxc)*0.7*h+0.15*h; });
|
| 763 |
+
var sep = v.rho_sep||30; var raf;
|
| 764 |
+
function step(){
|
| 765 |
+
ctx.fillStyle="rgba(7,7,7,.35)"; ctx.fillRect(0,0,w,h);
|
| 766 |
+
// boids: cohesion + separation + alignment (light)
|
| 767 |
+
var cx=0,cy=0; drones.forEach(function(d){cx+=d.x;cy+=d.y;}); cx/=drones.length; cy/=drones.length;
|
| 768 |
+
var minD=1e9, pair=null;
|
| 769 |
+
for(var i=0;i<drones.length;i++){ for(var j=i+1;j<drones.length;j++){
|
| 770 |
+
var dx=drones[i].x-drones[j].x, dy=drones[i].y-drones[j].y; var dd=Math.sqrt(dx*dx+dy*dy);
|
| 771 |
+
if(dd<minD){minD=dd;pair=[i,j];}
|
| 772 |
+
}}
|
| 773 |
+
drones.forEach(function(d){
|
| 774 |
+
d.vx += (cx-d.x)*0.0006; d.vy += (cy-d.y)*0.0006;
|
| 775 |
+
drones.forEach(function(o){ if(o!==d){ var dx=d.x-o.x, dy=d.y-o.y; var dd=Math.sqrt(dx*dx+dy*dy)||1;
|
| 776 |
+
if(dd<28){ d.vx += dx/dd*0.06; d.vy += dy/dd*0.06; } }});
|
| 777 |
+
var sp=Math.sqrt(d.vx*d.vx+d.vy*d.vy)||1; var max=1.6; if(sp>max){ d.vx=d.vx/sp*max; d.vy=d.vy/sp*max; }
|
| 778 |
+
d.x+=d.vx; d.y+=d.vy;
|
| 779 |
+
if(d.x<6||d.x>w-6) d.vx*=-1; if(d.y<6||d.y>h-6) d.vy*=-1;
|
| 780 |
+
d.x=Math.max(6,Math.min(w-6,d.x)); d.y=Math.max(6,Math.min(h-6,d.y));
|
| 781 |
+
});
|
| 782 |
+
// draw closest-pair link
|
| 783 |
+
if(pair){ var a=drones[pair[0]], b=drones[pair[1]]; ctx.beginPath(); ctx.moveTo(a.x,a.y); ctx.lineTo(b.x,b.y);
|
| 784 |
+
ctx.strokeStyle = minD<28?ERR:"rgba(95,179,163,.4)"; ctx.lineWidth=1.5; ctx.stroke(); }
|
| 785 |
+
drones.forEach(function(d){ ctx.beginPath(); ctx.arc(d.x,d.y,4,0,Math.PI*2);
|
| 786 |
+
ctx.fillStyle=GOLD; ctx.fill(); });
|
| 787 |
+
raf=requestAnimationFrame(step);
|
| 788 |
+
}
|
| 789 |
+
step();
|
| 790 |
+
_charts[cid]=function(){ if(raf) cancelAnimationFrame(raf); };
|
| 791 |
+
}
|
| 792 |
+
|
| 793 |
+
/* --- waterfall SVG --- */
|
| 794 |
+
function vizWaterfall(cid, v){
|
| 795 |
+
var c=$(cid); if(!c) return;
|
| 796 |
+
var steps=v.steps||[]; var dv=v.diverge_at;
|
| 797 |
+
var rows = steps.map(function(s,i){
|
| 798 |
+
var bad = (s.match===false) || (dv!=null && (s.name===dv||i>=dv));
|
| 799 |
+
return '<div style="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem;font-family:monospace;font-size:11px;">'+
|
| 800 |
+
'<span style="width:110px;color:'+CREAM+'">'+esc(s.name)+'</span>'+
|
| 801 |
+
'<span style="flex:1;height:10px;border-radius:3px;background:'+(bad?ERR:TEAL)+';opacity:.7;"></span>'+
|
| 802 |
+
'<span style="width:70px;color:'+(bad?ERR:DIM)+'">'+esc(s.orig||"")+'</span>'+
|
| 803 |
+
'<span style="width:70px;color:'+(bad?ERR:DIM)+'">'+esc(s.replay||"")+'</span>'+
|
| 804 |
+
'<span style="width:16px;color:'+(bad?ERR:LIVE)+'">'+(bad?"✕":"✓")+'</span>'+
|
| 805 |
+
'</div>';
|
| 806 |
+
}).join("");
|
| 807 |
+
c.innerHTML = '<div style="padding:.7rem;">'+
|
| 808 |
+
'<div style="display:flex;gap:.5rem;font-family:monospace;font-size:8.5px;color:'+DIM+';text-transform:uppercase;letter-spacing:.08em;margin-bottom:.5rem;">'+
|
| 809 |
+
'<span style="width:110px;">span</span><span style="flex:1;">replay overlay</span><span style="width:70px;">orig</span><span style="width:70px;">replay</span><span style="width:16px;">=</span></div>'+
|
| 810 |
+
rows + '</div>';
|
| 811 |
+
}
|
| 812 |
+
/* ============================ end VIZ ============================ */
|
| 813 |
+
|
| 814 |
+
/* ---------- renderers (timeline / catch / chain / tamper / formula / receipt) ---------- */
|
| 815 |
function renderTimeline(key, timeline, animate){
|
| 816 |
+
var ul = $("tl-"+key); if(!ul) return Promise.resolve(); ul.innerHTML = "";
|
| 817 |
var lis = [];
|
| 818 |
(timeline||[]).forEach(function(t){
|
| 819 |
var li = document.createElement("li");
|
| 820 |
+
li.className = animate ? "running" : (t.ok ? "complete" : "failed");
|
|
|
|
| 821 |
var icon = animate ? "•" : (t.ok ? "✓" : "✕");
|
| 822 |
li.innerHTML =
|
| 823 |
'<span class="ic">'+icon+'</span>'+
|
|
|
|
| 828 |
ul.appendChild(li); lis.push({li:li, t:t});
|
| 829 |
});
|
| 830 |
if(!animate) return Promise.resolve();
|
|
|
|
| 831 |
return new Promise(function(resolve){
|
| 832 |
var i = 0;
|
| 833 |
function tick(){
|
| 834 |
if(i >= lis.length){ resolve(); return; }
|
| 835 |
+
var o = lis[i]; o.li.className = "running"; o.li.querySelector(".ic").textContent="•";
|
| 836 |
+
setTimeout(function(){ var ok=o.t.ok; o.li.className= ok?"complete":"failed";
|
| 837 |
+
o.li.querySelector(".ic").textContent= ok?"✓":"✕"; i++; tick(); },
|
| 838 |
+
Math.min(260, 90 + (o.t.duration_ms||0)*3));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 839 |
}
|
| 840 |
tick();
|
| 841 |
});
|
| 842 |
}
|
|
|
|
| 843 |
function renderCatchTree(key, tree, firstFail){
|
| 844 |
+
var ul = $("ct-"+key); if(!ul) return; ul.innerHTML = "";
|
| 845 |
if(!tree || !tree.length){ ul.innerHTML = '<li class="empty">— no catch tree —</li>'; return; }
|
| 846 |
tree.forEach(function(c){
|
| 847 |
var pass = !!c.pass;
|
| 848 |
var li = document.createElement("li");
|
| 849 |
+
li.className = pass ? "pass" : ("fail" + (c.node===firstFail ? " firstfail" : ""));
|
| 850 |
var marginTxt = (c.margin!=null) ? ("margin "+c.margin) : (c.detail||"");
|
| 851 |
li.innerHTML =
|
| 852 |
'<span class="mark">'+(pass?"✓":"✕")+'</span>'+
|
| 853 |
'<span class="nm">'+esc(c.node)+'</span>'+
|
| 854 |
(c.node===firstFail ? '<span class="firstbadge">caught here</span>' : '')+
|
| 855 |
'<span class="margin">'+esc(marginTxt)+'</span>';
|
| 856 |
+
if(c.label){ var sub=document.createElement("div");
|
|
|
|
|
|
|
| 857 |
sub.style.cssText="font-size:10px;color:var(--muted);width:100%;margin-left:1.6rem;margin-top:-.1rem;";
|
| 858 |
+
sub.textContent=c.label; li.appendChild(sub); }
|
|
|
|
|
|
|
| 859 |
ul.appendChild(li);
|
| 860 |
});
|
| 861 |
}
|
|
|
|
| 862 |
function flipDiff(before, after){
|
| 863 |
if(before==null || after==null) return esc(String(after));
|
| 864 |
var b=String(before), a=String(after), i=0;
|
|
|
|
| 866 |
var pre=esc(a.slice(0,i)), ch=esc(a.slice(i,i+1)), post=esc(a.slice(i+1));
|
| 867 |
return '<span class="b4">before: '+esc(b)+'</span><br><span class="af">after: '+pre+'<span class="hl">'+ch+'</span>'+post+'</span>';
|
| 868 |
}
|
|
|
|
| 869 |
function renderChainSelf(key, cs){
|
| 870 |
+
var box = $("chainself-"+key); if(!box) return;
|
| 871 |
+
if(!cs){ box.innerHTML = '<div class="empty">— run a demo —</div>'; return; }
|
|
|
|
| 872 |
var intact = (cs.chain_intact===true) && (cs.merkle_root_matches===true);
|
| 873 |
box.innerHTML =
|
| 874 |
'<div class="tamper-grid">'+
|
|
|
|
| 878 |
'<div class="tamper-cell"><div class="tk">chain depth</div><div class="tv">'+esc(cs.depth)+'</div></div>'+
|
| 879 |
'</div>';
|
| 880 |
}
|
| 881 |
+
function renderTamper(key, tt){
|
| 882 |
+
var box = $("tamper-"+key); if(!box) return false;
|
|
|
|
| 883 |
if(!tt){ box.innerHTML = '<div class="empty">— no tamper data —</div>'; return false; }
|
| 884 |
var incl = tt.inclusion || {};
|
| 885 |
var caught = (tt.chain_intact===false) || (tt.merkle_root_matches===false) || (incl.inclusion_valid===false);
|
|
|
|
| 888 |
? ('TAMPER CAUGHT — flipped 1 byte in receipt #'+(t.tampered_seq!=null?t.tampered_seq:"?")+' field "'+esc(t.field||"?")+'". '+
|
| 889 |
'Chain intact: false (break at receipt #'+(tt.chain_break_at_seq!=null?tt.chain_break_at_seq:"?")+'). '+
|
| 890 |
'Merkle root match: false. Inclusion proof valid: false.')
|
| 891 |
+
: 'No break detected.';
|
| 892 |
box.innerHTML =
|
| 893 |
'<div class="tamper-summary">'+summary+'</div>'+
|
| 894 |
+
(t.before!=null ? '<div class="tamper-cell"><div class="tk">single-byte flip in '+esc(t.field||"")+'</div><div class="bytediff">'+flipDiff(t.before,t.after)+'</div></div>' : '')+
|
| 895 |
'<div class="tamper-grid" style="margin-top:.5rem">'+
|
| 896 |
'<div class="tamper-cell"><div class="tk">chain intact</div><div class="tv '+(tt.chain_intact?"good":"bad")+'">'+esc(tt.chain_intact)+'</div></div>'+
|
| 897 |
'<div class="tamper-cell"><div class="tk">chain break at receipt</div><div class="tv '+(tt.chain_break_at_seq!=null?"bad":"good")+'">#'+esc(tt.chain_break_at_seq)+'</div></div>'+
|
|
|
|
| 902 |
'</div>';
|
| 903 |
return caught;
|
| 904 |
}
|
| 905 |
+
function renderReceipt(key, d){
|
| 906 |
+
var box=$("receipt-"+key); if(!box) return;
|
| 907 |
+
var sealed = d.sealed||{}; var env = sealed.envelope||{};
|
| 908 |
+
var signed = !!sealed.signed;
|
| 909 |
+
var mode = d.mode;
|
| 910 |
+
// honest receipt verification: nominal -> chain self intact = verifiable PASS;
|
| 911 |
+
// tamper of a tamper_dag demo (the demo IS the tamper) -> FAIL by design.
|
| 912 |
+
var cs = d.chain_self||{}; var live = (cs.chain_intact===true && cs.merkle_root_matches===true);
|
| 913 |
+
var pass = signed && live;
|
| 914 |
+
box.innerHTML =
|
| 915 |
+
'<div class="receipt">'+
|
| 916 |
+
'<span class="badge '+(pass?"pass":"fail")+'">'+(pass?"RECEIPT VERIFIED":"RECEIPT INVALID")+'</span>'+
|
| 917 |
+
'<span class="kv">DSSE signed: '+esc(signed)+'</span>'+
|
| 918 |
+
'<span class="kv">payloadType: '+esc(env.payloadType||sealed.payloadType||"in-toto")+'</span>'+
|
| 919 |
+
'<span class="kv">seq #'+esc(sealed.chain_seq!=null?sealed.chain_seq:"–")+'</span>'+
|
| 920 |
+
'<span class="kv">merkle: '+esc(String(sealed.merkle_root||"").slice(0,18))+'…</span>'+
|
| 921 |
+
'</div>';
|
| 922 |
+
}
|
| 923 |
function renderFormula(key, panel){
|
| 924 |
+
var ul = $("fp-"+key); if(!ul) return; ul.innerHTML="";
|
| 925 |
if(!panel || !panel.length){ ul.innerHTML='<li class="empty">— no formula panel —</li>'; return; }
|
| 926 |
panel.forEach(function(f){
|
| 927 |
var li=document.createElement("li");
|
| 928 |
+
var stHtml = esc(String(f.status||""))
|
| 929 |
+
.replace(/LOCKED-PROVEN/g,'<b class="proven">LOCKED-PROVEN</b>')
|
|
|
|
| 930 |
.replace(/PROVEN/g,'<b class="proven">PROVEN</b>')
|
| 931 |
.replace(/roadmap/gi,'<b class="roadmap">roadmap</b>')
|
| 932 |
+
.replace(/Conjecture 1/g,'<b class="roadmap">Conjecture 1</b>')
|
| 933 |
+
.replace(/CI-green/g,'<b class="roadmap">CI-green</b>');
|
| 934 |
li.innerHTML =
|
| 935 |
'<div class="fn">'+esc(f.formula)+(f.role?'<span class="role">'+esc(f.role)+'</span>':'')+'</div>'+
|
| 936 |
(f.expr?'<div class="expr">'+esc(f.expr)+'</div>':'')+
|
|
|
|
| 939 |
ul.appendChild(li);
|
| 940 |
});
|
| 941 |
}
|
|
|
|
| 942 |
function setLabel(key, realOrRoadmap){
|
| 943 |
var el=$("lbl-"+key); if(!el) return;
|
| 944 |
var isReal = /REAL TODAY/i.test(realOrRoadmap||"");
|
| 945 |
el.className = isReal ? "label-real" : "label-roadmap";
|
| 946 |
el.textContent = isReal ? "REAL TODAY" : "ROADMAP · substrate real";
|
| 947 |
+
}
|
| 948 |
+
function setDemoDot(pkey, demoId, st){
|
| 949 |
+
var d=$("cdot-"+pkey+"-"+demoId); if(d) d.className="cdot"+(st?(" "+st):"");
|
|
|
|
|
|
|
| 950 |
}
|
| 951 |
|
| 952 |
+
/* ---------- run one demo ---------- */
|
| 953 |
+
function runDemo(pkey, demoId, mode){
|
| 954 |
+
var statusEl=$("status-"+pkey);
|
| 955 |
+
if(statusEl) statusEl.textContent = mode==="tamper"?"tamper run…":"running…";
|
| 956 |
+
setDemoDot(pkey, demoId, "loading");
|
| 957 |
+
return postJSON("/warhacker/run/"+pkey+"/"+demoId, {mode:mode}).then(function(r){
|
| 958 |
var d = r.json;
|
| 959 |
+
var raw=$("raw-"+pkey); if(raw) raw.textContent = pretty(d);
|
| 960 |
if(!d || d.ok===false){
|
| 961 |
+
setDemoDot(pkey, demoId, "err");
|
| 962 |
+
var v=$("verdict-"+pkey); if(v){ v.className="verdict err"; v.innerHTML='<span>✕</span><span>backend error — see raw record</span>'; }
|
| 963 |
+
if(raw) raw.classList.add("show");
|
| 964 |
return d;
|
| 965 |
}
|
| 966 |
+
setLabel(pkey, d.real_or_roadmap);
|
| 967 |
+
$("demo-title-"+pkey).textContent = d.title || demoId;
|
| 968 |
+
var v=$("verdict-"+pkey);
|
| 969 |
var ok = !!d.authorized;
|
| 970 |
+
v.className = "verdict " + (ok?"ok":"err");
|
| 971 |
v.innerHTML = '<span>'+(ok?"✓":"✕")+'</span><span>'+esc(d.decision)+' — '+esc(d.headline)+'</span>';
|
| 972 |
+
var anim = renderTimeline(pkey, d.timeline, true);
|
| 973 |
+
renderCatchTree(pkey, d.catch_tree, d.first_failing_node);
|
| 974 |
+
renderChainSelf(pkey, d.chain_self);
|
| 975 |
+
var caught = renderTamper(pkey, d.tamper_test);
|
| 976 |
+
renderReceipt(pkey, d);
|
| 977 |
+
renderFormula(pkey, d.formula_panel);
|
| 978 |
+
$("honesty-"+pkey).textContent = d.honesty || "";
|
| 979 |
+
// viz
|
| 980 |
+
var vt=$("viztitle-"+pkey); if(vt) vt.textContent = (d.viz&&d.viz.title)||"";
|
| 981 |
+
renderViz("viz-"+pkey, d.viz);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 982 |
// stats
|
| 983 |
+
state.run[pkey+"/"+demoId]=true;
|
|
|
|
| 984 |
state.tamperRun++; if(caught) state.tamperCaught++;
|
| 985 |
refreshStats();
|
| 986 |
anim.then(function(){
|
| 987 |
+
if(statusEl) statusEl.textContent = ok?"sealed + signed":"line crossed · caught";
|
| 988 |
+
setDemoDot(pkey, demoId, ok?"ok":"err");
|
| 989 |
});
|
| 990 |
return d;
|
| 991 |
}).catch(function(e){
|
| 992 |
+
setDemoDot(pkey, demoId, "err");
|
| 993 |
+
var raw=$("raw-"+pkey); if(raw){ raw.textContent = String(e); raw.classList.add("show"); }
|
| 994 |
});
|
| 995 |
}
|
| 996 |
|
| 997 |
+
function selectDemo(pkey, demoId){
|
| 998 |
+
sel[pkey]=demoId;
|
| 999 |
+
document.querySelectorAll('.demo-chip[data-p="'+pkey+'"]').forEach(function(ch){
|
| 1000 |
+
ch.classList.toggle("sel", ch.dataset.d===demoId);
|
| 1001 |
+
});
|
| 1002 |
+
var prob = PROBLEMS.filter(function(p){return p.key===pkey;})[0];
|
| 1003 |
+
var dm = (prob.demos||[]).filter(function(x){return x.id===demoId;})[0] || {};
|
| 1004 |
+
$("demo-title-"+pkey).textContent = dm.title || demoId;
|
| 1005 |
+
// reset run panels for the newly selected demo
|
| 1006 |
+
var v=$("verdict-"+pkey); if(v){ v.className="verdict"; v.style.display="none"; }
|
| 1007 |
+
}
|
| 1008 |
|
| 1009 |
+
function refreshStats(){
|
| 1010 |
+
$("stRun").textContent = Object.keys(state.run).length;
|
| 1011 |
+
$("stTamper").textContent = state.tamperCaught + " / " + state.tamperRun;
|
| 1012 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1013 |
|
| 1014 |
+
/* ---------- build UI from index ---------- */
|
| 1015 |
+
function buildUI(){
|
| 1016 |
+
var bar=$("tabsBar"); var wrap=$("panelWrap");
|
| 1017 |
+
bar.innerHTML=""; wrap.innerHTML="";
|
| 1018 |
+
PROBLEMS.forEach(function(p, idx){
|
| 1019 |
+
var b=document.createElement("button");
|
| 1020 |
+
b.className="tab-btn"+(idx===0?" active":""); b.dataset.tab=p.key;
|
| 1021 |
+
b.textContent=(idx+1)+" · "+p.shortName;
|
| 1022 |
+
b.addEventListener("click", function(){
|
| 1023 |
+
document.querySelectorAll(".tab-btn").forEach(function(x){x.classList.remove("active");});
|
| 1024 |
+
b.classList.add("active");
|
| 1025 |
+
document.querySelectorAll(".panel").forEach(function(pp){pp.classList.remove("active");});
|
| 1026 |
+
$("panel-"+p.key).classList.add("active");
|
| 1027 |
+
});
|
| 1028 |
+
bar.appendChild(b);
|
| 1029 |
+
|
| 1030 |
+
var sec=document.createElement("section");
|
| 1031 |
+
sec.className="panel"+(idx===0?" active":""); sec.id="panel-"+p.key;
|
| 1032 |
+
var chips = (p.demos||[]).map(function(dm){
|
| 1033 |
+
return '<button class="demo-chip" data-p="'+p.key+'" data-d="'+dm.id+'">'+
|
| 1034 |
+
'<span class="cid">'+esc(dm.id)+'</span>'+esc(dm.short||dm.title||dm.id)+
|
| 1035 |
+
'<span class="cdot" id="cdot-'+p.key+'-'+dm.id+'"></span></button>';
|
| 1036 |
+
}).join("");
|
| 1037 |
+
sec.innerHTML =
|
| 1038 |
+
'<div class="p-head">'+
|
| 1039 |
+
'<div class="p-titlerow"><h2 class="p-title">'+esc(p.title)+'</h2><span id="lbl-'+p.key+'" class="'+(/REAL TODAY/i.test(p.real_or_roadmap)?"label-real":"label-roadmap")+'">'+(/REAL TODAY/i.test(p.real_or_roadmap)?"REAL TODAY":"ROADMAP · substrate real")+'</span></div>'+
|
| 1040 |
+
'<p class="p-blurb">'+esc(p.blurb||"")+'</p>'+
|
| 1041 |
+
(/REAL TODAY/i.test(p.real_or_roadmap)?'':'<div class="roadmap-note">'+esc(p.real_or_roadmap)+'</div>')+
|
| 1042 |
+
'</div>'+
|
| 1043 |
+
'<div class="block-h" style="margin-top:.4rem"><span class="pillc">pick a demo</span> 5 demos · click one, then run nominal or tamper</div>'+
|
| 1044 |
+
'<div class="demo-pick">'+chips+'</div>'+
|
| 1045 |
+
'<div class="demo-title" id="demo-title-'+p.key+'"></div>'+
|
| 1046 |
+
'<div class="card-controls">'+
|
| 1047 |
+
'<button class="r-btn r-btn-primary" data-act="nominal" data-p="'+p.key+'">▶ Run nominal</button>'+
|
| 1048 |
+
'<button class="r-btn r-btn-danger" data-act="tamper" data-p="'+p.key+'">⚠ Run tamper test</button>'+
|
| 1049 |
+
'<span style="font-family:var(--mono);font-size:10px;color:var(--muted);align-self:center" id="status-'+p.key+'">idle</span>'+
|
| 1050 |
+
'</div>'+
|
| 1051 |
+
'<div id="verdict-'+p.key+'" class="verdict" style="display:none"></div>'+
|
| 1052 |
+
'<div class="block"><div class="block-h"><span class="pillc">visualization</span> unique per demo · vendored libs · zero CDN</div>'+
|
| 1053 |
+
'<div class="viz-title" id="viztitle-'+p.key+'"></div>'+
|
| 1054 |
+
'<div class="viz-canvas" id="viz-'+p.key+'"></div></div>'+
|
| 1055 |
+
'<div class="block"><div class="block-h"><span class="pillc">step timeline</span> real perf_counter durations · computed values</div>'+
|
| 1056 |
+
'<ul class="tl" id="tl-'+p.key+'"><li class="empty">— run a demo to populate the timeline —</li></ul></div>'+
|
| 1057 |
+
'<div class="block"><div class="block-h"><span class="pillc">catch tree</span> first failing condition auto-flagged</div>'+
|
| 1058 |
+
'<ul class="ct" id="ct-'+p.key+'"><li class="empty">— run a demo —</li></ul></div>'+
|
| 1059 |
+
'<div class="block"><div class="block-h"><span class="pillc">live chain</span> this run\u2019s own signed chain — re-verified intact</div>'+
|
| 1060 |
+
'<div id="chainself-'+p.key+'"><div class="empty">— run a demo —</div></div></div>'+
|
| 1061 |
+
'<div class="block"><div class="block-h"><span class="pillc">tamper test</span> one byte flipped → chain breaks</div>'+
|
| 1062 |
+
'<div id="tamper-'+p.key+'"><div class="empty">— run a demo; the always-on negative test flips one byte —</div></div></div>'+
|
| 1063 |
+
'<div class="block"><div class="block-h"><span class="pillc">signed receipt</span> DSSE / ECDSA-P256 · verify PASS/FAIL</div>'+
|
| 1064 |
+
'<div id="receipt-'+p.key+'"><div class="empty">— run a demo —</div></div></div>'+
|
| 1065 |
+
'<div class="block"><div class="block-h"><span class="pillc">formula-proof panel</span> exact formulas + honest status</div>'+
|
| 1066 |
+
'<ul class="fp" id="fp-'+p.key+'"><li class="empty">— run a demo —</li></ul></div>'+
|
| 1067 |
+
'<div class="out">'+
|
| 1068 |
+
'<div class="out-label" data-toggle="raw-'+p.key+'"><span>raw signed record (click to expand)</span></div>'+
|
| 1069 |
+
'<pre class="out-pre" id="raw-'+p.key+'"></pre></div>'+
|
| 1070 |
+
'<div class="honesty-note" id="honesty-'+p.key+'"></div>';
|
| 1071 |
+
wrap.appendChild(sec);
|
| 1072 |
+
});
|
| 1073 |
+
|
| 1074 |
+
// bind chip selection
|
| 1075 |
+
document.querySelectorAll(".demo-chip").forEach(function(ch){
|
| 1076 |
+
ch.addEventListener("click", function(){ selectDemo(ch.dataset.p, ch.dataset.d); });
|
| 1077 |
+
});
|
| 1078 |
+
// bind run buttons
|
| 1079 |
+
document.querySelectorAll(".r-btn[data-act]").forEach(function(b){
|
| 1080 |
+
b.addEventListener("click", function(){
|
| 1081 |
+
var pkey=b.dataset.p; var demoId=sel[pkey];
|
| 1082 |
+
if(!demoId){ // default to first demo
|
| 1083 |
+
var prob=PROBLEMS.filter(function(p){return p.key===pkey;})[0];
|
| 1084 |
+
demoId=(prob.demos[0]||{}).id; selectDemo(pkey, demoId);
|
| 1085 |
+
}
|
| 1086 |
+
var vd=$("verdict-"+pkey); if(vd) vd.style.display="flex";
|
| 1087 |
+
runDemo(pkey, demoId, b.dataset.act);
|
| 1088 |
+
});
|
| 1089 |
+
});
|
| 1090 |
+
// raw toggles
|
| 1091 |
+
document.querySelectorAll(".out-label[data-toggle]").forEach(function(el){
|
| 1092 |
+
el.addEventListener("click", function(){ var t=$(el.dataset.toggle); if(t) t.classList.toggle("show"); });
|
| 1093 |
+
});
|
| 1094 |
+
// pre-select first demo per problem
|
| 1095 |
+
PROBLEMS.forEach(function(p){ if(p.demos&&p.demos.length) selectDemo(p.key, p.demos[0].id); });
|
| 1096 |
+
}
|
| 1097 |
+
|
| 1098 |
+
// short tab names + short demo labels (fallbacks; index may already provide)
|
| 1099 |
+
var SHORT = {cannonico:"CANNONICO", tychee:"TYCHEE", hangar2apps:"HANGAR2APPS", cyber_rts:"CYBER-RTS", raven:"RAVEN"};
|
| 1100 |
+
|
| 1101 |
+
/* ---------- load index ---------- */
|
| 1102 |
+
getJSON("/warhacker/index").then(function(r){
|
| 1103 |
+
var j=r.json||{};
|
| 1104 |
+
var probs = j.problems || [];
|
| 1105 |
+
if(!probs.length){ $("stBackend").textContent="—"; return; }
|
| 1106 |
+
PROBLEMS = probs.map(function(p){
|
| 1107 |
+
return {
|
| 1108 |
+
key: p.key, title: p.title || p.key,
|
| 1109 |
+
shortName: SHORT[p.key] || (p.title||p.key).split(/[ \-—]/)[0].toUpperCase(),
|
| 1110 |
+
blurb: p.blurb || p.summary || p.description || "",
|
| 1111 |
+
real_or_roadmap: p.real_or_roadmap || "",
|
| 1112 |
+
demos: (p.demos||[]).map(function(d){
|
| 1113 |
+
return {id:d.id, title:d.title||d.id, short:d.short||d.subtitle||d.title||d.id};
|
| 1114 |
+
})
|
| 1115 |
+
};
|
| 1116 |
+
});
|
| 1117 |
+
var totDemos = PROBLEMS.reduce(function(a,p){return a+(p.demos?p.demos.length:0);},0);
|
| 1118 |
+
$("stProblems").textContent = PROBLEMS.length;
|
| 1119 |
+
$("stDemos").textContent = totDemos;
|
| 1120 |
+
$("stBackend").textContent = (j.ok!==false)?"LIVE":"—";
|
| 1121 |
+
buildUI();
|
| 1122 |
+
}).catch(function(e){
|
| 1123 |
+
$("stBackend").textContent="—";
|
| 1124 |
+
$("panelWrap").innerHTML = '<div class="empty" style="text-align:center;padding:2rem">Could not load the demo index. The backend may still be starting — refresh in a moment.</div>';
|
| 1125 |
+
});
|
| 1126 |
|
| 1127 |
refreshStats();
|
| 1128 |
})();
|
szl_warhacker_demos.py
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|