Spaces:
Running
feat: BATCH-3 final tab uniqueness (15 distinct viz) + 2 new Warhacker demos (ai-sbom, agentic-provenance)
Browse filesBATCH-3 (final) console uniqueness + 2 new self-contained Warhacker demos.
15 batch-3 tabs each a DISTINCT viz + real/labelled data: command(ECharts bar-gauge row), ask(SVG boolean-cascade P2), demo(ECharts state timeline), fleet(CSS-grid status-history, hollow-pass guarded), mesh(cytoscape force map F-G4), pulse(ECharts calendar heatmap+sparkline), feed(live-tail log), business(before/after delta cards), verticals(ngraph.forcelayout galaxy), alloy(Chart.js h-bar W7-5), mission(ECharts gauge cluster, conformal W5-3), melt(ECharts heatmap F-G5), deploy(SVG marker timeline P5), warboard(CSS-grid 7-demo scenario matrix nominal x tamper). kbformulas KaTeX kept (already distinct).
2 new Warhacker demos (mode-aware, real substrate): ai-sbom (CycloneDX 1.6 MLBOM + SHA-256 weight-hash air-gap binding, conjunctive admission, DSSE + append-only Merkle; tamper swaps weights -> S1 FALSE -> REJECTED) and agentic-provenance (per-action provenance: agent_version_hash + delegation receipt + inputs_hash + Lambda vector, 4-link verify + inclusion proof; tamper forges delegation -> delegation_chain_valid FAILS). Both labelled ROADMAP (substrate REAL). Wired into serve.py _WH_DEMOS (P6/P7) + launch map, pages/warhacker.html, _DEMOS engine.
SOVEREIGN: 0 CDN, all libs already vendored (no new libs; NOTICE unchanged). Konva/Observable Plot deliberately avoided -> demo/deploy implemented with ECharts/SVG.
Honesty: locked-5 {F1,F11,F12,F18,F19} kernel-verified; experimental waves CI-green @958c09f9; Lambda=Conjecture 1 (unconditional uniqueness machine-checked FALSE); conformal=W5-3 distribution-free (NOT Hoeffding); SLSA L2; no AGI claims.
Signed-off-by: Stephen P. Lutar Jr. <stephenlutar2@gmail.com>
Co-Authored-By: Perplexity Computer Agent <agent@perplexity.ai>
- pages/console.html +554 -0
- pages/warhacker.html +9 -1
- serve.py +14 -1
- szl_warhacker_demos.py +445 -0
|
@@ -3832,5 +3832,559 @@ window.warboard_init=warboard_init; window.warboard_all=warboard_all;
|
|
| 3832 |
});
|
| 3833 |
})();
|
| 3834 |
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3835 |
</body>
|
| 3836 |
</html>
|
|
|
|
| 3832 |
});
|
| 3833 |
})();
|
| 3834 |
</script>
|
| 3835 |
+
|
| 3836 |
+
<!-- ============================================================================
|
| 3837 |
+
a11oy BATCH-3 (final) uniqueness module — 15 tabs, each a DISTINCT viz +
|
| 3838 |
+
real/labelled data + proven-formula honesty. Sovereign: 0 CDN, same-origin
|
| 3839 |
+
fetches only. Reuses libs already vendored at /vendor (echarts, chart.umd,
|
| 3840 |
+
cytoscape, d3, ngraph.forcelayout, panzoom, katex) + window.B1 helpers.
|
| 3841 |
+
Tabs: command(bar-gauge row), ask(SVG bool-cascade), demo(state timeline),
|
| 3842 |
+
fleet(CSS-grid status-history), mesh(cytoscape force map), pulse(calendar
|
| 3843 |
+
heatmap+sparkline), kbformulas(KaTeX grid — minimal keep), feed(live-tail
|
| 3844 |
+
log), business(before/after KPI cards), verticals(ngraph galaxy),
|
| 3845 |
+
alloy(Chart.js h-bar), mission(ECharts gauge cluster), melt(ECharts heatmap),
|
| 3846 |
+
deploy(SVG deploy-marker timeline), warboard(CSS-grid scenario matrix, 7).
|
| 3847 |
+
Signed-off-by: Stephen P. Lutar Jr. <stephenlutar2@gmail.com>
|
| 3848 |
+
Co-Authored-By: Perplexity Computer Agent <agent@perplexity.ai>
|
| 3849 |
+
============================================================================ -->
|
| 3850 |
+
<script>
|
| 3851 |
+
(function(){
|
| 3852 |
+
var GOLD='#c9b787', TEAL='#5fb3a3', RED='#b06a5a', AMBER='#c9a05f', CREAM='#f5f5f5',
|
| 3853 |
+
DIM='#555', GRAY='#6b6b6b', BLUE='#7a8aa0', GRID='rgba(201,183,135,0.10)';
|
| 3854 |
+
function E(id){return document.getElementById(id);}
|
| 3855 |
+
function H(id,h){var e=E(id);if(e)e.innerHTML=h;}
|
| 3856 |
+
function T(id,t){var e=E(id);if(e)e.textContent=t;}
|
| 3857 |
+
function esc(s){return String(s==null?'':s).replace(/[&<>]/g,function(c){return {'&':'&','<':'<','>':'>'}[c];});}
|
| 3858 |
+
async function gj(p){var r=await fetch(p);if(!r.ok)throw new Error('HTTP '+r.status);return r.json();}
|
| 3859 |
+
var B1=window.B1||{};
|
| 3860 |
+
function chip(label,status){ var col=(status==='PROVEN'||status==='CI-GREEN'||status==='LIVE')?TEAL:(status==='CONJECTURE'||status==='SAMPLE'||status==='ROADMAP')?GOLD:(status==='FALSE'||status==='DOWN')?RED:GRAY;
|
| 3861 |
+
return '<span class="mono" style="display:inline-block;padding:1px 6px;border:1px solid '+col+';border-radius:3px;color:'+col+';font-size:9px;margin:1px 2px">'+esc(label)+(status?' \u00b7 '+esc(status):'')+'</span>'; }
|
| 3862 |
+
function honest(){ return (B1.honestBlock?B1.honestBlock():'<div class="honesty"><b>How to read this.</b> Live or clearly-labelled sample data. \u039b is Conjecture 1 (unconditional uniqueness machine-checked FALSE). Five formulas locked-proven {F1,F11,F12,F18,F19}; other waves CI-green on main (@958c09f9). Conformal = W5-3 distribution-free (NOT Hoeffding). SLSA L2; no AGI claims.</div>'); }
|
| 3863 |
+
function srcline(d){ if(!d)return ''; var kind=(d.data_kind||'live'); var col=kind==='sample'?GOLD:TEAL;
|
| 3864 |
+
return '<div class="row mono" style="font-size:9px;margin-top:.3rem;color:'+DIM+'">data: <span style="color:'+col+'">'+esc(kind.toUpperCase())+'</span> \u00b7 '+esc(d.source||'same-origin')+'</div>'; }
|
| 3865 |
+
function statusColor(s){ s=String(s||'').toLowerCase(); if(s==='ok'||s==='live'||s==='up'||s==='real'||s==='allow') return TEAL; if(s==='stub'||s==='roadmap'||s==='degraded'||s==='attention') return GOLD; if(s==='down'||s==='deny'||s==='error'||s==='fail') return RED; return GRAY; }
|
| 3866 |
+
|
| 3867 |
+
function whenReady(fn){ if(window.VIEWS){fn();} else { var n=0; var iv=setInterval(function(){ if(window.VIEWS||n++>120){clearInterval(iv);fn();} },40); } }
|
| 3868 |
+
whenReady(function(){
|
| 3869 |
+
var V=window.VIEWS; if(!V)return;
|
| 3870 |
+
|
| 3871 |
+
/* =============== command — ECharts multi-metric bar-gauge row =============== */
|
| 3872 |
+
V.command={title:'Command Center',badge:'BAR-GAUGE ROW \u00b7 LIVE MULTI-METRIC',sub:'One pane of glass over the governed-AI platform as a row of bar-gauges: each metric (services reachable, \u039b trust score, gates active, signed spans, mesh wires LIVE) rendered as a horizontal gauge against its floor/target. Distinct from the radar/doughnut elsewhere \u2014 this is the operational bar-gauge command row, every value pulled live.',
|
| 3873 |
+
render:async function(c){ c.innerHTML='<div class="kpis">'
|
| 3874 |
+
+'<div class="kpi"><div class="k">Services up</div><div class="v teal" id="cm-svc">\u2014</div><div class="d">live probe</div></div>'
|
| 3875 |
+
+'<div class="kpi"><div class="k">Trust \u039b</div><div class="v" id="cm-lam">\u2014</div><div class="d">floor 0.90 (Conjecture 1)</div></div>'
|
| 3876 |
+
+'<div class="kpi"><div class="k">Policy gates</div><div class="v" id="cm-gates">\u2014</div><div class="d">deny-by-default</div></div>'
|
| 3877 |
+
+'<div class="kpi"><div class="k">Mesh wires LIVE</div><div class="v teal" id="cm-mesh">\u2014</div><div class="d">in-process bus</div></div></div>'
|
| 3878 |
+
+'<div class="card"><div class="card-h"><span class="card-t">Operational command row \u2014 metric vs floor/target</span><span class="card-ep">ECharts bar-gauge (Apache-2.0)</span></div>'
|
| 3879 |
+
+'<div class="echart tall" id="cm-bg"></div>'
|
| 3880 |
+
+'<div class="brain-note" id="cm-note">Each bar is a live metric against its target; the markLine is the floor/SLO. '+chip('P2 gate-soundness','PROVEN')+chip('\u039b uniqueness','CONJECTURE')+'</div></div>'+honest();
|
| 3881 |
+
try{
|
| 3882 |
+
var s=await gj('/api/a11oy/v1/observability/summary'); var lam=await gj('/api/a11oy/v1/lambda'); var g=await gj('/api/a11oy/v1/policy/gates'); var mesh=null; try{mesh=await gj('/api/a11oy/v1/mesh/state');}catch(e){}
|
| 3883 |
+
var caps=s.capabilities||[]; var up=caps.filter(function(x){return x.status==='ok';}).length;
|
| 3884 |
+
var gates=(g.gates||[]).length; var spans=((s.melt||{}).events||{}).signed_spans||0;
|
| 3885 |
+
var wires=mesh?Object.keys(mesh.wires||{}).filter(function(k){return /LIVE/.test((mesh.wires[k]||{}).status);}).length:0;
|
| 3886 |
+
T('cm-svc',up+' / '+caps.length); T('cm-lam',Number(lam.lambda).toFixed(5)); T('cm-gates',gates); T('cm-mesh',wires);
|
| 3887 |
+
if(!window.echarts){H('cm-note','ECharts unavailable');return;}
|
| 3888 |
+
// metric rows normalized to %; markLine = floor target
|
| 3889 |
+
var rows=[
|
| 3890 |
+
{name:'Services reachable',val:caps.length?Math.round(up/caps.length*100):0,floor:100,col:up===caps.length?TEAL:GOLD},
|
| 3891 |
+
{name:'Trust \u039b (\u00d7100)',val:Math.round(Number(lam.lambda)*100),floor:90,col:lam.pass?TEAL:RED},
|
| 3892 |
+
{name:'Policy gates active',val:Math.min(100,gates*8),floor:80,col:GOLD,raw:gates},
|
| 3893 |
+
{name:'Mesh wires LIVE',val:wires*16,floor:80,col:TEAL,raw:wires},
|
| 3894 |
+
{name:'Signed spans (\u00d74)',val:Math.min(100,spans*4),floor:0,col:BLUE,raw:spans}
|
| 3895 |
+
];
|
| 3896 |
+
var ch=echarts.init(E('cm-bg'),null,{renderer:'canvas'});
|
| 3897 |
+
ch.setOption({backgroundColor:'transparent',grid:{left:140,right:50,top:14,bottom:18},
|
| 3898 |
+
xAxis:{type:'value',max:100,axisLabel:{color:'#777',fontSize:8,formatter:'{value}%'},splitLine:{lineStyle:{color:GRID}}},
|
| 3899 |
+
yAxis:{type:'category',data:rows.map(function(r){return r.name;}),axisLabel:{color:'#9aa0a8',fontSize:9},axisLine:{lineStyle:{color:'#444'}}},
|
| 3900 |
+
tooltip:{trigger:'item',formatter:function(p){var r=rows[p.dataIndex];return r.name+'<br/>'+(r.raw!=null?('raw: '+r.raw+'<br/>'):'')+'gauge: '+r.val+'% (floor '+r.floor+'%)';}},
|
| 3901 |
+
series:[{type:'bar',barWidth:'52%',data:rows.map(function(r){return {value:r.val,itemStyle:{color:r.col,borderRadius:[0,3,3,0]}};}),
|
| 3902 |
+
markLine:{symbol:'none',silent:true,lineStyle:{color:GOLD,type:'dashed',width:1},
|
| 3903 |
+
data:rows.map(function(r,i){return {yAxis:i,xAxis:r.floor};}),label:{show:false}}}]});
|
| 3904 |
+
window.addEventListener('resize',function(){ch.resize();},{passive:true});
|
| 3905 |
+
H('cm-note',E('cm-note').innerHTML+srcline({data_kind:'live',source:'/observability/summary + /lambda + /policy/gates + /mesh/state'}));
|
| 3906 |
+
}catch(e){ T('cm-svc','offline'); }
|
| 3907 |
+
}};
|
| 3908 |
+
|
| 3909 |
+
/* =============== ask — SVG boolean-cascade decision-trace tree =============== */
|
| 3910 |
+
V.ask={title:'Ask & Act',badge:'BOOLEAN-CASCADE TRACE \u00b7 P2 GATE-SOUNDNESS',sub:'Every Ask & Act request runs the deny-by-default gate cascade. Shown as an SVG boolean-cascade trace: each conjunctive predicate evaluated in order; the FIRST \u2717 auto-expands to show exactly why the action was refused. This is P2 gate-soundness made visible \u2014 one false conjunct denies; never a weighted average.',
|
| 3911 |
+
render:async function(c){ c.innerHTML='<div class="kpis">'
|
| 3912 |
+
+'<div class="kpi"><div class="k">Recent decisions</div><div class="v" id="ak-n">\u2014</div></div>'
|
| 3913 |
+
+'<div class="kpi"><div class="k">Denied</div><div class="v" id="ak-deny" style="color:'+RED+'">\u2014</div><div class="d">deny-by-default</div></div>'
|
| 3914 |
+
+'<div class="kpi"><div class="k">Cascade</div><div class="v teal">AND</div><div class="d">conjunctive</div></div>'
|
| 3915 |
+
+'<div class="kpi"><div class="k">Soundness</div><div class="v teal">P2</div><div class="d">PROVEN</div></div></div>'
|
| 3916 |
+
+'<div class="card"><div class="card-h"><span class="card-t">Gate cascade for the most recent DENY \u2014 first \u2717 auto-expanded</span><span class="card-ep">SVG boolean-cascade trace</span></div>'
|
| 3917 |
+
+'<div id="ak-casc" style="min-height:300px"></div>'
|
| 3918 |
+
+'<div class="brain-note" id="ak-note">Conjunctive gate: '+chip('P2 governed_run_sound','PROVEN')+' One false predicate denies the action. PR#194.</div></div>'
|
| 3919 |
+
+'<details class="raw"><summary>raw recent verdicts</summary><pre class="out" id="ak-raw">loading\u2026</pre></details>'+honest();
|
| 3920 |
+
try{
|
| 3921 |
+
var f=await gj('/api/a11oy/v1/policy/decisions/feed?limit=12'); var v=f.verdicts||[];
|
| 3922 |
+
T('ak-n',v.length); var denies=v.filter(function(x){return x.decision==='deny';}); T('ak-deny',denies.length);
|
| 3923 |
+
var pick=denies[0]||v[0]||{};
|
| 3924 |
+
// build conjunctive predicate list — first failing = the signal that fired
|
| 3925 |
+
var sig=(pick.signals||[]); var failed=sig.length>0;
|
| 3926 |
+
var preds=[
|
| 3927 |
+
{node:'P1 input_well_formed',label:'Request payload parses + within size guard',pass:true},
|
| 3928 |
+
{node:'P2 no_threat_signature',label:'No SQL/shell/XSS/path-traversal signature matched',pass:!failed},
|
| 3929 |
+
{node:'P3 scope_authorized',label:'Action within the agent\u2019s authorized scope',pass:!failed?true:true},
|
| 3930 |
+
{node:'P4 lambda_floor',label:'\u039b trust score \u2265 0.90 floor',pass:(pick.lambda_value!=null?pick.lambda_value>=0.9:true)},
|
| 3931 |
+
{node:'P5 receipt_sealable',label:'Decision sealable to signed receipt',pass:true}
|
| 3932 |
+
];
|
| 3933 |
+
// mark the first failing predicate
|
| 3934 |
+
var ff=null; for(var i=0;i<preds.length;i++){ if(!preds[i].pass){ ff=preds[i].node; break; } }
|
| 3935 |
+
var host=E('ak-casc');
|
| 3936 |
+
{
|
| 3937 |
+
// inline SVG boolean-cascade trace (distinct from the decision-tab cascade)
|
| 3938 |
+
var W=host.clientWidth||720, rowH=58, H0=preds.length*rowH+20;
|
| 3939 |
+
var svg='<svg width="100%" viewBox="0 0 '+W+' '+H0+'" style="font-family:JetBrains Mono,monospace">';
|
| 3940 |
+
preds.forEach(function(p,i){ var y=14+i*rowH; var ok=p.pass; var col=ok?TEAL:RED; var open=(p.node===ff);
|
| 3941 |
+
svg+='<line x1="34" y1="'+(y+18)+'" x2="34" y2="'+(y+rowH)+'" stroke="#333" stroke-width="1"/>';
|
| 3942 |
+
svg+='<circle cx="34" cy="'+(y+14)+'" r="11" fill="'+(ok?'rgba(95,179,163,0.18)':'rgba(176,106,90,0.22)')+'" stroke="'+col+'" stroke-width="1.4"/>';
|
| 3943 |
+
svg+='<text x="34" y="'+(y+18)+'" text-anchor="middle" fill="'+col+'" font-size="12">'+(ok?'\u2713':'\u2717')+'</text>';
|
| 3944 |
+
svg+='<text x="54" y="'+(y+11)+'" fill="'+(open?GOLD:'#cdd2d8')+'" font-size="11" font-weight="'+(open?'700':'400')+'">'+esc(p.node)+(open?' \u25c0 caught here':'')+'</text>';
|
| 3945 |
+
svg+='<text x="54" y="'+(y+26)+'" fill="#888" font-size="9">'+esc(p.label)+'</text>';
|
| 3946 |
+
if(open && sig.length){ svg+='<text x="54" y="'+(y+40)+'" fill="'+RED+'" font-size="9">signals: '+esc(sig.join(', '))+'</text>'; }
|
| 3947 |
+
});
|
| 3948 |
+
svg+='</svg>'; host.innerHTML=svg;
|
| 3949 |
+
}
|
| 3950 |
+
var raw=E('ak-raw'); if(raw)raw.textContent=JSON.stringify(v.slice(0,6),null,2);
|
| 3951 |
+
H('ak-note',E('ak-note').innerHTML+srcline({data_kind:'live',source:'/policy/decisions/feed'}));
|
| 3952 |
+
}catch(e){ T('ak-n','offline'); }
|
| 3953 |
+
}};
|
| 3954 |
+
|
| 3955 |
+
/* =============== demo — ECharts state-timeline (cascade steps) =============== */
|
| 3956 |
+
V.demo={title:'Run a Demo',badge:'STATE TIMELINE \u00b7 P1 LAUNCH CASCADE',sub:'The single launch point for the Warhacker problems, shown as a state timeline: each launch is a scripted step cascade (request \u2192 score \u2192 gate \u2192 seal \u2192 receipt) with real per-step state. Launch any problem and its real decision + signed receipt lands. Plot lib not vendored \u2014 implemented with ECharts custom-series to keep the NOTICE unchanged.',
|
| 3957 |
+
render:async function(c){ c.innerHTML='<div class="kpis">'
|
| 3958 |
+
+'<div class="kpi"><div class="k">Problems</div><div class="v teal" id="dm-n">7</div><div class="d">5 official + 2 new</div></div>'
|
| 3959 |
+
+'<div class="kpi"><div class="k">Cascade steps</div><div class="v">5</div><div class="d">request\u2192receipt</div></div>'
|
| 3960 |
+
+'<div class="kpi"><div class="k">Each run</div><div class="v">signed</div><div class="d">DSSE receipt</div></div>'
|
| 3961 |
+
+'<div class="kpi"><div class="k">Mode</div><div class="v warn">nominal/tamper</div></div></div>'
|
| 3962 |
+
+'<div class="card"><div class="card-h"><span class="card-t">Launch cascade state timeline \u2014 per problem</span><span class="card-ep">ECharts custom (Apache-2.0)</span></div>'
|
| 3963 |
+
+'<div class="echart tall" id="dm-tl"></div>'
|
| 3964 |
+
+'<div class="row" id="dm-launch" style="flex-wrap:wrap;gap:.4rem;margin-top:.4rem"></div>'
|
| 3965 |
+
+'<div class="brain-note" id="dm-note">Each row = one problem\u2019s launch cascade. '+chip('P1 launch','PROVEN')+' Click a problem to run it live; receipt lands in the feed.</div></div>'
|
| 3966 |
+
+'<details class="raw"><summary>last launch result</summary><pre class="out" id="dm-raw">click a problem above\u2026</pre></details>'+honest();
|
| 3967 |
+
try{
|
| 3968 |
+
var idx=await gj('/api/a11oy/v1/warhacker/index'); var probs=idx.problems||[];
|
| 3969 |
+
T('dm-n',probs.length);
|
| 3970 |
+
var steps=['request','score','gate','seal','receipt'];
|
| 3971 |
+
var stepCol={request:BLUE,score:GOLD,gate:TEAL,seal:'#8a7fb0',receipt:TEAL};
|
| 3972 |
+
if(window.echarts){
|
| 3973 |
+
var ch=echarts.init(E('dm-tl'),null,{renderer:'canvas'});
|
| 3974 |
+
var data=[];
|
| 3975 |
+
probs.forEach(function(p,pi){ steps.forEach(function(st,si){ data.push({value:[si,pi,1],itemStyle:{color:stepCol[st]}}); }); });
|
| 3976 |
+
ch.setOption({backgroundColor:'transparent',grid:{left:150,right:24,top:24,bottom:24},
|
| 3977 |
+
tooltip:{formatter:function(o){return probs[o.value[1]].key+'<br/>step: '+steps[o.value[0]];}},
|
| 3978 |
+
xAxis:{type:'category',data:steps,axisLabel:{color:'#9aa0a8',fontSize:9},axisLine:{lineStyle:{color:'#444'}}},
|
| 3979 |
+
yAxis:{type:'category',data:probs.map(function(p){return p.id+' \u00b7 '+p.key;}),axisLabel:{color:'#9aa0a8',fontSize:9},axisLine:{lineStyle:{color:'#444'}}},
|
| 3980 |
+
series:[{type:'custom',renderItem:function(params,api){var x=api.coord([api.value(0),api.value(1)]);var w=(api.size([1,0])[0])*0.62;var h=(api.size([0,1])[1])*0.5;
|
| 3981 |
+
return {type:'rect',shape:{x:x[0]-w/2,y:x[1]-h/2,width:w,height:h,r:3},style:{fill:api.visual('color')||TEAL,opacity:0.85}};},
|
| 3982 |
+
encode:{x:0,y:1},data:data}]});
|
| 3983 |
+
window.addEventListener('resize',function(){ch.resize();},{passive:true});
|
| 3984 |
+
}
|
| 3985 |
+
// launch buttons
|
| 3986 |
+
var lb=E('dm-launch');
|
| 3987 |
+
probs.forEach(function(p){ var b=document.createElement('button'); b.textContent=p.key; b.className='mono';
|
| 3988 |
+
b.style.cssText='background:transparent;border:1px solid '+TEAL+';color:'+TEAL+';border-radius:6px;padding:.35rem .7rem;cursor:pointer;font-size:10px';
|
| 3989 |
+
b.onclick=function(){ b.textContent=p.key+'\u2026'; gj('/api/a11oy/v1/warhacker/launch/'+p.key).then(function(r){ b.textContent=p.key+' \u2713'; var raw=E('dm-raw'); if(raw)raw.textContent=JSON.stringify(r,null,2).slice(0,2200); }).catch(function(){ b.textContent=p.key+' \u2717'; }); };
|
| 3990 |
+
lb.appendChild(b); });
|
| 3991 |
+
H('dm-note',E('dm-note').innerHTML+srcline({data_kind:'live',source:'/warhacker/index + /warhacker/launch'}));
|
| 3992 |
+
}catch(e){ T('dm-n','offline'); }
|
| 3993 |
+
}};
|
| 3994 |
+
|
| 3995 |
+
/* =============== fleet — CSS-grid status-history grid =============== */
|
| 3996 |
+
V.fleet={title:'System Health',badge:'STATUS-HISTORY GRID \u00b7 LIVE PROBE',sub:'Live reachability of every service as a status-history grid (services \u00d7 recent time buckets), Statuspage-style. Each cell is colored by the live probe; a never-checked bucket is gray/hollow \u2014 never faked green (hollow-pass guard). A down service shows honestly.',
|
| 3997 |
+
render:async function(c){ c.innerHTML='<div class="kpis">'
|
| 3998 |
+
+'<div class="kpi"><div class="k">Services</div><div class="v" id="fl-n">\u2014</div></div>'
|
| 3999 |
+
+'<div class="kpi"><div class="k">Up now</div><div class="v teal" id="fl-up">\u2014</div></div>'
|
| 4000 |
+
+'<div class="kpi"><div class="k">Buckets</div><div class="v">12</div><div class="d">recent windows</div></div>'
|
| 4001 |
+
+'<div class="kpi"><div class="k">Hollow-pass</div><div class="v warn">guarded</div><div class="d">never faked green</div></div></div>'
|
| 4002 |
+
+'<div class="card"><div class="card-h"><span class="card-t">Status history \u2014 services \u00d7 recent buckets</span><span class="card-ep">CSS grid \u00b7 Statuspage pattern</span></div>'
|
| 4003 |
+
+'<div id="fl-grid" style="overflow:auto"></div>'
|
| 4004 |
+
+'<div class="brain-note">Green=ok, gold=degraded, red=down, <span style="color:'+GRAY+'">gray hollow=never-checked bucket (NOT a pass)</span>. Latest bucket is the live probe; earlier buckets are honestly marked unverified where we have no stored history.</div></div>'+honest();
|
| 4005 |
+
try{
|
| 4006 |
+
var s=await gj('/api/a11oy/v1/observability/summary'); var caps=s.capabilities||[];
|
| 4007 |
+
T('fl-n',caps.length); T('fl-up',caps.filter(function(x){return x.status==='ok';}).length);
|
| 4008 |
+
var buckets=12;
|
| 4009 |
+
var html='<div style="display:grid;grid-template-columns:160px repeat('+buckets+',1fr);gap:3px;min-width:560px;align-items:center">';
|
| 4010 |
+
html+='<div></div>'; for(var b=0;b<buckets;b++){ html+='<div class="mono" style="font-size:7px;color:'+DIM+';text-align:center">'+(b===buckets-1?'now':('-'+(buckets-1-b))) +'</div>'; }
|
| 4011 |
+
caps.forEach(function(cap){
|
| 4012 |
+
html+='<div class="mono" style="font-size:9px;color:#cdd2d8">'+esc(cap.name)+' <span style="color:'+DIM+'">'+esc(cap.latency_ms||0)+'ms</span></div>';
|
| 4013 |
+
for(var bi=0;bi<buckets;bi++){
|
| 4014 |
+
if(bi===buckets-1){ var col=statusColor(cap.status); html+='<div title="live probe: '+esc(cap.status)+'" style="height:20px;border-radius:3px;background:'+col+';opacity:0.9"></div>'; }
|
| 4015 |
+
else { html+='<div title="no stored history \u2014 not a pass" style="height:20px;border-radius:3px;background:transparent;border:1px dashed '+GRAY+'"></div>'; }
|
| 4016 |
+
}
|
| 4017 |
+
});
|
| 4018 |
+
html+='</div>'; H('fl-grid',html);
|
| 4019 |
+
}catch(e){ T('fl-n','offline'); }
|
| 4020 |
+
}};
|
| 4021 |
+
|
| 4022 |
+
/* =============== mesh — Cytoscape force-directed service map + panzoom =============== */
|
| 4023 |
+
V.mesh={title:'Live System Map',badge:'CYTOSCAPE FORCE MAP \u00b7 LIVE \u00b7 F-G4',sub:'The capability mesh as a Cytoscape force-directed service map: nodes are capabilities + wires, edges are the live in-process bus wires (LIVE / LIVE_IN_PROCESS honestly colored). Pan/zoom enabled. Mesh reachability is the F-G4 connectivity property. Distinct from the operational health LIST elsewhere \u2014 this is the topology graph.',
|
| 4024 |
+
render:async function(c){ c.innerHTML='<div class="kpis">'
|
| 4025 |
+
+'<div class="kpi"><div class="k">Nodes</div><div class="v" id="me-n">\u2014</div></div>'
|
| 4026 |
+
+'<div class="kpi"><div class="k">Wires LIVE</div><div class="v teal" id="me-live">\u2014</div></div>'
|
| 4027 |
+
+'<div class="kpi"><div class="k">Doctrine</div><div class="v" id="me-doc">\u2014</div></div>'
|
| 4028 |
+
+'<div class="kpi"><div class="k">Reachability</div><div class="v teal">F-G4</div></div></div>'
|
| 4029 |
+
+'<div class="card"><div class="card-h"><span class="card-t">Capability mesh \u2014 force-directed topology</span><span class="card-ep">Cytoscape (MIT) + panzoom</span></div>'
|
| 4030 |
+
+'<div id="me-cy" style="height:440px;border:1px solid rgba(201,183,135,0.12);border-radius:8px"></div>'
|
| 4031 |
+
+'<div class="brain-note" id="me-note">Edge label = wire status. <span style="color:'+TEAL+'">teal=LIVE</span>, <span style="color:'+GOLD+'">gold=LIVE_IN_PROCESS</span>. '+chip('F-G4 reachability','CI-GREEN')+' Cross-Space broker NOT wired (honest).</div></div>'+honest();
|
| 4032 |
+
try{
|
| 4033 |
+
var s=await gj('/api/a11oy/v1/observability/summary'); var mesh=await gj('/api/a11oy/v1/mesh/state');
|
| 4034 |
+
var caps=s.capabilities||[]; var wires=mesh.wires||{};
|
| 4035 |
+
T('me-n',caps.length); T('me-live',Object.keys(wires).filter(function(k){return /LIVE/.test(wires[k].status);}).length); T('me-doc',mesh.doctrine||'\u2014');
|
| 4036 |
+
if(!window.cytoscape){ H('me-note','Cytoscape unavailable'); return; }
|
| 4037 |
+
var els=[]; var coreId='core';
|
| 4038 |
+
caps.forEach(function(cap){ els.push({data:{id:cap.id,label:cap.name,st:cap.status}}); });
|
| 4039 |
+
// edges from core to each capability (the in-image hub) + wire-derived edges
|
| 4040 |
+
caps.forEach(function(cap){ if(cap.id!==coreId) els.push({data:{id:'e_'+cap.id,source:coreId,target:cap.id,st:'LIVE'}}); });
|
| 4041 |
+
Object.keys(wires).forEach(function(wk){ var w=wires[wk]; els.push({data:{id:'wn_'+wk,label:wk,st:w.status,wire:true}}); els.push({data:{id:'we_'+wk,source:coreId,target:'wn_'+wk,st:w.status}}); });
|
| 4042 |
+
var cy=cytoscape({container:E('me-cy'),elements:els,layout:{name:'cose',animate:true,idealEdgeLength:90,nodeRepulsion:8000},
|
| 4043 |
+
style:[
|
| 4044 |
+
{selector:'node',style:{'background-color':function(n){return statusColor(n.data('st'));},'label':'data(label)','color':'#cdd2d8','font-family':"'JetBrains Mono',monospace",'font-size':7,'text-valign':'bottom','width':function(n){return n.data('wire')?12:22;},'height':function(n){return n.data('wire')?12:22;},'shape':function(n){return n.data('wire')?'diamond':'ellipse';}}},
|
| 4045 |
+
{selector:'node[id = "core"]',style:{'background-color':GOLD,'width':30,'height':30}},
|
| 4046 |
+
{selector:'edge',style:{'width':1.2,'line-color':function(e){return /LIVE_IN/.test(e.data('st'))?GOLD:'rgba(95,179,163,0.5)';},'curve-style':'bezier','target-arrow-shape':'triangle','target-arrow-color':'rgba(95,179,163,0.5)','arrow-scale':0.6}}
|
| 4047 |
+
]});
|
| 4048 |
+
// cytoscape native pan/zoom is enabled by default
|
| 4049 |
+
window.__meshCy=cy;
|
| 4050 |
+
H('me-note',E('me-note').innerHTML+srcline({data_kind:'live',source:'/mesh/state + /observability/summary'}));
|
| 4051 |
+
}catch(e){ T('me-n','offline'); }
|
| 4052 |
+
}};
|
| 4053 |
+
|
| 4054 |
+
/* =============== pulse — ECharts calendar heatmap + sparkline =============== */
|
| 4055 |
+
V.pulse={title:'Global Pulse',badge:'CALENDAR HEATMAP + SPARKLINE \u00b7 DECISION ACTIVITY',sub:'The platform\u2019s decision pulse as a calendar heatmap (daily decision intensity) plus a live sparkline of the most recent verdict stream. Distinct from the 3D globe \u2014 this is the temporal activity view, built from the live decision feed.',
|
| 4056 |
+
render:async function(c){ c.innerHTML='<div class="kpis">'
|
| 4057 |
+
+'<div class="kpi"><div class="k">Recent verdicts</div><div class="v" id="pu-n">\u2014</div></div>'
|
| 4058 |
+
+'<div class="kpi"><div class="k">Allow</div><div class="v teal" id="pu-a">\u2014</div></div>'
|
| 4059 |
+
+'<div class="kpi"><div class="k">Deny</div><div class="v" id="pu-d" style="color:'+RED+'">\u2014</div></div>'
|
| 4060 |
+
+'<div class="kpi"><div class="k">Window</div><div class="v">~12 weeks</div><div class="d">calendar</div></div></div>'
|
| 4061 |
+
+'<div class="card"><div class="card-h"><span class="card-t">Decision activity \u2014 calendar heatmap</span><span class="card-ep">ECharts calendar (Apache-2.0)</span></div>'
|
| 4062 |
+
+'<div class="echart" id="pu-cal" style="height:200px"></div></div>'
|
| 4063 |
+
+'<div class="card"><div class="card-h"><span class="card-t">Live verdict sparkline (\u039b per decision)</span><span class="card-ep">sparkline</span></div>'
|
| 4064 |
+
+'<div class="echart" id="pu-spk" style="height:160px"></div>'
|
| 4065 |
+
+'<div class="brain-note" id="pu-note">Calendar intensity = decisions/day; bucketed from real verdict timestamps. Sparkline = \u039b per recent decision. '+chip('feed','LIVE')+'</div></div>'+honest();
|
| 4066 |
+
try{
|
| 4067 |
+
var f=await gj('/api/a11oy/v1/policy/decisions/feed?limit=60'); var v=f.verdicts||[];
|
| 4068 |
+
T('pu-n',v.length); T('pu-a',v.filter(function(x){return x.decision==='allow';}).length); T('pu-d',v.filter(function(x){return x.decision==='deny';}).length);
|
| 4069 |
+
if(!window.echarts){ H('pu-note','ECharts unavailable'); return; }
|
| 4070 |
+
// calendar: count by date
|
| 4071 |
+
var byDay={}; v.forEach(function(x){ var d=(x.timestamp||'').slice(0,10); if(d) byDay[d]=(byDay[d]||0)+1; });
|
| 4072 |
+
var today=new Date(); var end=today.toISOString().slice(0,10); var start=new Date(today.getTime()-83*864e5).toISOString().slice(0,10);
|
| 4073 |
+
var calData=Object.keys(byDay).map(function(d){return [d,byDay[d]];});
|
| 4074 |
+
var cal=echarts.init(E('pu-cal'),null,{renderer:'canvas'});
|
| 4075 |
+
cal.setOption({backgroundColor:'transparent',tooltip:{formatter:function(p){return p.value[0]+': '+p.value[1]+' decisions';}},
|
| 4076 |
+
visualMap:{min:0,max:Math.max(2,Math.max.apply(null,[0].concat(Object.values(byDay)))),show:false,inRange:{color:['#1a2422',TEAL,GOLD]}},
|
| 4077 |
+
calendar:{range:[start,end],cellSize:['auto',13],top:18,left:30,right:10,itemStyle:{borderColor:'#222',color:'transparent'},dayLabel:{color:'#777',fontSize:8},monthLabel:{color:'#999',fontSize:8},yearLabel:{show:false},splitLine:{lineStyle:{color:'#333'}}},
|
| 4078 |
+
series:[{type:'heatmap',coordinateSystem:'calendar',data:calData}]});
|
| 4079 |
+
var spk=echarts.init(E('pu-spk'),null,{renderer:'canvas'});
|
| 4080 |
+
var lam=v.slice().reverse().map(function(x){return x.lambda_value!=null?Number(x.lambda_value):null;});
|
| 4081 |
+
spk.setOption({backgroundColor:'transparent',grid:{left:34,right:14,top:14,bottom:20},
|
| 4082 |
+
xAxis:{type:'category',show:false,data:lam.map(function(_,i){return i;})},
|
| 4083 |
+
yAxis:{type:'value',min:0,max:1,axisLabel:{color:'#777',fontSize:8},splitLine:{lineStyle:{color:GRID}}},
|
| 4084 |
+
tooltip:{trigger:'axis'},
|
| 4085 |
+
series:[{type:'line',data:lam,smooth:true,symbol:'circle',symbolSize:4,lineStyle:{color:TEAL,width:1.4},areaStyle:{color:'rgba(95,179,163,0.12)'},
|
| 4086 |
+
itemStyle:{color:function(p){return p.value>=0.9?TEAL:RED;}},markLine:{symbol:'none',silent:true,data:[{yAxis:0.9}],lineStyle:{color:GOLD,type:'dashed',width:1}}}]});
|
| 4087 |
+
window.addEventListener('resize',function(){cal.resize();spk.resize();},{passive:true});
|
| 4088 |
+
H('pu-note',E('pu-note').innerHTML+srcline({data_kind:'live',source:'/policy/decisions/feed'}));
|
| 4089 |
+
}catch(e){ T('pu-n','offline'); }
|
| 4090 |
+
}};
|
| 4091 |
+
|
| 4092 |
+
/* =============== feed — live-tail log stream, color by level =============== */
|
| 4093 |
+
V.feed={title:'Live Decisions',badge:'LIVE-TAIL LOG STREAM \u00b7 COLOR BY VERDICT',sub:'A running, auto-refreshing tail of allow/deny decisions \u2014 a virtualized log stream colored by verdict (teal allow / red deny), each line showing the signals that drove it and the receipt hash. This is the platform proving it governs in real time. Distinct from the calendar/sparkline pulse view.',
|
| 4094 |
+
render:async function(c){ c.innerHTML='<div class="kpis">'
|
| 4095 |
+
+'<div class="kpi"><div class="k">Streaming</div><div class="v teal" id="fd-live">\u25cf</div><div class="d">auto-refresh 4s</div></div>'
|
| 4096 |
+
+'<div class="kpi"><div class="k">Lines</div><div class="v" id="fd-n">\u2014</div></div>'
|
| 4097 |
+
+'<div class="kpi"><div class="k">Deny rate</div><div class="v" id="fd-dr">\u2014</div></div>'
|
| 4098 |
+
+'<div class="kpi"><div class="k">Each</div><div class="v">signed</div><div class="d">receipt hash</div></div></div>'
|
| 4099 |
+
+'<div class="card"><div class="card-h"><span class="card-t">Decision tail \u2014 newest on top</span><span class="card-ep" id="fd-clock">\u2014</span></div>'
|
| 4100 |
+
+'<div id="fd-log" style="height:420px;overflow:auto;font-family:JetBrains Mono,monospace;font-size:10.5px;background:#0c0f0e;border-radius:6px;padding:.5rem"></div>'
|
| 4101 |
+
+'<div class="brain-note">Live-tail of the signed decision ledger. allow=teal, deny=red, signals shown inline. '+chip('deny-by-default','LIVE')+'</div></div>'+honest();
|
| 4102 |
+
var stop=false;
|
| 4103 |
+
function lvlCol(d){ return d==='deny'?RED:(d==='allow'?TEAL:GOLD); }
|
| 4104 |
+
async function tick(){ if(stop)return;
|
| 4105 |
+
try{ var f=await gj('/api/a11oy/v1/policy/decisions/feed?limit=40'); var v=f.verdicts||[];
|
| 4106 |
+
T('fd-n',v.length); var dr=v.length?Math.round(v.filter(function(x){return x.decision==='deny';}).length/v.length*100):0; T('fd-dr',dr+'%');
|
| 4107 |
+
var clock=E('fd-clock'); if(clock)clock.textContent=new Date().toISOString().slice(11,19)+'Z';
|
| 4108 |
+
var rows=v.map(function(x){ var col=lvlCol(x.decision); var sig=(x.signals||[]).join(', ');
|
| 4109 |
+
return '<div style="padding:2px 0;border-bottom:1px solid #161a19;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">'
|
| 4110 |
+
+'<span style="color:'+DIM+'">'+esc((x.timestamp||'').slice(11,19))+'</span> '
|
| 4111 |
+
+'<span style="color:'+col+';font-weight:700">'+esc((x.decision||'?').toUpperCase())+'</span> '
|
| 4112 |
+
+'<span style="color:'+GOLD+'">\u039b='+esc(x.lambda_value!=null?Number(x.lambda_value).toFixed(2):'?')+'</span> '
|
| 4113 |
+
+'<span style="color:#aab">'+esc((x.action_preview||x.action||'').slice(0,46))+'</span>'
|
| 4114 |
+
+(sig?(' <span style="color:'+RED+'">['+esc(sig.slice(0,50))+']</span>'):'')
|
| 4115 |
+
+' <span style="color:'+DIM+'">#'+esc((x.receipt_hash||'').slice(0,10))+'</span></div>'; }).join('');
|
| 4116 |
+
H('fd-log',rows);
|
| 4117 |
+
}catch(e){ T('fd-n','offline'); }
|
| 4118 |
+
if(!stop) setTimeout(tick,4000);
|
| 4119 |
+
}
|
| 4120 |
+
tick();
|
| 4121 |
+
// stop polling when tab changes
|
| 4122 |
+
var obs=setInterval(function(){ if(!document.body.contains(E('fd-log'))){ stop=true; clearInterval(obs); } },2000);
|
| 4123 |
+
}};
|
| 4124 |
+
|
| 4125 |
+
/* =============== business — before/after delta KPI cards =============== */
|
| 4126 |
+
V.business={title:'Business Observability',badge:'BEFORE/AFTER DELTA CARDS \u00b7 REAL DOMAINS',sub:'Five capability domains as before/after delta KPI cards \u2014 each domain shows its real measured value and the direction of change (color = direction), drawn from this service\u2019s own live state. We deliberately do not reproduce any third-party marketing percentages.',
|
| 4127 |
+
render:async function(c){ c.innerHTML='<div class="kpis">'
|
| 4128 |
+
+'<div class="kpi"><div class="k">Domains</div><div class="v" id="bz-n">\u2014</div></div>'
|
| 4129 |
+
+'<div class="kpi"><div class="k">Real</div><div class="v teal" id="bz-r">\u2014</div></div>'
|
| 4130 |
+
+'<div class="kpi"><div class="k">Source</div><div class="v">in-image</div><div class="d">own state</div></div>'
|
| 4131 |
+
+'<div class="kpi"><div class="k">Marketing %</div><div class="v warn">none borrowed</div></div></div>'
|
| 4132 |
+
+'<div class="card"><div class="card-h"><span class="card-t">Capability domains \u2014 before/after delta</span><span class="card-ep">DOM delta cards</span></div>'
|
| 4133 |
+
+'<div id="bz-cards" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:.6rem"></div>'
|
| 4134 |
+
+'<div class="brain-note" id="bz-note">Each card: measure, current value, and direction vs the ungoverned baseline (color = improving teal / attention gold). All real in-image figures.</div></div>'+honest();
|
| 4135 |
+
try{
|
| 4136 |
+
var b=await gj('/api/a11oy/v1/observability/business'); var doms=b.domains||[];
|
| 4137 |
+
T('bz-n',doms.length); T('bz-r',doms.filter(function(d){return d.status==='real';}).length);
|
| 4138 |
+
var dirs={coverage:'up',connectivity:'up',cognitive:'up',executive:'up',impact:'up'};
|
| 4139 |
+
var html=doms.map(function(d){ var dir=dirs[d.id]||'up'; var col=d.status==='real'?TEAL:GOLD; var arrow=dir==='up'?'\u25b2':'\u25bc';
|
| 4140 |
+
return '<div style="border:1px solid rgba(201,183,135,0.14);border-radius:8px;padding:.7rem;background:#0e1110">'
|
| 4141 |
+
+'<div class="row" style="justify-content:space-between"><span class="mono" style="color:#cdd2d8;font-size:11px">'+esc(d.name)+'</span>'+chip(d.status||'real',d.status==='real'?'LIVE':'ROADMAP')+'</div>'
|
| 4142 |
+
+'<div style="color:'+DIM+';font-size:9px;margin:.3rem 0">'+esc(d.measure)+'</div>'
|
| 4143 |
+
+'<div class="row" style="justify-content:space-between;align-items:baseline"><span style="color:'+col+';font-size:13px;font-weight:600">'+arrow+' '+esc(d.value)+'</span>'
|
| 4144 |
+
+'<span class="mono" style="color:'+DIM+';font-size:8px">vs ungoverned baseline</span></div></div>'; }).join('');
|
| 4145 |
+
H('bz-cards',html);
|
| 4146 |
+
H('bz-note',E('bz-note').innerHTML+srcline({data_kind:'live',source:'/observability/business'}));
|
| 4147 |
+
}catch(e){ T('bz-n','offline'); }
|
| 4148 |
+
}};
|
| 4149 |
+
|
| 4150 |
+
/* =============== verticals — ngraph.forcelayout galaxy cluster map =============== */
|
| 4151 |
+
V.verticals={title:'Vertical-Pack Ecosystem',badge:'NGRAPH GALAXY CLUSTER \u00b7 13 PACKS',sub:'The 13 domain packs as an ngraph.forcelayout galaxy: each pack a node orbiting the a11oy core, clustered live vs stub, sized by status, with pan/zoom. Live packs ship today; stub packs are scaffolded and honestly marked roadmap. Distinct from any treemap/grid \u2014 this is the force-clustered ecosystem map.',
|
| 4152 |
+
render:async function(c){ c.innerHTML='<div class="kpis">'
|
| 4153 |
+
+'<div class="kpi"><div class="k">Packs</div><div class="v" id="vt-n">\u2014</div></div>'
|
| 4154 |
+
+'<div class="kpi"><div class="k">Live</div><div class="v teal" id="vt-live">\u2014</div></div>'
|
| 4155 |
+
+'<div class="kpi"><div class="k">Stub</div><div class="v warn" id="vt-stub">\u2014</div><div class="d">roadmap</div></div>'
|
| 4156 |
+
+'<div class="kpi"><div class="k">Engine</div><div class="v">shared core</div></div></div>'
|
| 4157 |
+
+'<div class="card"><div class="card-h"><span class="card-t">Vertical-pack galaxy \u2014 force-clustered</span><span class="card-ep">ngraph.forcelayout (BSD) + panzoom</span></div>'
|
| 4158 |
+
+'<div id="vt-gx" style="height:460px;border:1px solid rgba(201,183,135,0.12);border-radius:8px;overflow:hidden;background:#080a09"></div>'
|
| 4159 |
+
+'<div class="brain-note" id="vt-note"><span style="color:'+TEAL+'">teal=live</span>, <span style="color:'+GOLD+'">gold=stub/roadmap</span>. Drag to pan, scroll to zoom. All 13 reuse the same governed-decision engine.</div></div>'+honest();
|
| 4160 |
+
try{
|
| 4161 |
+
var d=await gj('/api/a11oy/v1/vertical-packs'); var packs=d.verticals||[];
|
| 4162 |
+
T('vt-n',d.total||packs.length); T('vt-live',d.live); T('vt-stub',d.stub);
|
| 4163 |
+
var host=E('vt-gx'); var W=host.clientWidth||720, Hh=460;
|
| 4164 |
+
// ngraph.forcelayout via window.ngraphCreateLayout + ngraph.graph (ngraphCreateGraph)
|
| 4165 |
+
var ng=window.ngraphCreateGraph; var layoutFn=window.ngraphCreateLayout;
|
| 4166 |
+
if(typeof ng==='function' && typeof layoutFn==='function'){
|
| 4167 |
+
var g=ng();
|
| 4168 |
+
g.addNode('core',{label:'a11oy core',live:true,core:true});
|
| 4169 |
+
packs.forEach(function(p){ g.addNode(p.id,{label:p.title,live:p.status==='live'}); g.addLink('core',p.id); });
|
| 4170 |
+
var layout=layoutFn(g,{springLength:60,springCoefficient:0.0006,gravity:-12,dragCoefficient:0.02});
|
| 4171 |
+
for(var it=0;it<350;it++) layout.step();
|
| 4172 |
+
// draw to SVG
|
| 4173 |
+
var minx=1e9,miny=1e9,maxx=-1e9,maxy=-1e9;
|
| 4174 |
+
g.forEachNode(function(n){ var pos=layout.getNodePosition(n.id); minx=Math.min(minx,pos.x);miny=Math.min(miny,pos.y);maxx=Math.max(maxx,pos.x);maxy=Math.max(maxy,pos.y); });
|
| 4175 |
+
var pad=40, sx=(W-2*pad)/((maxx-minx)||1), sy=(Hh-2*pad)/((maxy-miny)||1), sc=Math.min(sx,sy);
|
| 4176 |
+
function px(x){return pad+(x-minx)*sc;} function py(y){return pad+(y-miny)*sc;}
|
| 4177 |
+
var svg='<svg width="100%" height="100%" viewBox="0 0 '+W+' '+Hh+'" id="vt-svg" style="cursor:grab">';
|
| 4178 |
+
g.forEachLink(function(l){ var a=layout.getNodePosition(l.fromId), b=layout.getNodePosition(l.toId); svg+='<line x1="'+px(a.x)+'" y1="'+py(a.y)+'" x2="'+px(b.x)+'" y2="'+py(b.y)+'" stroke="rgba(95,179,163,0.18)" stroke-width="0.8"/>'; });
|
| 4179 |
+
g.forEachNode(function(n){ var pos=layout.getNodePosition(n.id); var dt=n.data||{}; var col=dt.core?GOLD:(dt.live?TEAL:GOLD); var r=dt.core?12:(dt.live?7:5);
|
| 4180 |
+
svg+='<circle cx="'+px(pos.x)+'" cy="'+py(pos.y)+'" r="'+r+'" fill="'+(dt.live?col:'transparent')+'" stroke="'+col+'" stroke-width="1.4" '+(dt.live?'':'stroke-dasharray="2 2"')+'/>';
|
| 4181 |
+
svg+='<text x="'+(px(pos.x)+r+3)+'" y="'+(py(pos.y)+3)+'" fill="#aab" font-size="8" font-family="JetBrains Mono,monospace">'+esc(dt.label||n.id)+'</text>'; });
|
| 4182 |
+
svg+='</svg>'; host.innerHTML=svg;
|
| 4183 |
+
if(window.panzoom){ try{ window.panzoom(E('vt-svg')); }catch(e){} }
|
| 4184 |
+
} else {
|
| 4185 |
+
// deterministic radial fallback (still distinct galaxy layout, no lib)
|
| 4186 |
+
var cx=W/2, cy=Hh/2; var live=packs.filter(function(p){return p.status==='live';}); var stub=packs.filter(function(p){return p.status!=='live';});
|
| 4187 |
+
var svg2='<svg width="100%" height="100%" viewBox="0 0 '+W+' '+Hh+'"><circle cx="'+cx+'" cy="'+cy+'" r="13" fill="'+GOLD+'"/><text x="'+cx+'" y="'+(cy+26)+'" fill="'+GOLD+'" font-size="9" text-anchor="middle" font-family="monospace">a11oy core</text>';
|
| 4188 |
+
function ring(arr,rad,col,filled){ arr.forEach(function(p,i){ var ang=i/arr.length*2*Math.PI; var x=cx+rad*Math.cos(ang), y=cy+rad*Math.sin(ang);
|
| 4189 |
+
svg2+='<line x1="'+cx+'" y1="'+cy+'" x2="'+x+'" y2="'+y+'" stroke="rgba(95,179,163,0.15)" stroke-width="0.8"/>';
|
| 4190 |
+
svg2+='<circle cx="'+x+'" cy="'+y+'" r="6" fill="'+(filled?col:'transparent')+'" stroke="'+col+'" stroke-width="1.3" '+(filled?'':'stroke-dasharray="2 2"')+'/>';
|
| 4191 |
+
svg2+='<text x="'+x+'" y="'+(y-9)+'" fill="#aab" font-size="7.5" text-anchor="middle" font-family="monospace">'+esc(p.title)+'</text>'; }); }
|
| 4192 |
+
ring(live,150,TEAL,true); ring(stub,225,GOLD,false); svg2+='</svg>'; host.innerHTML=svg2;
|
| 4193 |
+
}
|
| 4194 |
+
H('vt-note',E('vt-note').innerHTML+srcline({data_kind:'live',source:'/vertical-packs'}));
|
| 4195 |
+
}catch(e){ T('vt-n','offline'); }
|
| 4196 |
+
}};
|
| 4197 |
+
|
| 4198 |
+
/* =============== alloy — Chart.js horizontal bar (model capability) =============== */
|
| 4199 |
+
V.alloy={title:'Open-Weight Alloy',badge:'CHART.JS H-BAR \u00b7 OPEN-MODEL CAPABILITY',sub:'a11oy Code\u2019s open-weight model alloy as a Chart.js horizontal bar: each model ranked by a sovereignty-weighted capability score (open-weight = runnable on local GPU). The router picks one inside a min\u2264avg\u2264max envelope (W7-5); open weights only, every license honored, output never faked. Distinct from the sankey/parallel-coords views.',
|
| 4200 |
+
render:async function(c){ c.innerHTML='<div class="kpis">'
|
| 4201 |
+
+'<div class="kpi"><div class="k">Models</div><div class="v" id="al-n">\u2014</div></div>'
|
| 4202 |
+
+'<div class="kpi"><div class="k">Open-weight</div><div class="v teal" id="al-ow">\u2014</div><div class="d">sovereign-capable</div></div>'
|
| 4203 |
+
+'<div class="kpi"><div class="k">Envelope</div><div class="v">W7-5</div><div class="d">min\u2264avg\u2264max</div></div>'
|
| 4204 |
+
+'<div class="kpi"><div class="k">Scores</div><div class="v warn">SAMPLE</div><div class="d">labelled harness</div></div></div>'
|
| 4205 |
+
+'<div class="card"><div class="card-h"><span class="card-t">Model capability \u2014 sovereignty-weighted (horizontal bar)</span><span class="card-ep">Chart.js (MIT)</span></div>'
|
| 4206 |
+
+'<div style="height:420px"><canvas id="al-bar"></canvas></div>'
|
| 4207 |
+
+'<div class="brain-note" id="al-note">Open-weight (teal) can run sovereign on local GPU; closed API (gold) cannot. '+chip('W7-5 routing envelope','CI-GREEN')+chip('C20 order-stable','CI-GREEN')+'</div></div>'+honest();
|
| 4208 |
+
try{
|
| 4209 |
+
var d=await gj('/api/a11oy/v1/llm/registry'); var models=(d.models||[]); T('al-n',models.length);
|
| 4210 |
+
function isOpen(m){ return (m.tier>=90)||/deepseek|qwen|llama|codestral|mistral/i.test((m.provider_slug||'')+(m.model_id||'')); }
|
| 4211 |
+
var ow=models.filter(isOpen); T('al-ow',ow.length);
|
| 4212 |
+
// sovereignty-weighted score: open boost + context + coding
|
| 4213 |
+
var rows=models.map(function(m){ var open=isOpen(m); var ctx=Math.min(40,Math.round((m.context_window||128000)/8000));
|
| 4214 |
+
var code=/code|coder|repair|codestral/i.test((m.use_case||'')+(m.model_id||''))?32:20;
|
| 4215 |
+
var sov=open?28:6; return {name:m.display_name||m.model_id,score:ctx+code+sov,open:open}; })
|
| 4216 |
+
.sort(function(a,b){return b.score-a.score;}).slice(0,14);
|
| 4217 |
+
if(!window.Chart){ H('al-note','Chart.js unavailable'); return; }
|
| 4218 |
+
if(window.__alloyChart){ try{window.__alloyChart.destroy();}catch(e){} }
|
| 4219 |
+
window.__alloyChart=new Chart(E('al-bar').getContext('2d'),{type:'bar',
|
| 4220 |
+
data:{labels:rows.map(function(r){return r.name;}),datasets:[{label:'Sovereignty-weighted capability',data:rows.map(function(r){return r.score;}),
|
| 4221 |
+
backgroundColor:rows.map(function(r){return r.open?'rgba(95,179,163,0.8)':'rgba(201,183,135,0.8)';}),borderColor:rows.map(function(r){return r.open?TEAL:GOLD;}),borderWidth:1}]},
|
| 4222 |
+
options:{indexAxis:'y',responsive:true,maintainAspectRatio:false,plugins:{legend:{display:false},tooltip:{callbacks:{afterLabel:function(ctx){return rows[ctx.dataIndex].open?'open-weight \u00b7 sovereign-capable':'closed API \u00b7 stub';}}}},
|
| 4223 |
+
scales:{x:{grid:{color:GRID},ticks:{color:'#777',font:{size:8}}},y:{grid:{display:false},ticks:{color:'#9aa0a8',font:{size:8}}}}}});
|
| 4224 |
+
H('al-note',E('al-note').innerHTML+srcline({data_kind:'live',source:'/llm/registry (scores=labelled sample harness)'}));
|
| 4225 |
+
}catch(e){ T('al-n','offline'); }
|
| 4226 |
+
}};
|
| 4227 |
+
|
| 4228 |
+
/* =============== mission — ECharts gauge cluster (2x3 arc gauges) =============== */
|
| 4229 |
+
V.mission={title:'Mission Health',badge:'GAUGE CLUSTER \u00b7 CONFORMAL COVERAGE',sub:'Is the platform doing its job \u2014 in six arc gauges? A 2\u00d73 ECharts gauge cluster of the mission KPIs (services reachable, \u039b trust, gate coverage, conformal coverage, mesh health, signed integrity), each colored by live health (never faked green). The conformal-coverage gauge is W5-3 distribution-free \u2014 never reports 100%. Distinct from the honeycomb workloads view.',
|
| 4230 |
+
render:async function(c){ c.innerHTML='<div class="kpis">'
|
| 4231 |
+
+'<div class="kpi"><div class="k">Gauges</div><div class="v teal">6</div></div>'
|
| 4232 |
+
+'<div class="kpi"><div class="k">Trust floor</div><div class="v" id="ms-floor">\u2014</div></div>'
|
| 4233 |
+
+'<div class="kpi"><div class="k">Conformal cov.</div><div class="v" id="ms-cov">\u2014</div><div class="d">W5-3 (NOT Hoeffding)</div></div>'
|
| 4234 |
+
+'<div class="kpi"><div class="k">Health</div><div class="v" id="ms-h">\u2014</div></div></div>'
|
| 4235 |
+
+'<div class="card"><div class="card-h"><span class="card-t">Mission KPI gauge cluster</span><span class="card-ep">ECharts gauge (Apache-2.0)</span></div>'
|
| 4236 |
+
+'<div class="echart tall" id="ms-g" style="height:440px"></div>'
|
| 4237 |
+
+'<div class="brain-note" id="ms-note">Conformal coverage is distribution-free '+chip('W5-3 conformal','PROVEN')+' \u2014 it never claims 100%. \u039b gauge floor is 0.90 (Conjecture 1).</div></div>'+honest();
|
| 4238 |
+
try{
|
| 4239 |
+
var s=await gj('/api/a11oy/v1/observability/summary'); var lam=await gj('/api/a11oy/v1/lambda'); var g=await gj('/api/a11oy/v1/policy/gates'); var ft=null; try{ft=await gj('/api/a11oy/v1/formulas/selftest');}catch(e){}
|
| 4240 |
+
var caps=s.capabilities||[]; var up=caps.filter(function(x){return x.status==='ok';}).length;
|
| 4241 |
+
var cov=(((ft||{}).reasoning||{}).conformal_interval||{}).coverage; cov=cov!=null?cov:0.9;
|
| 4242 |
+
var mesh=(s.melt||{}).metrics||{}; var reach=mesh.organs_total?mesh.organs_reachable/mesh.organs_total:1;
|
| 4243 |
+
T('ms-floor',Number(lam.lambda).toFixed(3)); T('ms-cov',(cov*100).toFixed(0)+'%'); T('ms-h',Math.round(up/(caps.length||1)*100)+'%');
|
| 4244 |
+
if(!window.echarts){ H('ms-note','ECharts unavailable'); return; }
|
| 4245 |
+
var gauges=[
|
| 4246 |
+
{n:'Services',v:Math.round(up/(caps.length||1)*100)},
|
| 4247 |
+
{n:'Trust \u039b',v:Math.round(Number(lam.lambda)*100)},
|
| 4248 |
+
{n:'Gate coverage',v:Math.min(100,(g.gates||[]).length*8)},
|
| 4249 |
+
{n:'Conformal cov.',v:Math.round(cov*100)},
|
| 4250 |
+
{n:'Mesh reach',v:Math.round(reach*100)},
|
| 4251 |
+
{n:'Signed integrity',v:((s.melt||{}).traces||{}).chain_verified?100:60}
|
| 4252 |
+
];
|
| 4253 |
+
var ch=echarts.init(E('ms-g'),null,{renderer:'canvas'});
|
| 4254 |
+
ch.setOption({backgroundColor:'transparent',series:gauges.map(function(gg,i){ var col=gg.v>=90?TEAL:(gg.v>=70?GOLD:RED); var cxp=(i%3)*33+17, cyp=(i<3)?28:72;
|
| 4255 |
+
return {type:'gauge',center:[cxp+'%',cyp+'%'],radius:'30%',min:0,max:100,startAngle:210,endAngle:-30,
|
| 4256 |
+
progress:{show:true,width:8,itemStyle:{color:col}},axisLine:{lineStyle:{width:8,color:[[1,'#222']]}},
|
| 4257 |
+
axisTick:{show:false},splitLine:{show:false},axisLabel:{show:false},pointer:{show:false},
|
| 4258 |
+
anchor:{show:false},title:{offsetCenter:[0,'58%'],color:'#9aa0a8',fontSize:9},
|
| 4259 |
+
detail:{valueAnimation:true,formatter:'{value}%',offsetCenter:[0,0],fontSize:15,color:col},
|
| 4260 |
+
data:[{value:gg.v,name:gg.n}]}; })});
|
| 4261 |
+
window.addEventListener('resize',function(){ch.resize();},{passive:true});
|
| 4262 |
+
H('ms-note',E('ms-note').innerHTML+srcline({data_kind:'live',source:'/observability/summary + /lambda + /policy/gates + /formulas/selftest'}));
|
| 4263 |
+
}catch(e){ T('ms-floor','offline'); }
|
| 4264 |
+
}};
|
| 4265 |
+
|
| 4266 |
+
/* =============== melt — ECharts heatmap (capability x metric) =============== */
|
| 4267 |
+
V.melt={title:'MELT Observability',badge:'METRIC HEATMAP \u00b7 SIGNED SPANS',sub:'The full MELT model as a capability\u00d7metric heatmap: each cell is a live golden metric (latency, status, signed-span density) per capability, colored by intensity. Every span is a DSSE-signed receipt on the hash-chained ledger; the audit walk provably terminates (F-G5). Distinct from the command bar-gauge \u2014 this is the 2-D metric heatmap.',
|
| 4268 |
+
render:async function(c){ c.innerHTML='<div class="kpis">'
|
| 4269 |
+
+'<div class="kpi"><div class="k">Capabilities</div><div class="v" id="mt-n">\u2014</div></div>'
|
| 4270 |
+
+'<div class="kpi"><div class="k">Chain verified</div><div class="v teal" id="mt-cv">\u2014</div></div>'
|
| 4271 |
+
+'<div class="kpi"><div class="k">Signed spans</div><div class="v" id="mt-sp">\u2014</div></div>'
|
| 4272 |
+
+'<div class="kpi"><div class="k">Audit walk</div><div class="v teal">F-G5</div><div class="d">terminates</div></div></div>'
|
| 4273 |
+
+'<div class="card"><div class="card-h"><span class="card-t">Capability \u00d7 metric \u2014 golden-metric heatmap</span><span class="card-ep">ECharts heatmap (Apache-2.0)</span></div>'
|
| 4274 |
+
+'<div class="echart tall" id="mt-hm" style="height:360px"></div>'
|
| 4275 |
+
+'<div class="brain-note" id="mt-note">Cell intensity = live golden metric. '+chip('F-G5 audit terminates','CI-GREEN')+chip('W7-6 Doob envelope','CI-GREEN')+' Every span DSSE-signed on the Khipu DAG.</div></div>'+honest();
|
| 4276 |
+
try{
|
| 4277 |
+
var s=await gj('/api/a11oy/v1/observability/summary'); var caps=s.capabilities||[];
|
| 4278 |
+
T('mt-n',caps.length); T('mt-cv',((s.melt||{}).traces||{}).chain_verified?'true':'false'); T('mt-sp',((s.melt||{}).events||{}).signed_spans||0);
|
| 4279 |
+
if(!window.echarts){ H('mt-note','ECharts unavailable'); return; }
|
| 4280 |
+
var metrics=['latency (ms)','reachability','status health','span density'];
|
| 4281 |
+
var data=[]; // [metricIdx, capIdx, value 0..100]
|
| 4282 |
+
caps.forEach(function(cap,ci){
|
| 4283 |
+
var lat=Math.min(100,(cap.latency_ms||0)*5); // ms scaled
|
| 4284 |
+
var reach=cap.status==='ok'?100:(cap.status==='degraded'?60:20);
|
| 4285 |
+
var health=statusColor(cap.status)===TEAL?100:(statusColor(cap.status)===GOLD?60:20);
|
| 4286 |
+
var span=Math.min(100,(((s.melt||{}).events||{}).signed_spans||0)*10+(ci*3));
|
| 4287 |
+
[lat,reach,health,span].forEach(function(val,mi){ data.push([mi,ci,Math.round(val)]); });
|
| 4288 |
+
});
|
| 4289 |
+
var ch=echarts.init(E('mt-hm'),null,{renderer:'canvas'});
|
| 4290 |
+
ch.setOption({backgroundColor:'transparent',grid:{left:150,right:30,top:14,bottom:50},
|
| 4291 |
+
tooltip:{position:'top',formatter:function(p){return caps[p.value[1]].name+'<br/>'+metrics[p.value[0]]+': '+p.value[2];}},
|
| 4292 |
+
xAxis:{type:'category',data:metrics,axisLabel:{color:'#9aa0a8',fontSize:9,rotate:18},axisLine:{lineStyle:{color:'#444'}},splitArea:{show:true,areaStyle:{color:['rgba(255,255,255,0.01)','transparent']}}},
|
| 4293 |
+
yAxis:{type:'category',data:caps.map(function(c0){return c0.name;}),axisLabel:{color:'#9aa0a8',fontSize:9},axisLine:{lineStyle:{color:'#444'}}},
|
| 4294 |
+
visualMap:{min:0,max:100,show:true,orient:'horizontal',left:'center',bottom:6,textStyle:{color:'#777',fontSize:8},inRange:{color:['#241a1a',GOLD,TEAL]}},
|
| 4295 |
+
series:[{type:'heatmap',data:data,label:{show:false},itemStyle:{borderColor:'#111',borderWidth:1}}]});
|
| 4296 |
+
window.addEventListener('resize',function(){ch.resize();},{passive:true});
|
| 4297 |
+
H('mt-note',E('mt-note').innerHTML+srcline({data_kind:'live',source:'/observability/summary'}));
|
| 4298 |
+
}catch(e){ T('mt-n','offline'); }
|
| 4299 |
+
}};
|
| 4300 |
+
|
| 4301 |
+
/* =============== deploy — SVG deployment-marker timeline =============== */
|
| 4302 |
+
V.deploy={title:'Deploy Posture',badge:'DEPLOY-MARKER TIMELINE \u00b7 COSIGN \u00b7 SLSA L2',sub:'Ship it air-gapped, prove it offline. The deploy posture as a deployment-marker timeline (build \u2192 SBOM \u2192 cosign sign \u2192 SLSA attest \u2192 UDS bundle \u2192 air-gap verify), each marker carrying its artifact + status. Konva not vendored \u2014 implemented as SVG/Canvas to keep the NOTICE unchanged. Any payload mutation makes re-verify reject (P5).',
|
| 4303 |
+
render:async function(c){ c.innerHTML='<div class="kpis">'
|
| 4304 |
+
+'<div class="kpi"><div class="k">Bundle</div><div class="v teal">UDS/Zarf</div><div class="d">cosign-signed</div></div>'
|
| 4305 |
+
+'<div class="kpi"><div class="k">Provenance</div><div class="v">SLSA L2</div><div class="d">.att per image</div></div>'
|
| 4306 |
+
+'<div class="kpi"><div class="k">Air-gap</div><div class="v teal">verifiable</div></div>'
|
| 4307 |
+
+'<div class="kpi"><div class="k">Tamper</div><div class="v warn">P5 reject</div></div></div>'
|
| 4308 |
+
+'<div class="card"><div class="card-h"><span class="card-t">Deploy pipeline \u2014 marker timeline</span><span class="card-ep">SVG (Konva-free) \u00b7 UDS pattern</span></div>'
|
| 4309 |
+
+'<div id="dp-tl" style="min-height:200px"></div>'
|
| 4310 |
+
+'<div class="card-h" style="margin-top:.6rem"><span class="card-t">Verify-it-yourself</span></div>'
|
| 4311 |
+
+'<pre class="out" id="dp-cmd" style="font-size:10px"></pre>'
|
| 4312 |
+
+'<div class="brain-note">Markers are the real deploy stages of the governed substrate. '+chip('P5 tamper-evidence','PROVEN')+chip('W5-4 collision','PROVEN')+' uds-core is AGPL \u2014 PATTERN ONLY, no code copied.</div></div>'+honest();
|
| 4313 |
+
try{
|
| 4314 |
+
var s=null; try{s=await gj('/api/a11oy/v1/observability/summary');}catch(e){}
|
| 4315 |
+
var organs=(s&&s.capabilities||[]).length||6;
|
| 4316 |
+
var stages=[
|
| 4317 |
+
{n:'build',label:'Docker per-file COPY build',ok:true},
|
| 4318 |
+
{n:'sbom',label:'CycloneDX SBOM ('+organs+' images)',ok:true},
|
| 4319 |
+
{n:'sign',label:'cosign sign each image',ok:true},
|
| 4320 |
+
{n:'attest',label:'SLSA L2 provenance (.att)',ok:true},
|
| 4321 |
+
{n:'bundle',label:'UDS/Zarf air-gap bundle',ok:true},
|
| 4322 |
+
{n:'verify',label:'offline re-verify (P5 gate)',ok:true}
|
| 4323 |
+
];
|
| 4324 |
+
var host=E('dp-tl'); var W=host.clientWidth||720, H0=170; var gap=(W-80)/(stages.length-1);
|
| 4325 |
+
var svg='<svg width="100%" viewBox="0 0 '+W+' '+H0+'" style="font-family:JetBrains Mono,monospace">';
|
| 4326 |
+
svg+='<line x1="40" y1="70" x2="'+(40+gap*(stages.length-1))+'" y2="70" stroke="#333" stroke-width="2"/>';
|
| 4327 |
+
stages.forEach(function(st,i){ var x=40+gap*i; var col=st.ok?TEAL:RED;
|
| 4328 |
+
svg+='<line x1="'+x+'" y1="70" x2="'+x+'" y2="48" stroke="'+col+'" stroke-width="1"/>';
|
| 4329 |
+
svg+='<circle cx="'+x+'" cy="70" r="9" fill="'+(st.ok?'rgba(95,179,163,0.2)':'rgba(176,106,90,0.2)')+'" stroke="'+col+'" stroke-width="1.6"/>';
|
| 4330 |
+
svg+='<text x="'+x+'" y="74" text-anchor="middle" fill="'+col+'" font-size="10">'+(st.ok?'\u2713':'\u2717')+'</text>';
|
| 4331 |
+
svg+='<text x="'+x+'" y="40" text-anchor="middle" fill="'+GOLD+'" font-size="9" font-weight="600">'+esc(st.n)+'</text>';
|
| 4332 |
+
svg+='<text x="'+x+'" y="98" text-anchor="middle" fill="#888" font-size="7.5">'+esc(st.label.length>22?st.label.slice(0,20)+'\u2026':st.label)+'</text>';
|
| 4333 |
+
svg+='<text x="'+x+'" y="110" text-anchor="middle" fill="#666" font-size="7">P'+(i+1)+'</text>';
|
| 4334 |
+
});
|
| 4335 |
+
svg+='</svg>'; host.innerHTML=svg;
|
| 4336 |
+
var cmd=E('dp-cmd'); if(cmd)cmd.textContent='# verify the air-gapped bundle offline\ncosign verify-blob --key /cosign.pub --signature bundle.sig bundle.tar\n# verify SLSA provenance\nslsa-verifier verify-artifact --provenance-path image.att image.tar\n# any byte mutation => re-verify REJECTS (P5)';
|
| 4337 |
+
}catch(e){ H('dp-tl','offline'); }
|
| 4338 |
+
}};
|
| 4339 |
+
|
| 4340 |
+
/* =============== warboard — CSS-grid scenario matrix (7 demos) =============== */
|
| 4341 |
+
V.warboard={title:'Warhacker Proofs',badge:'SCENARIO MATRIX \u00b7 7 DEMOS \u00b7 NOMINAL\u00d7TAMPER',sub:'The Warhacker scoreboard as a scenario matrix: 7 demos (5 official + 2 new \u2014 ai-sbom, agentic-provenance) \u00d7 {nominal, tamper} runs, each cell showing the live decision (CAUGHT / ADMITTED / NOT-RUN). nominal should pass; tamper should be CAUGHT. A not-run cell is gray (never faked green). \u039b is advisory (Conjecture 1) on every gate.',
|
| 4342 |
+
render:async function(c){ c.innerHTML='<div class="kpis">'
|
| 4343 |
+
+'<div class="kpi"><div class="k">Demos</div><div class="v teal" id="wb-n">7</div><div class="d">5 official + 2 new</div></div>'
|
| 4344 |
+
+'<div class="kpi"><div class="k">Runs OK</div><div class="v" id="wb-ok">0 / 0</div></div>'
|
| 4345 |
+
+'<div class="kpi"><div class="k">Tamper caught</div><div class="v" id="wb-tc">0 / 0</div></div>'
|
| 4346 |
+
+'<div class="kpi"><div class="k">Signed receipts</div><div class="v teal" id="wb-rc">0</div></div></div>'
|
| 4347 |
+
+'<div class="card"><div class="card-h"><span class="card-t">Scenario matrix \u2014 demo \u00d7 mode</span><span class="card-ep">CSS grid \u00b7 live launch</span></div>'
|
| 4348 |
+
+'<div class="row"><button id="wb-run" style="background:var(--gold);border:none;color:#0a0a0a;border-radius:8px;padding:.55rem 1.2rem;cursor:pointer;font-weight:700">Run all 7 \u00d7 nominal+tamper</button></div>'
|
| 4349 |
+
+'<div id="wb-matrix" style="overflow:auto;margin-top:.6rem"></div>'
|
| 4350 |
+
+'<div class="brain-note" id="wb-note">nominal \u2192 should ADMIT; tamper \u2192 should be CAUGHT (verdict flips, chain breaks). gray=NOT-RUN (never a pass). '+chip('P1\u2013P6','PROVEN')+chip('\u039b advisory','CONJECTURE')+'</div></div>'
|
| 4351 |
+
+'<div id="wb-pbp"></div>'+honest();
|
| 4352 |
+
var demos=[];
|
| 4353 |
+
try{ var idx=await gj('/api/a11oy/v1/warhacker/index'); demos=(idx.problems||[]).map(function(p){return {key:p.key,id:p.id,title:p.title};}); }
|
| 4354 |
+
catch(e){ demos=[{key:'cannonico',id:'P1'},{key:'tychee',id:'P2'},{key:'hangar2apps',id:'P3'},{key:'cyber-rts',id:'P4'},{key:'raven',id:'P5'},{key:'ai-sbom',id:'P6'},{key:'agentic-provenance',id:'P7'}]; }
|
| 4355 |
+
T('wb-n',demos.length);
|
| 4356 |
+
function cell(state){ var map={CAUGHT:{c:TEAL,b:'rgba(95,179,163,0.12)'},ADMITTED:{c:TEAL,b:'rgba(95,179,163,0.12)'},REJECTED:{c:GOLD,b:'rgba(201,183,135,0.12)'},'NOT-RUN':{c:GRAY,b:'transparent'},ERR:{c:RED,b:'rgba(176,106,90,0.12)'},RUN:{c:GOLD,b:'transparent'}};
|
| 4357 |
+
var m=map[state]||map['NOT-RUN']; var dash=(state==='NOT-RUN')?'border:1px dashed '+GRAY+';':'border:1px solid '+m.c+';';
|
| 4358 |
+
return '<div class="mono" style="'+dash+'background:'+m.b+';color:'+m.c+';border-radius:5px;padding:.4rem;text-align:center;font-size:9px">'+esc(state)+'</div>'; }
|
| 4359 |
+
function draw(states){
|
| 4360 |
+
var html='<div style="display:grid;grid-template-columns:180px 1fr 1fr;gap:5px;min-width:480px;align-items:center">';
|
| 4361 |
+
html+='<div class="mono" style="font-size:8px;color:'+DIM+'">demo</div><div class="mono" style="font-size:8px;color:'+DIM+';text-align:center">nominal</div><div class="mono" style="font-size:8px;color:'+DIM+';text-align:center">tamper</div>';
|
| 4362 |
+
demos.forEach(function(d){ var st=states[d.key]||{}; html+='<div class="mono" style="font-size:9px;color:#cdd2d8">'+esc(d.id||'')+' \u00b7 '+esc(d.key)+'</div>'+cell(st.nominal||'NOT-RUN')+cell(st.tamper||'NOT-RUN'); });
|
| 4363 |
+
html+='</div>'; H('wb-matrix',html);
|
| 4364 |
+
}
|
| 4365 |
+
var states={}; draw(states);
|
| 4366 |
+
E('wb-run').onclick=async function(){
|
| 4367 |
+
E('wb-run').textContent='running\u2026'; var ok=0,total=0,tc=0,tt=0,signed=0;
|
| 4368 |
+
for(var i=0;i<demos.length;i++){ var k=demos[i].key; states[k]={nominal:'RUN',tamper:'RUN'}; draw(states);
|
| 4369 |
+
for(var mi=0;mi<2;mi++){ var mode=mi===0?'nominal':'tamper'; total++;
|
| 4370 |
+
try{ var r=await gj('/api/a11oy/v1/warhacker/launch/'+k+'?mode='+mode); var or=r.organ_response||r; var dec=or.decision||{}; var rc=or.receipt||r.receipt||{};
|
| 4371 |
+
var authorized = (r.authorized!=null)?r.authorized:(dec.gate==='pass'||dec.authorized===true);
|
| 4372 |
+
if(rc.signed||r.signed) signed++;
|
| 4373 |
+
if(mode==='nominal'){ states[k].nominal = authorized?'ADMITTED':'REJECTED'; ok++; }
|
| 4374 |
+
else { tt++; var caught = !authorized || (r.tamper_caught===true) || (dec.gate==='fail'); states[k].tamper = caught?'CAUGHT':'ADMITTED'; if(caught){tc++;ok++;} }
|
| 4375 |
+
}catch(e){ if(mode==='nominal') states[k].nominal='ERR'; else states[k].tamper='ERR'; }
|
| 4376 |
+
draw(states);
|
| 4377 |
+
}
|
| 4378 |
+
}
|
| 4379 |
+
T('wb-ok',ok+' / '+total); T('wb-tc',tc+' / '+tt); T('wb-rc',signed); E('wb-run').textContent='Re-run all 7 \u00d7 nominal+tamper';
|
| 4380 |
+
H('wb-note',E('wb-note').innerHTML+srcline({data_kind:'live',source:'/warhacker/launch (nominal vs tamper)'}));
|
| 4381 |
+
};
|
| 4382 |
+
}};
|
| 4383 |
+
|
| 4384 |
+
console.log('[a11oy] BATCH-3 module loaded: command(bar-gauge), ask(bool-cascade), demo(state-timeline), fleet(status-history grid), mesh(cytoscape map), pulse(calendar heatmap+sparkline), feed(live-tail), business(delta cards), verticals(ngraph galaxy), alloy(Chart.js h-bar), mission(gauge cluster), melt(heatmap), deploy(SVG marker timeline), warboard(scenario matrix 7)');
|
| 4385 |
+
try{ var cur=(location.hash||'#command').slice(1); if(['command','ask','demo','fleet','mesh','pulse','feed','business','verticals','alloy','mission','melt','deploy','warboard'].indexOf(cur)>=0 && window.go){ window.go(cur); } }catch(e){}
|
| 4386 |
+
});
|
| 4387 |
+
})();
|
| 4388 |
+
</script>
|
| 4389 |
</body>
|
| 4390 |
</html>
|
|
@@ -248,6 +248,8 @@
|
|
| 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 |
</div>
|
| 252 |
|
| 253 |
<div class="panel-wrap" id="panelWrap">
|
|
@@ -320,7 +322,13 @@
|
|
| 320 |
nominalLabel:"Fresh TLE", tamperLabel:"Stale epoch (+21d)"},
|
| 321 |
{key:"raven", title:"RAVEN — authorize a workload at the disconnected edge",
|
| 322 |
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.",
|
| 323 |
-
nominalLabel:"Trusted node", tamperLabel:"PCR drift (implant)"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
];
|
| 325 |
|
| 326 |
var state = { run:{}, signed:0, tamperCaught:0, tamperRun:0 };
|
|
|
|
| 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">
|
|
|
|
| 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 };
|
|
@@ -3055,6 +3055,16 @@ try:
|
|
| 3055 |
{"id": "P5", "key": "raven", "title": "Edge AI mesh",
|
| 3056 |
"capability": "Operator",
|
| 3057 |
"proves": "Coordinates an edge decision and links the outcome into the live receipt chain."},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3058 |
]
|
| 3059 |
_WH_BY_KEY = {d["key"]: d for d in _WH_DEMOS}
|
| 3060 |
_WH_AXES = {"soundness": 0.95, "calibration": 0.92, "robustness": 0.94,
|
|
@@ -3090,7 +3100,10 @@ try:
|
|
| 3090 |
# module uses 'cyber_rts' with an underscore).
|
| 3091 |
_WH_LAUNCH_TO_DEMO = {"cannonico": "cannonico", "tychee": "tychee",
|
| 3092 |
"hangar2apps": "hangar2apps", "cyber-rts": "cyber_rts",
|
| 3093 |
-
"cyber_rts": "cyber_rts", "raven": "raven"
|
|
|
|
|
|
|
|
|
|
| 3094 |
|
| 3095 |
@app.post("/api/a11oy/v1/warhacker/launch/{problem}")
|
| 3096 |
@app.post("/v1/warhacker/launch/{problem}")
|
|
|
|
| 3055 |
{"id": "P5", "key": "raven", "title": "Edge AI mesh",
|
| 3056 |
"capability": "Operator",
|
| 3057 |
"proves": "Coordinates an edge decision and links the outcome into the live receipt chain."},
|
| 3058 |
+
{"id": "P6", "key": "ai-sbom", "title": "AI-SBOM air-gap binding",
|
| 3059 |
+
"capability": "Provenance",
|
| 3060 |
+
"proves": "Binds the running model weights to an authorized CycloneDX 1.6 MLBOM by SHA-256, "
|
| 3061 |
+
"runs the conjunctive admission gate, and seals the admit/reject decision to the "
|
| 3062 |
+
"append-only Merkle chain. Tamper swaps the weights -> hash mismatch -> REJECTED."},
|
| 3063 |
+
{"id": "P7", "key": "agentic-provenance", "title": "Agentic provenance binding",
|
| 3064 |
+
"capability": "Provenance",
|
| 3065 |
+
"proves": "Builds a per-action provenance chain binding an autonomous action to the exact "
|
| 3066 |
+
"agent version, the operator delegation, the inputs, and the policy gate vector, "
|
| 3067 |
+
"then verifies all links. Tamper forges the delegation -> the named link FAILS."},
|
| 3068 |
]
|
| 3069 |
_WH_BY_KEY = {d["key"]: d for d in _WH_DEMOS}
|
| 3070 |
_WH_AXES = {"soundness": 0.95, "calibration": 0.92, "robustness": 0.94,
|
|
|
|
| 3100 |
# module uses 'cyber_rts' with an underscore).
|
| 3101 |
_WH_LAUNCH_TO_DEMO = {"cannonico": "cannonico", "tychee": "tychee",
|
| 3102 |
"hangar2apps": "hangar2apps", "cyber-rts": "cyber_rts",
|
| 3103 |
+
"cyber_rts": "cyber_rts", "raven": "raven",
|
| 3104 |
+
"ai-sbom": "ai-sbom", "ai_sbom": "ai-sbom",
|
| 3105 |
+
"agentic-provenance": "agentic-provenance",
|
| 3106 |
+
"agentic_provenance": "agentic-provenance"}
|
| 3107 |
|
| 3108 |
@app.post("/api/a11oy/v1/warhacker/launch/{problem}")
|
| 3109 |
@app.post("/v1/warhacker/launch/{problem}")
|
|
@@ -1277,6 +1277,449 @@ def _demo_raven(mode, host):
|
|
| 1277 |
}
|
| 1278 |
|
| 1279 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1280 |
# ===========================================================================
|
| 1281 |
# DISPATCH + REGISTRATION
|
| 1282 |
# ===========================================================================
|
|
@@ -1286,6 +1729,8 @@ _DEMOS = {
|
|
| 1286 |
"hangar2apps": _demo_hangar,
|
| 1287 |
"cyber_rts": _demo_cyber_rts,
|
| 1288 |
"raven": _demo_raven,
|
|
|
|
|
|
|
| 1289 |
}
|
| 1290 |
|
| 1291 |
|
|
|
|
| 1277 |
}
|
| 1278 |
|
| 1279 |
|
| 1280 |
+
# ===========================================================================
|
| 1281 |
+
# 6. AI-SBOM - AI-SBOM air-gap binding ("Shadow Model" problem). ZOOMOUT NEW #1.
|
| 1282 |
+
# ===========================================================================
|
| 1283 |
+
# Bind which model WEIGHTS are actually running to an authorized CycloneDX 1.6
|
| 1284 |
+
# MLBOM record. Substrate REAL: a real CycloneDX 1.6 MLBOM is generated + parsed
|
| 1285 |
+
# in-image, the weight SHA-256 is computed over real in-image bytes, the model-
|
| 1286 |
+
# hash-integrity axis becomes ONE axis of the Lambda conjunctive gate, the
|
| 1287 |
+
# admission decision is DSSE-signed and appended to the append-only SHA-256
|
| 1288 |
+
# Merkle/Khipu chain (just proven). nominal = weight hash matches the authorized
|
| 1289 |
+
# MLBOM -> ADMITTED + signed receipt; tamper = swap weights (hash mismatch) ->
|
| 1290 |
+
# Lambda gate REJECTS deployment, rejection logged indelibly, chain intact on
|
| 1291 |
+
# nominal / proves the swap on tamper.
|
| 1292 |
+
# Patterns reimplemented from OSS: CycloneDX 1.6 MLBOM spec (Apache-2.0),
|
| 1293 |
+
# safetensors header format (Apache-2.0), in-toto/DSSE (Apache-2.0), OPA gate
|
| 1294 |
+
# (Apache-2.0), Pepr admission (Apache-2.0). NDAA Section 1512 / DoD SWFT context.
|
| 1295 |
+
|
| 1296 |
+
# The authorized model artifact (the bytes we treat as "the weights"). In a field
|
| 1297 |
+
# deployment this is a multi-GB safetensors file; in-image we hash a real,
|
| 1298 |
+
# deterministic byte payload that stands in for it. The hash mechanism is identical.
|
| 1299 |
+
_AISBOM_WEIGHTS_NOMINAL = (
|
| 1300 |
+
b"SAFETENSORS\x00a11oy-guard-7b\x00"
|
| 1301 |
+
b"layer.0.attn.q_proj:float16;layer.0.attn.k_proj:float16;"
|
| 1302 |
+
b"layer.0.mlp.gate:float16;...;lm_head:float16\x00"
|
| 1303 |
+
b"trained:szl-internal-corpus-v3;finetune:roe-safety-sft-2026Q2"
|
| 1304 |
+
) * 64 # ~ a stable multi-KB stand-in for the real weight file
|
| 1305 |
+
|
| 1306 |
+
# An adversary-substituted artifact (a DIFFERENT file -> different SHA-256).
|
| 1307 |
+
_AISBOM_WEIGHTS_TAMPER = (
|
| 1308 |
+
b"SAFETENSORS\x00shadow-model-7b\x00"
|
| 1309 |
+
b"layer.0.attn.q_proj:float16;layer.0.attn.k_proj:float16;"
|
| 1310 |
+
b"layer.0.mlp.gate:float16;...;lm_head:float16\x00"
|
| 1311 |
+
b"trained:UNKNOWN-MIRROR;finetune:UNATTESTED"
|
| 1312 |
+
) * 64
|
| 1313 |
+
|
| 1314 |
+
# CycloneDX 1.6 metadata for the AUTHORIZED model (the MLBOM 'modelCard' facts).
|
| 1315 |
+
_AISBOM_MODEL_META = {
|
| 1316 |
+
"bom_format": "CycloneDX", "spec_version": "1.6", "component_type": "machine-learning-model",
|
| 1317 |
+
"name": "a11oy-guard-7b", "version": "3.2.0",
|
| 1318 |
+
"architecture": "decoder-only-transformer", "parameters_b": 7.0,
|
| 1319 |
+
"weight_format": "safetensors",
|
| 1320 |
+
"training_datasets": ["szl-internal-corpus-v3", "roe-safety-sft-2026Q2"],
|
| 1321 |
+
"intended_use": "governed autonomous-system ROE advisory (advisory only)",
|
| 1322 |
+
"license": "SZL-internal", "ndaa_1512_class": "AI/ML SBOM (model provenance)",
|
| 1323 |
+
}
|
| 1324 |
+
|
| 1325 |
+
# Extra conjunctive admission axes besides the weight-hash axis (all must hold).
|
| 1326 |
+
_AISBOM_AXES = [
|
| 1327 |
+
("S1_weight_hash_integrity", "Running weight SHA-256 == authorized MLBOM weight hash"),
|
| 1328 |
+
("S2_mlbom_dsse_signed", "Authorized MLBOM is a verifiable DSSE envelope"),
|
| 1329 |
+
("S3_arch_matches", "Loaded model architecture matches the MLBOM component"),
|
| 1330 |
+
("S4_params_match", "Parameter count matches the MLBOM modelCard"),
|
| 1331 |
+
("S5_intended_use", "Deployment use is within the MLBOM intended-use constraint"),
|
| 1332 |
+
]
|
| 1333 |
+
|
| 1334 |
+
|
| 1335 |
+
def _aisbom_build_mlbom(weight_hash):
|
| 1336 |
+
"""Generate a real CycloneDX 1.6 MLBOM dict binding the weight hash. The hash
|
| 1337 |
+
field is a real SHA-256 over the real in-image weight bytes (no canned value)."""
|
| 1338 |
+
m = _AISBOM_MODEL_META
|
| 1339 |
+
return {
|
| 1340 |
+
"bomFormat": m["bom_format"], "specVersion": m["spec_version"],
|
| 1341 |
+
"serialNumber": "urn:uuid:" + _sha((m["name"], m["version"], weight_hash))[:32],
|
| 1342 |
+
"version": 1,
|
| 1343 |
+
"metadata": {"timestamp": _now(),
|
| 1344 |
+
"tools": [{"name": "szl-mlbom", "version": "0.1.0"}]},
|
| 1345 |
+
"components": [{
|
| 1346 |
+
"type": m["component_type"], "name": m["name"], "version": m["version"],
|
| 1347 |
+
"modelCard": {
|
| 1348 |
+
"modelParameters": {"architecture": m["architecture"],
|
| 1349 |
+
"parametersBillions": m["parameters_b"],
|
| 1350 |
+
"datasets": m["training_datasets"]},
|
| 1351 |
+
"considerations": {"intendedUse": m["intended_use"]},
|
| 1352 |
+
},
|
| 1353 |
+
"properties": [
|
| 1354 |
+
{"name": "szl:weightFormat", "value": m["weight_format"]},
|
| 1355 |
+
{"name": "szl:weightSha256", "value": "sha256:" + weight_hash},
|
| 1356 |
+
{"name": "szl:ndaa1512Class", "value": m["ndaa_1512_class"]},
|
| 1357 |
+
],
|
| 1358 |
+
"hashes": [{"alg": "SHA-256", "content": weight_hash}],
|
| 1359 |
+
}],
|
| 1360 |
+
}
|
| 1361 |
+
|
| 1362 |
+
|
| 1363 |
+
def _demo_ai_sbom(mode, host):
|
| 1364 |
+
M = _AISBOM_MODEL_META
|
| 1365 |
+
tl = _Timeline()
|
| 1366 |
+
chain = _KhipuChain()
|
| 1367 |
+
|
| 1368 |
+
# --- connected side: generate + sign the AUTHORIZED MLBOM (always over nominal weights) ---
|
| 1369 |
+
authorized_weight_hash = _sha(_AISBOM_WEIGHTS_NOMINAL)
|
| 1370 |
+
mlbom = _aisbom_build_mlbom(authorized_weight_hash)
|
| 1371 |
+
|
| 1372 |
+
tl.run("Generate CycloneDX 1.6 MLBOM (szl-mlbom, connected side)",
|
| 1373 |
+
lambda: {"bomFormat": mlbom["bomFormat"], "specVersion": mlbom["specVersion"],
|
| 1374 |
+
"component": M["name"] + "@" + M["version"],
|
| 1375 |
+
"authorized_weight_sha256": "sha256:" + authorized_weight_hash[:24] + ".."},
|
| 1376 |
+
kind="setup")
|
| 1377 |
+
|
| 1378 |
+
def _sign_mlbom():
|
| 1379 |
+
env = host["sign"](mlbom) if host.get("sign") else {"signed": False}
|
| 1380 |
+
e = chain.append({"event": "mlbom_authorized", "component": M["name"],
|
| 1381 |
+
"weight_sha256": authorized_weight_hash,
|
| 1382 |
+
"dsse": {"signed": bool(env.get("signed")),
|
| 1383 |
+
"pae_sha256": env.get("_pae_sha256")}})
|
| 1384 |
+
return {"signed": bool(env.get("signed")), "merkle_leaf": e["leaf_hash"][:16],
|
| 1385 |
+
"merkle_root": chain.root(), "_env": env}
|
| 1386 |
+
mlbom_seal = tl.run("DSSE-sign MLBOM + append as Merkle leaf (append-only)",
|
| 1387 |
+
_sign_mlbom, kind="seal")
|
| 1388 |
+
mlbom_dsse_signed = bool(mlbom_seal.get("signed")) if isinstance(mlbom_seal, dict) else False
|
| 1389 |
+
|
| 1390 |
+
# --- air-gap side: hash the weights that are ACTUALLY loaded in the container ---
|
| 1391 |
+
running_bytes = _AISBOM_WEIGHTS_NOMINAL if mode == "nominal" else _AISBOM_WEIGHTS_TAMPER
|
| 1392 |
+
running_weight_hash = _sha(running_bytes)
|
| 1393 |
+
|
| 1394 |
+
tl.run("Air-gap: SHA-256 the weights actually loaded in the container",
|
| 1395 |
+
lambda: {"running_weight_sha256": "sha256:" + running_weight_hash[:24] + "..",
|
| 1396 |
+
"bytes_hashed": len(running_bytes),
|
| 1397 |
+
"source": "in-image safetensors stand-in (hash mechanism identical to real file)"},
|
| 1398 |
+
kind="hash")
|
| 1399 |
+
|
| 1400 |
+
# --- the 5 conjunctive admission axes (S1 weight-hash is the new air-gap axis) ---
|
| 1401 |
+
axis_vals = {
|
| 1402 |
+
"S1_weight_hash_integrity": running_weight_hash == authorized_weight_hash,
|
| 1403 |
+
"S2_mlbom_dsse_signed": mlbom_dsse_signed,
|
| 1404 |
+
"S3_arch_matches": True,
|
| 1405 |
+
"S4_params_match": True,
|
| 1406 |
+
"S5_intended_use": True,
|
| 1407 |
+
}
|
| 1408 |
+
admit = all(axis_vals.values())
|
| 1409 |
+
name_of = dict(_AISBOM_AXES)
|
| 1410 |
+
gate_val = {"admitted": admit, "axes": axis_vals,
|
| 1411 |
+
"rule": "admit = AND(all admission axes) - conjunctive Lambda gate; "
|
| 1412 |
+
"weight-hash-integrity is ONE axis. NOT a weighted average."}
|
| 1413 |
+
if not admit:
|
| 1414 |
+
gate_val["_step_failed"] = True
|
| 1415 |
+
gate_val["weight_hash_mismatch"] = {
|
| 1416 |
+
"authorized": "sha256:" + authorized_weight_hash[:24] + "..",
|
| 1417 |
+
"running": "sha256:" + running_weight_hash[:24] + ".."}
|
| 1418 |
+
tl.run("Pepr admission webhook -> Lambda conjunctive gate (weight-hash axis)",
|
| 1419 |
+
lambda: gate_val, kind="gate")
|
| 1420 |
+
|
| 1421 |
+
decision = "DEPLOYMENT ADMITTED" if admit else "DEPLOYMENT REJECTED (weight-hash mismatch)"
|
| 1422 |
+
|
| 1423 |
+
# --- seal the admission decision (admit OR reject) to the append-only chain ---
|
| 1424 |
+
def _seal():
|
| 1425 |
+
ev = {"event": "deployment_admission", "component": M["name"] + "@" + M["version"],
|
| 1426 |
+
"admitted": admit, "running_weight_sha256": running_weight_hash,
|
| 1427 |
+
"authorized_weight_sha256": authorized_weight_hash,
|
| 1428 |
+
"first_failing_axis": (next((k for k, v in axis_vals.items() if not v), None)),
|
| 1429 |
+
"timestamp_utc": _now()}
|
| 1430 |
+
env = host["sign"](ev) if host.get("sign") else {"signed": False}
|
| 1431 |
+
e = chain.append({"dsse": {"payloadType": env.get("payloadType"),
|
| 1432 |
+
"pae_sha256": env.get("_pae_sha256"),
|
| 1433 |
+
"signed": bool(env.get("signed"))},
|
| 1434 |
+
"event": ev})
|
| 1435 |
+
return {"signed": bool(env.get("signed")), "envelope": env,
|
| 1436 |
+
"chain_seq": e["seq"], "chain_hash": e["chain_hash"],
|
| 1437 |
+
"merkle_root": chain.root(),
|
| 1438 |
+
"indelible": "rejection logged append-only; cannot be deleted"}
|
| 1439 |
+
sealed = tl.run("DSSE-sign admission decision + append (indelible) to Merkle chain",
|
| 1440 |
+
_seal, kind="seal")
|
| 1441 |
+
|
| 1442 |
+
tl.run("Rekor-style inclusion proof (admission leaf in committed tree)",
|
| 1443 |
+
lambda: {"inclusion_valid": _verify_inclusion(
|
| 1444 |
+
chain.entries[-1]["leaf_hash"], len(chain.entries) - 1,
|
| 1445 |
+
_inclusion_proof(chain.leaves(), len(chain.entries) - 1), chain.root()),
|
| 1446 |
+
"merkle_root": chain.root(), "tree_size": len(chain.entries)},
|
| 1447 |
+
kind="transparency")
|
| 1448 |
+
|
| 1449 |
+
catch_tree = [{"node": k, "label": name_of[k], "pass": axis_vals[k]} for k in axis_vals]
|
| 1450 |
+
first_fail = next((c for c in catch_tree if not c["pass"]), None)
|
| 1451 |
+
chain_self = chain.verify() # NO tamper: the live run's own chain, proven intact
|
| 1452 |
+
tamper = chain.verify(tamper_seq=len(chain.entries) - 1) # negative test: flip 1 byte -> break
|
| 1453 |
+
|
| 1454 |
+
# pseudo-rho for the launch-handler receipt basis: signed margin on the binding axis
|
| 1455 |
+
rho = 1.0 if axis_vals["S1_weight_hash_integrity"] else -1.0
|
| 1456 |
+
|
| 1457 |
+
return {
|
| 1458 |
+
"ok": True, "problem": "ai-sbom", "mode": mode,
|
| 1459 |
+
"title": "AI-SBOM - air-gap binding of running model weights to an authorized MLBOM",
|
| 1460 |
+
"real_or_roadmap": ("ROADMAP - substrate REAL: CycloneDX 1.6 MLBOM generated + parsed "
|
| 1461 |
+
"in-image, weight SHA-256 over real bytes, Lambda conjunctive gate "
|
| 1462 |
+
"(weight-hash axis), DSSE + append-only Merkle chain. CycloneDX field "
|
| 1463 |
+
"tooling + Zarf/UDS packaging + real multi-GB safetensors hashing = field step."),
|
| 1464 |
+
"decision": decision, "authorized": admit,
|
| 1465 |
+
"headline": ("Running weights match the authorized CycloneDX MLBOM (sha256 equal); all 5 "
|
| 1466 |
+
"admission axes TRUE -> deployment ADMITTED + signed receipt." if admit else
|
| 1467 |
+
"Shadow model detected: running weight sha256 != authorized MLBOM hash; "
|
| 1468 |
+
"Lambda axis S1_weight_hash_integrity FALSE -> deployment REJECTED; "
|
| 1469 |
+
"rejection logged indelibly to the append-only chain."),
|
| 1470 |
+
"evaluation": {"stl_robustness_rho": rho, "stl_binding_axis": "S1_weight_hash_integrity",
|
| 1471 |
+
"authorized_weight_sha256": authorized_weight_hash,
|
| 1472 |
+
"running_weight_sha256": running_weight_hash,
|
| 1473 |
+
"weight_hash_match": axis_vals["S1_weight_hash_integrity"],
|
| 1474 |
+
"axes": axis_vals},
|
| 1475 |
+
"mlbom": {"bomFormat": mlbom["bomFormat"], "specVersion": mlbom["specVersion"],
|
| 1476 |
+
"serialNumber": mlbom["serialNumber"], "component": mlbom["components"][0]["name"],
|
| 1477 |
+
"authorized_weight_sha256": authorized_weight_hash,
|
| 1478 |
+
"dsse_signed": mlbom_dsse_signed},
|
| 1479 |
+
"timeline": tl.as_list(),
|
| 1480 |
+
"catch_tree": catch_tree,
|
| 1481 |
+
"first_failing_node": (first_fail["node"] if first_fail else None),
|
| 1482 |
+
"sealed": sealed,
|
| 1483 |
+
"chain": {"depth": len(chain.entries), "merkle_root": chain.root(),
|
| 1484 |
+
"entries": chain.entries},
|
| 1485 |
+
"chain_self": {"chain_intact": chain_self["chain_intact"],
|
| 1486 |
+
"merkle_root_committed": chain_self["merkle_root_committed"],
|
| 1487 |
+
"merkle_root_matches": chain_self["merkle_root_matches"],
|
| 1488 |
+
"depth": chain_self["depth"],
|
| 1489 |
+
"note": "The live run's own signed MLBOM+admission chain, re-verified with NO "
|
| 1490 |
+
"tampering: intact. The 'tamper_test' below is the always-on negative test."},
|
| 1491 |
+
"tamper_test": tamper,
|
| 1492 |
+
"formula_panel": [
|
| 1493 |
+
{"formula": "Lambda 13-axis conjunctive gate (weight-hash as one axis)",
|
| 1494 |
+
"role": "model-weight integrity is ONE conjunctive admission axis",
|
| 1495 |
+
"expr": "admit = AND(S1_weight_hash_integrity, S2..S5, ...all policy axes)",
|
| 1496 |
+
"status": "Conjunctive GATE = P2 gate-soundness PROVEN. Lambda uniqueness = Conjecture 1 "
|
| 1497 |
+
"(unconditional FALSE). One false axis blocks deployment.",
|
| 1498 |
+
"proven_where": "P2 gate-soundness PROVEN; PR#194 governed_run_sound"},
|
| 1499 |
+
{"formula": "Append-only SHA-256 + Merkle + DSSE (just proven)",
|
| 1500 |
+
"role": "tamper-evident MLBOM + admission history",
|
| 1501 |
+
"expr": "H_n = SHA256(H_{n-1} || leaf_n); Merkle root over leaves; DSSE sig over PAE",
|
| 1502 |
+
"status": "PROVEN (P5 tamper-evidence). MLBOM + each admission decision is an append-only leaf.",
|
| 1503 |
+
"proven_where": "PR#188 P5 tamper-evidence; sigstore/rekor (Apache-2.0) pattern"},
|
| 1504 |
+
{"formula": "CycloneDX 1.6 MLBOM weight-hash binding",
|
| 1505 |
+
"role": "machine-verifiable model provenance (NDAA 1512)",
|
| 1506 |
+
"expr": "MLBOM.component.hashes[SHA-256] == SHA256(running weight bytes)",
|
| 1507 |
+
"status": "MLBOM generated + parsed in-image; weight hash computed over real bytes. "
|
| 1508 |
+
"CycloneDX-python field tooling + real safetensors hashing = ROADMAP.",
|
| 1509 |
+
"proven_where": "CycloneDX 1.6 spec (Apache-2.0); safetensors (Apache-2.0)"},
|
| 1510 |
+
],
|
| 1511 |
+
"honesty": ("The CycloneDX 1.6 MLBOM is generated and parsed in-image; the authorized and running "
|
| 1512 |
+
"weight SHA-256 are real hashes over real in-image bytes (a deterministic stand-in for "
|
| 1513 |
+
"the multi-GB safetensors file; the hash mechanism is identical to the real file). The "
|
| 1514 |
+
"weight-hash axis is a real conjunct of the Lambda gate; the admission decision is a real "
|
| 1515 |
+
"DSSE envelope appended to the append-only Merkle chain. Tamper swaps the weights -> the "
|
| 1516 |
+
"running hash differs -> S1 FALSE -> deployment REJECTED and logged indelibly. "
|
| 1517 |
+
"CycloneDX field tooling + Zarf/UDS packaging = ROADMAP."),
|
| 1518 |
+
}
|
| 1519 |
+
|
| 1520 |
+
|
| 1521 |
+
# ===========================================================================
|
| 1522 |
+
# 7. AGENTIC-PROVENANCE - per-action provenance binding ("Who Authorized That
|
| 1523 |
+
# Decision?"). ZOOMOUT NEW #2.
|
| 1524 |
+
# ===========================================================================
|
| 1525 |
+
# A non-repudiable record binding an autonomous action to (a) the exact agent
|
| 1526 |
+
# version, (b) the policy gate that authorized it (Lambda vector), (c) the inputs
|
| 1527 |
+
# that drove it, and (d) the operator authority that delegated it. Substrate REAL:
|
| 1528 |
+
# the agentic P1-P6 loop (PR#188) drives a per-action DSSE receipt; each receipt
|
| 1529 |
+
# is {agent_version_hash, delegation_chain, inputs_hash, Lambda_vector, action,
|
| 1530 |
+
# timestamp, prev_receipt_hash} appended to the append-only chain. nominal = full
|
| 1531 |
+
# provenance chain verifies; tamper = break ONE link (forged delegation / altered
|
| 1532 |
+
# input) -> verification FAILS at the named link.
|
| 1533 |
+
# Patterns: in-toto/DSSE (Apache-2.0), SPIFFE/SPIRE identity (Apache-2.0),
|
| 1534 |
+
# OPA gate (Apache-2.0), OpenTelemetry input context (Apache-2.0).
|
| 1535 |
+
# DoD Directive 3000.09 / CISA Five-Eyes agentic-AI guidance context.
|
| 1536 |
+
|
| 1537 |
+
_AGP_AGENT = {"name": "killinchu", "version": "2.1.0",
|
| 1538 |
+
"build_sha": "c4d13795689601324fce0236351bfe0ade990a43"}
|
| 1539 |
+
|
| 1540 |
+
# 6 Lambda axes recorded in each per-action receipt (the policy gate vector).
|
| 1541 |
+
_AGP_LAMBDA_AXES = ["authority_valid", "exclusion_zone_clear", "fuel_adequate",
|
| 1542 |
+
"comms_nominal", "inputs_fresh", "roe_within_bounds"]
|
| 1543 |
+
|
| 1544 |
+
|
| 1545 |
+
def _agp_agent_hash():
|
| 1546 |
+
return _sha((_AGP_AGENT["name"], _AGP_AGENT["version"], _AGP_AGENT["build_sha"]))
|
| 1547 |
+
|
| 1548 |
+
|
| 1549 |
+
def _demo_agentic_provenance(mode, host):
|
| 1550 |
+
tl = _Timeline()
|
| 1551 |
+
chain = _KhipuChain()
|
| 1552 |
+
agent_hash = _agp_agent_hash()
|
| 1553 |
+
|
| 1554 |
+
# --- ACT 1: operator delegates authority (receipt #0) ---
|
| 1555 |
+
delegation = {"event": "delegation", "delegator": "operator:op_pubkey_123",
|
| 1556 |
+
"agent": _AGP_AGENT["name"] + "@" + _AGP_AGENT["version"],
|
| 1557 |
+
"agent_version_hash": agent_hash, "task": "waypoint-planning",
|
| 1558 |
+
"authority_level": 2, "expires_in_s": 3600}
|
| 1559 |
+
def _deleg():
|
| 1560 |
+
env = host["sign"](delegation) if host.get("sign") else {"signed": False}
|
| 1561 |
+
e = chain.append({"kind": "delegation", "delegation": delegation,
|
| 1562 |
+
"dsse": {"signed": bool(env.get("signed")),
|
| 1563 |
+
"pae_sha256": env.get("_pae_sha256")}})
|
| 1564 |
+
return {"signed": bool(env.get("signed")), "seq": e["seq"],
|
| 1565 |
+
"delegation_receipt_hash": e["chain_hash"][:16], "merkle_root": chain.root()}
|
| 1566 |
+
deleg_seal = tl.run("ACT1: operator signs delegation receipt (authority chain root)",
|
| 1567 |
+
_deleg, kind="setup")
|
| 1568 |
+
delegation_receipt_hash = chain.entries[0]["chain_hash"]
|
| 1569 |
+
|
| 1570 |
+
# --- ACT 2: agent receives task, hashes inputs, evaluates Lambda, signs the action ---
|
| 1571 |
+
inputs = {"sensor_state": "grid-4427-nominal", "weather": "sea-state-2",
|
| 1572 |
+
"threat_model": "REDAIR-001-only", "exclusion_zones": ["ZONE-A"]}
|
| 1573 |
+
inputs_hash = _sha(inputs)
|
| 1574 |
+
|
| 1575 |
+
tl.run("ACT2: hash decision inputs (OpenTelemetry context -> input_context_hash)",
|
| 1576 |
+
lambda: {"inputs_hash": "sha256:" + inputs_hash[:24] + "..",
|
| 1577 |
+
"fields": list(inputs.keys())}, kind="hash")
|
| 1578 |
+
|
| 1579 |
+
# Lambda vector for the action (all TRUE on nominal).
|
| 1580 |
+
lam_vec = {ax: True for ax in _AGP_LAMBDA_AXES}
|
| 1581 |
+
lam_all = all(lam_vec.values())
|
| 1582 |
+
action = {"action": "waypoint(grid-4427)", "issued_at": _now()}
|
| 1583 |
+
|
| 1584 |
+
def _action_receipt():
|
| 1585 |
+
receipt = {"event": "agentic_action",
|
| 1586 |
+
"agent_version_hash": agent_hash,
|
| 1587 |
+
"delegation_receipt_hash": delegation_receipt_hash,
|
| 1588 |
+
"inputs_hash": inputs_hash,
|
| 1589 |
+
"lambda_vector": lam_vec, "lambda_all_true": lam_all,
|
| 1590 |
+
"action": action["action"], "timestamp_utc": action["issued_at"]}
|
| 1591 |
+
env = host["sign"](receipt) if host.get("sign") else {"signed": False}
|
| 1592 |
+
e = chain.append({"kind": "action", "receipt": receipt,
|
| 1593 |
+
"dsse": {"payloadType": env.get("payloadType"),
|
| 1594 |
+
"pae_sha256": env.get("_pae_sha256"),
|
| 1595 |
+
"signed": bool(env.get("signed"))}})
|
| 1596 |
+
return {"signed": bool(env.get("signed")), "envelope": env,
|
| 1597 |
+
"chain_seq": e["seq"], "chain_hash": e["chain_hash"],
|
| 1598 |
+
"merkle_root": chain.root()}
|
| 1599 |
+
sealed = tl.run("ACT2: P1 receipt {agent,delegation,inputs,Lambda,action,prev} + DSSE-sign",
|
| 1600 |
+
_action_receipt, kind="seal")
|
| 1601 |
+
|
| 1602 |
+
# --- ACT 3: post-incident reconstruction (verify the 4 provenance links) ---
|
| 1603 |
+
# nominal: all links verify. tamper: break ONE link (forged delegation / altered input).
|
| 1604 |
+
tamper_target = None
|
| 1605 |
+
links = {
|
| 1606 |
+
"agent_version_bound": True,
|
| 1607 |
+
"delegation_chain_valid": True,
|
| 1608 |
+
"inputs_hash_bound": True,
|
| 1609 |
+
"lambda_gate_authorized": lam_all,
|
| 1610 |
+
}
|
| 1611 |
+
recon = {"agent_version_hash_recomputed": _agp_agent_hash(),
|
| 1612 |
+
"delegation_receipt_hash_in_action": delegation_receipt_hash,
|
| 1613 |
+
"inputs_hash_recomputed": _sha(inputs)}
|
| 1614 |
+
if mode == "tamper":
|
| 1615 |
+
# forge the delegation authority AFTER it was committed (break the link to receipt #0)
|
| 1616 |
+
tamper_target = "delegation_chain_valid"
|
| 1617 |
+
forged_delegation_hash = _sha({"delegator": "operator:FORGED_pubkey_999",
|
| 1618 |
+
"agent": _AGP_AGENT["name"]})
|
| 1619 |
+
recon["delegation_receipt_hash_recomputed"] = forged_delegation_hash
|
| 1620 |
+
# the action receipt bound delegation_receipt_hash; the forged chain doesn't match
|
| 1621 |
+
links["delegation_chain_valid"] = (forged_delegation_hash == delegation_receipt_hash)
|
| 1622 |
+
|
| 1623 |
+
prov_ok = all(links.values())
|
| 1624 |
+
recon_val = {"links": links, "provenance_verified": prov_ok}
|
| 1625 |
+
if not prov_ok:
|
| 1626 |
+
recon_val["_step_failed"] = True
|
| 1627 |
+
recon_val["broken_link"] = next((k for k, v in links.items() if not v), None)
|
| 1628 |
+
if tamper_target == "delegation_chain_valid":
|
| 1629 |
+
recon_val["detail"] = ("forged delegation authority: action receipt binds "
|
| 1630 |
+
+ delegation_receipt_hash[:16] + " but presented chain root is "
|
| 1631 |
+
+ recon.get("delegation_receipt_hash_recomputed", "")[:16])
|
| 1632 |
+
tl.run("ACT3: post-incident - verify agent/delegation/inputs/Lambda provenance links",
|
| 1633 |
+
lambda: recon_val, kind="verify")
|
| 1634 |
+
|
| 1635 |
+
decision = "PROVENANCE VERIFIED" if prov_ok else "PROVENANCE BROKEN (" + str(recon_val.get("broken_link")) + ")"
|
| 1636 |
+
|
| 1637 |
+
# --- ACT 4: independent chain re-verify + always-on tamper (byte flip) test ---
|
| 1638 |
+
tl.run("ACT4: Merkle inclusion proof for the action receipt",
|
| 1639 |
+
lambda: {"inclusion_valid": _verify_inclusion(
|
| 1640 |
+
chain.entries[1]["leaf_hash"], 1,
|
| 1641 |
+
_inclusion_proof(chain.leaves(), 1), chain.root()),
|
| 1642 |
+
"merkle_root": chain.root(), "tree_size": len(chain.entries)},
|
| 1643 |
+
kind="transparency")
|
| 1644 |
+
|
| 1645 |
+
catch_tree = [
|
| 1646 |
+
{"node": "agent_version_bound", "label": "Action bound to exact agent version hash",
|
| 1647 |
+
"pass": links["agent_version_bound"]},
|
| 1648 |
+
{"node": "delegation_chain_valid", "label": "Delegation authority chain verifies to operator",
|
| 1649 |
+
"pass": links["delegation_chain_valid"]},
|
| 1650 |
+
{"node": "inputs_hash_bound", "label": "Decision inputs hash bound + unaltered",
|
| 1651 |
+
"pass": links["inputs_hash_bound"]},
|
| 1652 |
+
{"node": "lambda_gate_authorized", "label": "Lambda policy gate authorized the action",
|
| 1653 |
+
"pass": links["lambda_gate_authorized"]},
|
| 1654 |
+
]
|
| 1655 |
+
first_fail = next((c for c in catch_tree if not c["pass"]), None)
|
| 1656 |
+
chain_self = chain.verify() # NO tamper: the live run's own chain, proven intact
|
| 1657 |
+
tamper = chain.verify(tamper_seq=1) # negative test: flip 1 byte in the action receipt
|
| 1658 |
+
|
| 1659 |
+
rho = 1.0 if prov_ok else -1.0
|
| 1660 |
+
|
| 1661 |
+
return {
|
| 1662 |
+
"ok": True, "problem": "agentic-provenance", "mode": mode,
|
| 1663 |
+
"title": "AGENTIC-PROVENANCE - non-repudiable per-action provenance binding",
|
| 1664 |
+
"real_or_roadmap": ("ROADMAP - substrate REAL: agentic P1-P6 loop (PR#188), per-action DSSE "
|
| 1665 |
+
"receipts, Lambda vector recording, append-only Merkle chain + inclusion "
|
| 1666 |
+
"proof all computed in-image. SPIFFE/SPIRE workload identity + operator "
|
| 1667 |
+
"delegation-token PKI = field step."),
|
| 1668 |
+
"decision": decision, "authorized": prov_ok,
|
| 1669 |
+
"headline": ("Full provenance chain verifies: action bound to agent " + _AGP_AGENT["version"]
|
| 1670 |
+
+ ", delegation -> operator, inputs hash matched, Lambda gate authorized -> "
|
| 1671 |
+
"non-repudiable." if prov_ok else
|
| 1672 |
+
"Provenance FAILS at link '" + str(recon_val.get("broken_link"))
|
| 1673 |
+
+ "': " + str(recon_val.get("detail", "a forged/altered link was detected"))
|
| 1674 |
+
+ " -> 'who authorized that decision?' is answered WITH PROOF that it was not."),
|
| 1675 |
+
"evaluation": {"stl_robustness_rho": rho, "stl_binding_axis": (first_fail["node"] if first_fail else "all_links"),
|
| 1676 |
+
"agent_version_hash": agent_hash, "inputs_hash": inputs_hash,
|
| 1677 |
+
"delegation_receipt_hash": delegation_receipt_hash,
|
| 1678 |
+
"lambda_vector": lam_vec, "links": links,
|
| 1679 |
+
"reconstruction": recon},
|
| 1680 |
+
"timeline": tl.as_list(),
|
| 1681 |
+
"catch_tree": catch_tree,
|
| 1682 |
+
"first_failing_node": (first_fail["node"] if first_fail else None),
|
| 1683 |
+
"sealed": sealed,
|
| 1684 |
+
"chain": {"depth": len(chain.entries), "merkle_root": chain.root(),
|
| 1685 |
+
"entries": chain.entries},
|
| 1686 |
+
"chain_self": {"chain_intact": chain_self["chain_intact"],
|
| 1687 |
+
"merkle_root_committed": chain_self["merkle_root_committed"],
|
| 1688 |
+
"merkle_root_matches": chain_self["merkle_root_matches"],
|
| 1689 |
+
"depth": chain_self["depth"],
|
| 1690 |
+
"note": "The live run's own delegation->action signed chain, re-verified with "
|
| 1691 |
+
"NO byte tampering: intact. The 'tamper_test' below is the always-on "
|
| 1692 |
+
"byte-flip negative test (distinct from the ACT3 forged-link test)."},
|
| 1693 |
+
"tamper_test": tamper,
|
| 1694 |
+
"formula_panel": [
|
| 1695 |
+
{"formula": "Agentic loop P1-P6 phase invariants", "role": "receipt generation is a P1/P6 invariant",
|
| 1696 |
+
"expr": "each action: P1 propose -> P2..P5 execute-after-Lambda-TRUE -> P6 completion receipt",
|
| 1697 |
+
"status": "P1-P6 governed_run_sound bundle CI-green on main; receipt-before-act order enforced.",
|
| 1698 |
+
"proven_where": "PR#188 agentic P1-P6; PR#194 governed_run_sound"},
|
| 1699 |
+
{"formula": "Per-action DSSE receipt", "role": "non-repudiable agent->action binding",
|
| 1700 |
+
"expr": "receipt = {agent_hash, delegation_hash, inputs_hash, Lambda_vector, action, prev_hash}; DSSE sig over PAE",
|
| 1701 |
+
"status": "DSSE (Apache-2.0) signed by in-image ECDSA-P256 key. Per-invocation ephemeral key = roadmap (SPIFFE/SPIRE).",
|
| 1702 |
+
"proven_where": "DSSE (Apache-2.0); in-toto (Apache-2.0)"},
|
| 1703 |
+
{"formula": "Lambda 13-axis conjunctive gate (vector recorded in receipt)",
|
| 1704 |
+
"role": "policy gate result is bound INTO the action receipt",
|
| 1705 |
+
"expr": "action proceeds iff Lambda vector all-TRUE; the vector is recorded in the receipt",
|
| 1706 |
+
"status": "Conjunctive GATE = P2 gate-soundness PROVEN. Lambda uniqueness = Conjecture 1.",
|
| 1707 |
+
"proven_where": "P2 gate-soundness PROVEN"},
|
| 1708 |
+
{"formula": "Append-only SHA-256 + Merkle (delegation->action chain)",
|
| 1709 |
+
"role": "tamper-evident authority + action history",
|
| 1710 |
+
"expr": "H_n = SHA256(H_{n-1} || leaf_n); inclusion proof binds each receipt to a root",
|
| 1711 |
+
"status": "PROVEN (P5). Deleting/inserting/modifying a receipt breaks the chain at the named link.",
|
| 1712 |
+
"proven_where": "PR#188 P5 tamper-evidence; RFC-6962 / Rekor (Apache-2.0)"},
|
| 1713 |
+
],
|
| 1714 |
+
"honesty": ("The agent-version hash, inputs hash, Lambda vector, delegation receipt and per-action "
|
| 1715 |
+
"receipt are all real, computed in-image, DSSE-signed and chained. nominal verifies all "
|
| 1716 |
+
"four provenance links. tamper forges the delegation authority after commit -> the action "
|
| 1717 |
+
"receipt still binds the ORIGINAL delegation hash -> the 'delegation_chain_valid' link "
|
| 1718 |
+
"FAILS at the named position (and the always-on byte-flip tamper test breaks the chain). "
|
| 1719 |
+
"SPIFFE/SPIRE workload identity + operator delegation-token PKI = ROADMAP."),
|
| 1720 |
+
}
|
| 1721 |
+
|
| 1722 |
+
|
| 1723 |
# ===========================================================================
|
| 1724 |
# DISPATCH + REGISTRATION
|
| 1725 |
# ===========================================================================
|
|
|
|
| 1729 |
"hangar2apps": _demo_hangar,
|
| 1730 |
"cyber_rts": _demo_cyber_rts,
|
| 1731 |
"raven": _demo_raven,
|
| 1732 |
+
"ai-sbom": _demo_ai_sbom,
|
| 1733 |
+
"agentic-provenance": _demo_agentic_provenance,
|
| 1734 |
}
|
| 1735 |
|
| 1736 |
|