code wiki / _hdl_build / nx_hr_frontdesk_shell.nx

nx_hr_frontdesk_shell.nx source

↩ module page · 19 lines · 2838 B

1// nx_hr_frontdesk_shell.nx -- NISHI HR, the front-desk FRONT-END (Nishi-emitted, the gs_shell pattern). Emits the 2// privacy-preserving behavioral collector that runs in the visitor's browser: plants a HONEYPOT field, watches 3// interaction (mousemove/scroll/keydown) to derive an ENTROPY + timing-REGULARITY score, solves the server's 4// PROOF-OF-WORK challenge (crypto.subtle SHA-256, matching nx_hr_pow), and POSTs ONLY the derived scores + the 5// pow solution + the signed challenge to /hr/verify. It NEVER reads coordinates, NEVER sends an IP/UA/identity, 6// and stores NOTHING -- the raw events are accumulated locally and discarded (privacy by construction). The 7// server then runs hb_evaluate(nx_hr_bot_behavior) + pow_verify(nx_hr_pow) on those ephemeral signals. 8// license_tier: ORIGINAL 9import "nx_syscalls.nx" 10 11const FD_HTML: *u8 = "<form id=nishi-fd autocomplete=off><input name=hp_website tabindex=-1 aria-hidden=true autocomplete=off style='position:absolute;left:-9999px;opacity:0;height:0'></form><script>(function(){var moves=0,times=[];function rec(){moves++;times.push(Date.now())}document.addEventListener('mousemove',rec);document.addEventListener('scroll',rec);document.addEventListener('keydown',rec);function gaps(){var g=[];for(var i=1;i<times.length;i++)g.push(times[i]-times[i-1]);return g}function variance(g){if(g.length<1)return 0;var m=0;g.forEach(function(x){m+=x});m/=g.length;var v=0;g.forEach(function(x){v+=(x-m)*(x-m)});return v/g.length}function entropy(){if(moves<3)return 0;var e=moves*5+Math.min(50,Math.sqrt(variance(gaps())));return Math.min(100,Math.round(e))}function regularity(){if(times.length<4)return 100;var v=variance(gaps());if(v<25)return 100;return Math.max(0,100-Math.round(Math.sqrt(v)))}async function sha256bits(s){var b=new TextEncoder().encode(s);var h=await crypto.subtle.digest('SHA-256',b);var a=new Uint8Array(h);var bits=0;for(var i=0;i<a.length;i++){if(a[i]===0){bits+=8;continue}var m=128,c=0;while(m){if(!(a[i]&m)){c++;m=m>>1}else{break}}bits+=c;break}return bits}async function solve(n,d){for(var s=0;s<5000000;s++){if(await sha256bits(n+'/'+s)>=d)return s}return -1}async function nishiFrontDesk(){var r=await fetch('/hr/pow/challenge');var ch=await r.json();var sol=await solve(ch.nonce,ch.difficulty);var hp=document.querySelector('[name=hp_website]').value;return fetch('/hr/verify',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({challenge:ch.challenge,solution:sol,entropy:entropy(),regularity:regularity(),honeypot:hp?1:0})})}window.nishiFrontDesk=nishiFrontDesk})();</script>" as *u8 12 13func fd_emit(out: *u8) -> i64 { 14 let s: *u8 = FD_HTML // bind const to a local before indexing (nx_cc const-index gotcha) 15 var i: i64 = 0 16 while s[i] != (0 as u8) { out[i] = s[i]; i = i + 1 } 17 out[i] = 0 as u8 18 return i 19}