@@ -1158,6 +1158,32 @@ <h1 class="hero-h1">
11581158 < span id ="rp-bar-text "> Verifying…</ span >
11591159 </ div >
11601160 </ div >
1161+ < p class ="hero-note "> The animation above explains the model. The live proof below runs it.</ p >
1162+ </ div >
1163+ </ section >
1164+
1165+ < section class ="section live-proof " id ="live-proof ">
1166+ < div class ="container ">
1167+ < p class ="section-eyebrow "> Live Protocol Proof</ p >
1168+ < h2 class ="section-h2 "> Resolve. Sign. Verify. Tamper. Reject.</ h2 >
1169+ < p class ="section-p "> This is not a mock animation. The homepage calls the production runtime, verifies the returned receipt, then tampers with the payload and verifies again.</ p >
1170+ < div class ="live-proof-card ">
1171+ < div class ="lp-rows ">
1172+ < div class ="lp-row pending " data-live-step ="resolve "> < div > < div class ="lp-row-main "> < span class ="lp-dot "> </ span > < span class ="lp-label "> Resolve / identify signer</ span > </ div > < div class ="lp-meta " data-live-meta ="resolve "> pending</ div > </ div > < div class ="lp-state " data-live-state ="resolve "> pending</ div > </ div >
1173+ < div class ="lp-row pending " data-live-step ="sign "> < div > < div class ="lp-row-main "> < span class ="lp-dot "> </ span > < span class ="lp-label "> Sign</ span > </ div > < div class ="lp-meta " data-live-meta ="sign "> pending</ div > </ div > < div class ="lp-state " data-live-state ="sign "> pending</ div > </ div >
1174+ < div class ="lp-row pending " data-live-step ="verify "> < div > < div class ="lp-row-main "> < span class ="lp-dot "> </ span > < span class ="lp-label "> Verify</ span > </ div > < div class ="lp-meta " data-live-meta ="verify "> pending</ div > </ div > < div class ="lp-state " data-live-state ="verify "> pending</ div > </ div >
1175+ < div class ="lp-row pending " data-live-step ="tamper "> < div > < div class ="lp-row-main "> < span class ="lp-dot "> </ span > < span class ="lp-label "> Tamper</ span > </ div > < div class ="lp-meta " data-live-meta ="tamper "> pending</ div > </ div > < div class ="lp-state " data-live-state ="tamper "> pending</ div > </ div >
1176+ < div class ="lp-row pending " data-live-step ="reject "> < div > < div class ="lp-row-main "> < span class ="lp-dot "> </ span > < span class ="lp-label "> Reject</ span > </ div > < div class ="lp-meta " data-live-meta ="reject "> pending</ div > </ div > < div class ="lp-state " data-live-state ="reject "> pending</ div > </ div >
1177+ </ div >
1178+ < div class ="lp-grid ">
1179+ < div class ="lp-panel "> < h4 > Original receipt</ h4 > < div class ="lp-kv " id ="lp-valid-summary "> waiting...</ div > </ div >
1180+ < div class ="lp-panel "> < h4 > Tampered receipt</ h4 > < div class ="lp-kv " id ="lp-invalid-summary "> waiting...</ div > </ div >
1181+ </ div >
1182+ < div id ="lp-error " class ="lp-error " style ="display:none; "> Runtime or verifier unavailable. No success was faked.</ div >
1183+ < details class ="lp-raw "> < summary > Show raw JSON</ summary > < pre id ="lp-raw-json "> No data yet.</ pre > </ details >
1184+ < div class ="lp-note "> Runtime signs. Verifier validates. MCP bridges. SDK wraps. Schemas describe. Schema-valid alone is not verified. Tampering changes the canonical payload and breaks verification.</ div >
1185+ < div class ="lp-ctas "> < a class ="btn btn-primary " href ="/webhook-auto-verify.html "> Run Auto-Verify Demo</ a > < a class ="btn btn-secondary " href ="/verify.html "> Open Verifier</ a > < a class ="btn btn-secondary " href ="/stack-proof-demo.html "> View Production Proof</ a > </ div >
1186+ </ div >
11611187 </ div >
11621188 </ section >
11631189
@@ -1390,98 +1416,108 @@ <h2>Trust the proof.<br><span class="grad">Not the agent.</span></h2>
13901416
13911417 < script >
13921418 ( function ( ) {
1393- const steps = document . querySelectorAll ( '.rp-step[data-step]' ) ;
1394- const statusRow = document . getElementById ( 'rp-status' ) ;
1395- const statusText = document . getElementById ( 'rp-status-text' ) ;
1396- const statusSub = document . getElementById ( 'rp-status-sub' ) ;
1397- const statusDot = document . getElementById ( 'rp-dot' ) ;
1398- const bar = document . getElementById ( 'rp-bar' ) ;
1399- const fill = document . getElementById ( 'rp-fill' ) ;
1400- const barText = document . getElementById ( 'rp-bar-text' ) ;
1401- const preview = document . querySelector ( '.receipt-preview' ) ;
1402-
1403- if ( ! preview || ! steps . length ) return ;
1404-
1405- let tids = [ ] ;
1406- let active = false ;
1407-
1408- function wait ( ms ) {
1409- return new Promise ( r => { const t = setTimeout ( r , ms ) ; tids . push ( t ) ; } ) ;
1410- }
1411-
1412- function clearAll ( ) {
1413- tids . forEach ( clearTimeout ) ;
1414- tids = [ ] ;
1415- }
1416-
1417- function reset ( ) {
1418- steps . forEach ( s => { s . classList . remove ( 'is-active' , 'is-done' ) ; } ) ;
1419- statusRow . className = 'status-row is-pending' ;
1420- statusDot . style . background = '#94A3B8' ;
1421- statusText . textContent = 'Waiting…' ;
1422- statusSub . textContent = 'Ready to verify' ;
1423- bar . className = 'rp-verified-bar' ;
1424- barText . textContent = 'Verifying…' ;
1425- fill . style . width = '0%' ;
1426- }
1427-
1428- async function run ( ) {
1429- if ( active ) return ;
1430- active = true ;
1431- reset ( ) ;
1432-
1433- await wait ( 500 ) ;
1434-
1435- /* mark "running" in status */
1436- statusText . textContent = 'STEP 1 SIGNED' ;
1437- statusSub . textContent = 'Running canonical proof checks' ;
1438-
1439- const total = steps . length ;
1440- for ( let i = 0 ; i < total ; i ++ ) {
1441- steps [ i ] . classList . add ( 'is-active' ) ;
1442- fill . style . width = ( ( i / total ) * 100 ) + '%' ;
1443- await wait ( 700 ) ;
1444- steps [ i ] . classList . remove ( 'is-active' ) ;
1445- steps [ i ] . classList . add ( 'is-done' ) ;
1446- fill . style . width = ( ( ( i + 1 ) / total ) * 100 ) + '%' ;
1447- await wait ( 120 ) ;
1419+ const preview = document . querySelector ( '.receipt-preview' ) ;
1420+ const liveWidget = document . getElementById ( 'live-proof' ) ;
1421+
1422+ ( function animateHeroPreview ( ) {
1423+ const steps = document . querySelectorAll ( '.rp-step[data-step]' ) ;
1424+ const statusRow = document . getElementById ( 'rp-status' ) ;
1425+ const statusText = document . getElementById ( 'rp-status-text' ) ;
1426+ const statusSub = document . getElementById ( 'rp-status-sub' ) ;
1427+ const statusDot = document . getElementById ( 'rp-dot' ) ;
1428+ const bar = document . getElementById ( 'rp-bar' ) ;
1429+ const fill = document . getElementById ( 'rp-fill' ) ;
1430+ const barText = document . getElementById ( 'rp-bar-text' ) ;
1431+ if ( ! preview || ! steps . length ) return ;
1432+ let tids = [ ] ; let active = false ;
1433+ const wait = ms => new Promise ( r => { const t = setTimeout ( r , ms ) ; tids . push ( t ) ; } ) ;
1434+ const clearAll = ( ) => { tids . forEach ( clearTimeout ) ; tids = [ ] ; } ;
1435+ const reset = ( ) => { steps . forEach ( s => s . classList . remove ( 'is-active' , 'is-done' ) ) ; statusRow . className = 'status-row is-pending' ; statusDot . style . background = '#94A3B8' ; statusText . textContent = 'Animation preview' ; statusSub . textContent = 'Model walkthrough' ; bar . className = 'rp-verified-bar' ; barText . textContent = 'Previewing…' ; fill . style . width = '0%' ; } ;
1436+ async function run ( ) { if ( active ) return ; active = true ; reset ( ) ; await wait ( 350 ) ; for ( let i = 0 ; i < steps . length ; i ++ ) { steps [ i ] . classList . add ( 'is-active' ) ; fill . style . width = ( ( i / steps . length ) * 100 ) + '%' ; await wait ( 500 ) ; steps [ i ] . classList . remove ( 'is-active' ) ; steps [ i ] . classList . add ( 'is-done' ) ; fill . style . width = ( ( ( i + 1 ) / steps . length ) * 100 ) + '%' ; await wait ( 90 ) ; } statusDot . style . background = '#10B981' ; statusRow . className = 'status-row is-verified' ; statusText . textContent = 'Preview complete' ; statusSub . textContent = 'Live proof runs below' ; bar . className = 'rp-verified-bar is-verified' ; barText . textContent = 'Model preview' ; await wait ( 2600 ) ; active = false ; run ( ) ; }
1437+ const io = new IntersectionObserver ( e => { if ( e [ 0 ] . isIntersecting ) run ( ) ; else { clearAll ( ) ; active = false ; reset ( ) ; } } , { threshold : 0.2 } ) ;
1438+ io . observe ( preview ) ;
1439+ } ) ( ) ;
1440+
1441+ ( function liveProtocolProof ( ) {
1442+ if ( ! liveWidget ) return ;
1443+ const runtimeBase = 'https://runtime.commandlayer.org' ;
1444+ const signerId = 'runtime.commandlayer.eth' ;
1445+ const knownKid = 'vC4WbcNoq2znSCiQ' ;
1446+ const stateEls = Object . fromEntries ( [ 'resolve' , 'sign' , 'verify' , 'tamper' , 'reject' ] . map ( k => [ k , document . querySelector ( `[data-live-state="${ k } "]` ) ] ) ) ;
1447+ const metaEls = Object . fromEntries ( [ 'resolve' , 'sign' , 'verify' , 'tamper' , 'reject' ] . map ( k => [ k , document . querySelector ( `[data-live-meta="${ k } "]` ) ] ) ) ;
1448+ const rowEls = Object . fromEntries ( [ 'resolve' , 'sign' , 'verify' , 'tamper' , 'reject' ] . map ( k => [ k , document . querySelector ( `[data-live-step="${ k } "]` ) ] ) ) ;
1449+ const validSummary = document . getElementById ( 'lp-valid-summary' ) ;
1450+ const invalidSummary = document . getElementById ( 'lp-invalid-summary' ) ;
1451+ const raw = document . getElementById ( 'lp-raw-json' ) ;
1452+ const err = document . getElementById ( 'lp-error' ) ;
1453+ const compact = v => typeof v === 'string' && v . length > 18 ? `${ v . slice ( 0 , 10 ) } …${ v . slice ( - 8 ) } ` : String ( v ?? 'n/a' ) ;
1454+ const setStep = ( name , status , meta ) => { rowEls [ name ] . className = `lp-row ${ status } ` ; stateEls [ name ] . textContent = status ; metaEls [ name ] . textContent = meta ; } ;
1455+ const normalize = r => ( { status : r ?. status || ( r ?. ok === true ? 'VALID' : 'INVALID' ) , hash_matches : r ?. hash_matches , signature_valid : r ?. signature_valid } ) ;
1456+ const isValid = r => r ?. ok === true || r ?. status === 'VALID' || r ?. status === 'VERIFIED' ;
1457+
1458+ async function runLive ( ) {
1459+ err . style . display = 'none' ;
1460+ [ 'resolve' , 'sign' , 'verify' , 'tamper' , 'reject' ] . forEach ( k => setStep ( k , 'pending' , 'pending' ) ) ;
1461+ setStep ( 'resolve' , 'running' , 'Signer identity loaded' ) ;
1462+ await Promise . resolve ( ) ;
1463+ setStep ( 'resolve' , 'passed' , `signer_id=${ signerId } · kid=${ knownKid } ` ) ;
1464+
1465+ let receipt ;
1466+ try {
1467+ setStep ( 'sign' , 'running' , 'POST /trust-verification/sign/v1.0.0' ) ;
1468+ const signResp = await fetch ( `${ runtimeBase } /trust-verification/sign/v1.0.0` , { method :'POST' , headers :{ 'content-type' :'application/json' } , body : JSON . stringify ( { payload :{ message :'homepage live proof' , source :'commandlayer.org' , ts :new Date ( ) . toISOString ( ) } } ) } ) ;
1469+ const signJson = await signResp . json ( ) ;
1470+ receipt = signJson ?. receipt || signJson ?. final_receipt ;
1471+ if ( ! signResp . ok || ! receipt ) throw new Error ( 'TRANSPORT_ERROR' ) ;
1472+ const proof = receipt ?. metadata ?. proof || { } ;
1473+ setStep ( 'sign' , 'passed' , `verb=${ receipt ?. verb || 'n/a' } class=${ receipt ?. class || 'n/a' } hash=${ compact ( proof ?. hash ?. value ) } kid=${ proof ?. signature ?. kid || 'n/a' } signer_id=${ proof ?. signer_id || 'n/a' } ` ) ;
1474+
1475+ setStep ( 'verify' , 'running' , 'POST /verify' ) ;
1476+ const verifyResp = await fetch ( `${ runtimeBase } /verify` , { method :'POST' , headers :{ 'content-type' :'application/json' } , body : JSON . stringify ( { receipt } ) } ) ;
1477+ const verifyJson = await verifyResp . json ( ) ;
1478+ const v = normalize ( verifyJson ) ;
1479+ if ( ! verifyResp . ok || ! isValid ( verifyJson ) ) throw new Error ( 'VERIFY_INVALID' ) ;
1480+ setStep ( 'verify' , 'passed' , `verifier_status=${ v . status } hash_matches=${ String ( v . hash_matches ) } signature_valid=${ String ( v . signature_valid ) } ` ) ;
1481+ validSummary . textContent = `VALID
1482+ verifier_status: ${ v . status }
1483+ hash_matches: ${ String ( v . hash_matches ) }
1484+ signature_valid: ${ String ( v . signature_valid ) } ` ;
1485+
1486+ setStep ( 'tamper' , 'running' , 'Mutating receipt.result.payload.message' ) ;
1487+ const tampered = JSON . parse ( JSON . stringify ( receipt ) ) ;
1488+ if ( ! tampered . result ) tampered . result = { } ;
1489+ if ( ! tampered . result . payload ) tampered . result . payload = { } ;
1490+ tampered . result . payload . message = 'tampered homepage proof' ;
1491+ setStep ( 'tamper' , 'passed' , 'message="tampered homepage proof"' ) ;
1492+
1493+ setStep ( 'reject' , 'running' , 'POST /verify with tampered receipt' ) ;
1494+ const rejectResp = await fetch ( `${ runtimeBase } /verify` , { method :'POST' , headers :{ 'content-type' :'application/json' } , body : JSON . stringify ( { receipt : tampered } ) } ) ;
1495+ const rejectJson = await rejectResp . json ( ) ;
1496+ const t = normalize ( rejectJson ) ;
1497+ const tamperRejected = ! isValid ( rejectJson ) ;
1498+ if ( ! rejectResp . ok || ! tamperRejected ) throw new Error ( 'REJECT_EXPECTED' ) ;
1499+ setStep ( 'reject' , 'failed' , `tampered_status=${ t . status } hash_matches=${ String ( t . hash_matches ) } signature_valid=${ String ( t . signature_valid ) } ` ) ;
1500+ invalidSummary . textContent = `INVALID
1501+ tampered_status: ${ t . status }
1502+ hash_matches: ${ String ( t . hash_matches ) }
1503+ signature_valid: ${ String ( t . signature_valid ) } ` ;
1504+ raw . textContent = JSON . stringify ( { sign_response : signJson , verify_response : verifyJson , tampered_verify_response : rejectJson } , null , 2 ) ;
1505+ } catch ( e ) {
1506+ err . style . display = 'block' ;
1507+ const msg = String ( e && e . message || 'TRANSPORT_ERROR' ) ;
1508+ if ( msg === 'TRANSPORT_ERROR' ) setStep ( 'sign' , 'failed' , 'TRANSPORT_ERROR' ) ;
1509+ else if ( msg === 'VERIFY_INVALID' ) setStep ( 'verify' , 'failed' , 'TRANSPORT_ERROR' ) ;
1510+ else setStep ( 'reject' , 'failed' , 'TRANSPORT_ERROR' ) ;
1511+ validSummary . textContent = 'No VERIFIED result returned.' ;
1512+ invalidSummary . textContent = 'Tampered rejection not completed.' ;
1513+ }
14481514 }
14491515
1450- /* all done — light everything green */
1451- await wait ( 180 ) ;
1452- statusDot . style . background = '#10B981' ;
1453- statusRow . className = 'status-row is-verified' ;
1454- statusText . textContent = 'STEP 2 VERIFIED' ;
1455- statusSub . textContent = 'STEP 3 TAMPERED INVALID' ;
1456- bar . className = 'rp-verified-bar is-verified' ;
1457- barText . textContent = 'Proof complete' ;
1458-
1459- await wait ( 3200 ) ;
1460-
1461- active = false ;
1462- run ( ) ;
1463- }
1464-
1465- /* trigger on scroll, plus immediately on load if already in view */
1466- const io = new IntersectionObserver ( entries => {
1467- if ( entries [ 0 ] . isIntersecting ) {
1468- run ( ) ;
1469- } else {
1470- clearAll ( ) ;
1471- active = false ;
1472- reset ( ) ;
1473- }
1474- } , { threshold : 0.2 } ) ;
1475-
1476- io . observe ( preview ) ;
1477-
1478- /* belt-and-suspenders: if card is in view on page load, start right away */
1479- document . addEventListener ( 'DOMContentLoaded' , ( ) => {
1480- requestAnimationFrame ( ( ) => {
1481- const r = preview . getBoundingClientRect ( ) ;
1482- if ( r . top < window . innerHeight && r . bottom > 0 ) run ( ) ;
1483- } ) ;
1484- } ) ;
1516+ const io = new IntersectionObserver ( entries => {
1517+ if ( entries [ 0 ] . isIntersecting ) { io . disconnect ( ) ; runLive ( ) ; }
1518+ } , { threshold : 0.25 } ) ;
1519+ io . observe ( liveWidget ) ;
1520+ } ) ( ) ;
14851521 } ) ( ) ;
14861522 </ script >
14871523</ body >
0 commit comments