nx_opaque_login_routes.nx
buildroot/runtime/nx_opaque_login_routes.nx
about
nx_opaque_login_routes.nx -- the SHARED route table for the no-cookie OPAQUE login. Builds a full HTTP
response into a caller buffer (NOT a socket write), so BOTH transports reuse one table (rule 15 DRY):
- plain HTTP daemon: sys_read -> olg_route -> sys_write
- TLS daemon: tls handshake -> app_recv -> olg_route -> app_send
Routes: GET /login (page) | POST /login | GET /whoami | GET /audio (session-gated) | POST /register.
No shortcuts: full OPAQUE, every reject path returns a clean status, no token leak on failure.
dependencies 8 imports · 3 importers
imports: nx_opaque_login.nxnx_http_form.nxnx_audio_web.nxnx_finance_web.nxnx_intake_form.nxnx_intake_handler.nxnx_lan_signup.nxnx_bills_entry.nx
imported by: nx_fin_route_gate.nxnx_opaque_login_tls_daemon.nxnx_signup_origin_gate.nx
structs
| none |
consts
| 15 | const OLR_MAGIC_2026: i64 = 2026 |
| 16 | const OLR_MAGIC_131072: i64 = 131072 |
| 58 | const OLR_HTML: *u8 = "<!doctype html><html><head><meta charset=utf-8><meta name=viewport content=\"width=device-width,initial-scale=1\"><title>Nishi OPAQUE login</title><style>body{font-family:system-ui,sans-serif;max-width:480px;margin:5vh auto;padding:0 18px;color:#16202e}h1{font-size:1.2rem}h3{margin:18px 0 4px}input{width:100%;padding:9px;margin:5px 0;box-sizing:border-box;border:1px solid #b9c2d6;border-radius:5px}button{padding:9px 16px;margin:6px 6px 6px 0;background:#0b2545;color:#fff;border:0;border-radius:5px;cursor:pointer;font-size:.95rem}#msg{margin:14px 0;padding:12px;background:#f1f4fa;border-left:3px solid #0b2545;word-break:break-all;min-height:1.2em}</style></head><body><h1>Nishi — no-cookie OPAQUE login</h1><p style=\"color:#667;font-size:.9rem\">Full OPAQUE aPAKE (RFC 9807) over TLS. The server never stores your passphrase; sessions are Ed25519 no-cookie tokens (sessionStorage + X-Nishi-Session header), never cookies.</p><div id=msg>Register a handle + passphrase, then log in.</div><h3>1. Register</h3><input id=rh placeholder=\"handle (e.g. elder)\"><input id=rp type=password placeholder=\"passphrase\"><button onclick=reg()>Register</button><h3>2. Login</h3><input id=lh placeholder=handle><input id=lp type=password placeholder=passphrase><button onclick=login()>Login</button> <button onclick=who()>Who am I?</button> <button onclick=aud()>Open audio</button> <button onclick=out()>Sign out</button><script>function $(i){return document.getElementById(i)}function M(t){$('msg').textContent=t}async function G(p,b){try{var o={method:b?'POST':'GET',headers:{}};if(b){o.headers['Content-Type']='application/x-www-form-urlencoded';o.body=b}else{o.headers['X-Nishi-Session']=sessionStorage.getItem('nsess')||''}var r=await fetch(p,o);var j={};try{j=await r.json()}catch(e){}return{ok:r.ok,s:r.status,j:j}}catch(e){return{ok:false,s:0,j:{error:'network: '+e}}}}async function reg(){M('Registering\\u2026 (memory-hard OPAQUE step \\u2014 a moment)');var r=await G('/register','handle='+encodeURIComponent($('rh').value)+'&pw='+encodeURIComponent($('rp').value));M(r.ok?('Registered! SAVE THIS RECOVERY MNEMONIC: '+r.j.mnemonic):('Register failed ('+r.s+'): '+(r.j.error||'unknown')))}async function login(){M('Logging in\\u2026 (a moment)');var r=await G('/login','handle='+encodeURIComponent($('lh').value)+'&pw='+encodeURIComponent($('lp').value));if(r.ok){sessionStorage.setItem('nsess',r.j.token);var R=new URLSearchParams(location.search).get('return');if(!R&&document.referrer){try{var u=new URL(document.referrer);if(u.origin==location.origin&&u.pathname!='/login'&&u.pathname!='/'){R=u.pathname}}catch(e){}}if(R){location.href=R;return}M('Logged in \\u2014 NO cookie. Token saved ('+r.j.token.length+' chars). Open /torrent or /gallery.')}else{M('Login failed ('+r.s+'): '+(r.j.error||'unknown'))}}async function who(){if(!sessionStorage.getItem('nsess')){M('No session \\u2014 log in first.');return}M('Validating\\u2026');var r=await G('/whoami');M(r.ok?('Valid no-cookie session. user-id hash: '+r.j.uid):('Session rejected ('+r.s+'): '+(r.j.error||'unknown')))}async function aud(){if(!sessionStorage.getItem('nsess')){M('Log in first.');return}M('Opening audio\\u2026');try{var r=await fetch('/audio',{headers:{'X-Nishi-Session':sessionStorage.getItem('nsess')}});if(r.ok){var h=await r.text();var x=window.open('','_blank');x.document.write(h);x.document.close();M('Audio page opened (protected by your session).')}else{M('Audio denied ('+r.status+') \\u2014 login required.')}}catch(e){M('audio error: '+e)}}function out(){sessionStorage.removeItem('nsess');M('Signed out (token cleared).')}</script></body></html>" as *u8 |
functions
| 18 | func olr_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } called by 1: olg_route_ip |
| 19 | func olr_find(buf: *u8, n: i64, needle: *u8, nl: i64) -> i64 |
| 25 | func olr_starts(buf: *u8, n: i64, pre: *u8) -> i64 { var i: i64=0; while pre[i]!=(0 as u8){ if i>=n {return 0} if buf[i]!=pre[i]{return 0} i=i+1 } return 1 } called by 1: olg_route_ip |
| 26 | func olr_cat(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var i: i64=0; while s[i]!=(0 as u8){dst[o]=s[i]; o=o+1; i=i+1} return o } |
| 27 | func olr_catb(dst: *u8, off: i64, src: *u8, n: i64) -> i64 { var o: i64=off; var i: i64=0; while i<n {dst[o]=src[i]; o=o+1; i=i+1} return o } |
| 28 | func olr_itoa(dst: *u8, off: i64, v: i64) -> i64 { let t: *u8=sys_mmap(28); var m: i64=v; var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var o: i64=off; var q: i64=k-1; while q>=0{dst[o]=t[q];o=o+1;q=q-1} return o } called by 1: olr_resp |
| 29 | func olr_hex(dst: *u8, off: i64, src: *u8, n: i64) -> i64 { let hx: *u8="0123456789abcdef" as *u8; var o: i64=off; var i: i64=0; while i<n { let c: i64=(src[i] as i64)&0xff; dst[o]=hx[(c>>4)&15]; dst[o+1]=hx[c&15]; o=o+2; i=i+1 } return o } called by 1: olg_route_ip |
| 30 | func olr_hdr_val(req: *u8, hend: i64, name: *u8, nl: i64, out: *u8, cap: i64) -> i64 |
| 40 | func olr_resp(buf: *u8, status: *u8, ctype: *u8, body: *u8, blen: i64) -> i64 |
| 51 | func olr_resp303(buf: *u8, location: *u8) -> i64 |
| 65 | func olg_route_ip(ctx: *NxAuthContext, req: *u8, rn: i64, now: i64, resp_out: *u8, resp_cap: i64, called by 2: olg_routemain calls 21: olr_findolr_startsnx_http_form_get_fieldolg_loginolr_catolr_catb+15 |
| 185 | func olg_route(ctx: *NxAuthContext, req: *u8, rn: i64, now: i64, resp_out: *u8, resp_cap: i64) -> i64 |