code wiki / _hdl_build / nx_vault_gateway.nx

nx_vault_gateway.nx source

↩ module page · 456 lines · 30214 B

1// nx_vault_gateway.nx -- OPAQUE-gated /vault media browser (M0 product surface) per the family-suite spec. 2// Reuses the gallery opaque realm (argv keys/store) so existing creds log in. Renders the SPEC'd shallow- 3// faceted VISUAL browse: MAJOR=gen/real, GROUP=type, SOURCE=badge, thumbnails served from the record `ref` 4// (DSM layout /volume1/vault/<class>/<type>/<shard>/<cid>_<source>.<ext>). Gated: no valid session -> 401, 5// never a public byte (private-media cardinal rule). Cookie-auth (ngs, HttpOnly, Path=/vault). 6// argv: [1]=port [2]=keys_path [3]=store_path [4]=budget [5]=allow_register [6]=m [7]=t [8]=p [9]=ttl 7// license_tier: ORIGINAL No hw writes (Rule 26). 8import "nx_opaque_login.nx" 9import "nx_http_form.nx" 10import "nx_registry.nx" 11import "nx_mvault_record.nx" 12import "nx_mvault_coll.nx" // site/album/source groupings -- the browse axis the operator asked for 13const VGW_MAGIC_262144: i64 = 262144 14const VGW_MAGIC_2048: i64 = 2048 15const VGW_MAGIC_8388608: i64 = 8388608 16const VGW_MAGIC_16777216: i64 = 16777216 17const VGW_MAGIC_131072: i64 = 131072 18const VGW_MAGIC_131071: i64 = 131071 19const VGW_MAGIC_8192: i64 = 8192 20 21const VGW_PROD_M: i64 = 65536 22const VGW_SESSION_TTL: i64 = 86400 23const VGW_MVAULT_STORE: *u8 = "/volume1/homes/elderwesto/nishihost/knowledge/store/mvault-" as *u8 24const VGW_MAX_ITEMS: i64 = 3000 25const VGW_MEDIA_CAP: i64 = 25165824 // 24 MiB serve cap -- OOM guard; images/thumbnails only 26// The record's `ref` uses the logical DSM root /volume1/vault (14 chars). That shared folder needs 27// root/DSM to create, so serving remaps that prefix to this CONFIGURABLE writable root (Rule 11/17: 28// the path was hardcoded -- a latent bug). When the /volume1/vault shared folder exists, set this to 29// "/volume1/vault" for spec-exact DSM layout. NOTE: serving from a MIRROR copies bytes -> proof-scale 30// only; the no-copy scale answer is an `orig` field in the record + serve-in-place (surfaced to owner). 31const VGW_MEDIA_ROOT: *u8 = "/volume1/homes/elderwesto/vaultfs" as *u8 32const VGW_REF_ROOT_LEN: i64 = 14 // len("/volume1/vault") 33 34const VGW_COLLBUF: i64 = 1048576 // buffer for the collection id index feeding the groupings facet 35func gw_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 36func gw_atoi(s: *u8) -> i64 { var v: i64=0; var i: i64=0; while s[i]!=(0 as u8){ let c: i64=s[i] as i64; if c<48{return v} if c>57{return v} v=v*10+(c-48); i=i+1 } return v } 37func gw_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 } 38func gw_find(buf: *u8, n: i64, needle: *u8, nl: i64) -> i64 { 39 if nl==0 { return 0 } 40 var i: i64=0 41 while i+nl<=n { var j: i64=0; var ok: i64=1; while j<nl { if buf[i+j]!=needle[j]{ok=0; j=nl} else {j=j+1} } if ok==1 {return i} i=i+1 } 42 return 0-1 43} 44func gw_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 } 45func gw_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 } 46func gw_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 } 47 48func gw_send(cfd: i64, status: *u8, ctype: *u8, body: *u8, blen: i64) -> i64 { 49 let buf: *u8 = sys_mmap(VGW_MAGIC_262144); var o: i64 = 0 50 o = gw_cat(buf, o, "HTTP/1.1 " as *u8); o = gw_cat(buf, o, status) 51 o = gw_cat(buf, o, "\r\nContent-Type: " as *u8); o = gw_cat(buf, o, ctype) 52 o = gw_cat(buf, o, "\r\nContent-Length: " as *u8); o = gw_itoa(buf, o, blen) 53 o = gw_cat(buf, o, "\r\nConnection: close\r\nCache-Control: no-store\r\n\r\n" as *u8) 54 o = gw_catb(buf, o, body, blen) 55 sys_write(cfd, buf, o); return 0 56} 57func gw_send_big(cfd: i64, body: *u8, blen: i64) -> i64 { 58 let hdr: *u8 = sys_mmap(512); var o: i64 = 0 59 o = gw_cat(hdr, o, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: " as *u8) 60 o = gw_itoa(hdr, o, blen) 61 o = gw_cat(hdr, o, "\r\nConnection: close\r\nCache-Control: no-store\r\n\r\n" as *u8) 62 sys_write(cfd, hdr, o); sys_write(cfd, body, blen); return 0 63} 64func gw_401(cfd: i64) -> i64 { 65 let b: *u8 = "{\"error\":\"login required\"}" as *u8 66 gw_send(cfd, "401 Unauthorized" as *u8, "application/json" as *u8, b, gw_slen(b)); return 0 67} 68func gw_is_nav(req: *u8, n: i64) -> i64 { if gw_find(req, n, "Sec-Fetch-Mode: navigate" as *u8, 24) >= 0 { return 1 } return 0 } 69func gw_302_login(cfd: i64) -> i64 { 70 let b: *u8 = "HTTP/1.1 302 Found\r\nLocation: /vault/login\r\nContent-Length: 0\r\nCache-Control: no-store\r\nConnection: close\r\n\r\n" as *u8 71 sys_write(cfd, b, gw_slen(b)); return 0 72} 73func gw_send_ck(cfd: i64, ctype: *u8, body: *u8, blen: i64, ckval: *u8, ckvallen: i64, ttl: i64) -> i64 { 74 let buf: *u8 = sys_mmap(VGW_MAGIC_262144); var o: i64 = 0 75 o = gw_cat(buf, o, "HTTP/1.1 200 OK\r\nContent-Type: " as *u8); o = gw_cat(buf, o, ctype) 76 o = gw_cat(buf, o, "\r\nSet-Cookie: ngs=" as *u8); o = gw_catb(buf, o, ckval, ckvallen) 77 o = gw_cat(buf, o, "; HttpOnly; Secure; SameSite=Strict; Path=/vault; Max-Age=" as *u8); o = gw_itoa(buf, o, ttl) 78 o = gw_cat(buf, o, "\r\nContent-Length: " as *u8); o = gw_itoa(buf, o, blen) 79 o = gw_cat(buf, o, "\r\nConnection: close\r\nCache-Control: no-store\r\n\r\n" as *u8) 80 o = gw_catb(buf, o, body, blen) 81 sys_write(cfd, buf, o); return 0 82} 83func gw_cookie_val(req: *u8, hend: i64, out: *u8, cap: i64) -> i64 { 84 let p: i64 = gw_find(req, hend, "ngs=" as *u8, 4) 85 if p < 0 { out[0]=0 as u8; return 0 } 86 var i: i64 = p + 4; var o: i64 = 0 87 while i < hend { let c: u8 = req[i]; if c==(59 as u8){i=hend} else { if c==(13 as u8){i=hend} else { if c==(10 as u8){i=hend} else { if c==(32 as u8){i=hend} else { if o<cap-1 {out[o]=c; o=o+1} i=i+1 } } } } } 88 out[o]=0 as u8; return o 89} 90func gw_hdr_val(req: *u8, hend: i64, name: *u8, nl: i64, out: *u8, cap: i64) -> i64 { 91 let p: i64 = gw_find(req, hend, name, nl) 92 if p < 0 { out[0]=0 as u8; return 0 } 93 var i: i64 = p + nl 94 if i < hend { if req[i]==(32 as u8) { i=i+1 } } 95 var o: i64 = 0 96 while i < hend { let c: u8 = req[i]; if c==(13 as u8){i=hend} else { if c==(10 as u8){i=hend} else { if o<cap-1 {out[o]=c; o=o+1} i=i+1 } } } 97 out[o]=0 as u8; return o 98} 99func gw_reqpath(req: *u8, rn: i64, out: *u8, cap: i64) -> i64 { 100 var s1: i64 = 0-1; var i: i64 = 0 101 while i < rn { if req[i]==(32 as u8) { s1=i; i=rn } else { i=i+1 } } 102 if s1 < 0 { out[0]=0 as u8; return 0 } 103 var p: i64 = s1+1; var o: i64 = 0 104 while p < rn { let c: u8 = req[p]; if c==(32 as u8) { p=rn } else { if o<cap-1 { out[o]=c; o=o+1 } p=p+1 } } 105 out[o]=0 as u8; return o 106} 107func gw_read_full(cfd: i64, req: *u8, cap: i64) -> i64 { 108 var total: i64 = 0 109 var he: i64 = 0 - 1 110 while he < 0 { 111 if total >= cap { return total } 112 let r: i64 = sys_read(cfd, ((req as i64) + total) as *u8, cap - total) 113 if r <= 0 { return total } 114 total = total + r 115 he = gw_find(req, total, "\r\n\r\n" as *u8, 4) 116 } 117 let clbuf: *u8 = sys_mmap(32) 118 let cln: i64 = gw_hdr_val(req, he, "\r\nContent-Length:" as *u8, 17, clbuf, 32) 119 var need: i64 = he + 4 120 if cln > 0 { need = he + 4 + gw_atoi(clbuf) } 121 while total < need { 122 if total >= cap { return total } 123 let r2: i64 = sys_read(cfd, ((req as i64) + total) as *u8, cap - total) 124 if r2 <= 0 { return total } 125 total = total + r2 126 } 127 return total 128} 129 130const VGW_LOGIN_HTML: *u8 = "<!doctype html><html><head><meta charset=utf-8><meta name=viewport content=\"width=device-width,initial-scale=1\"><title>Nishi Vault</title><style>body{font-family:system-ui,sans-serif;max-width:420px;margin:8vh auto;padding:0 18px;color:#cdd7e6;background:#0b1019}h1{font-size:1.2rem;color:#e8eef7}p{color:#7c8aa5;font-size:.86rem}input{width:100%;padding:9px;margin:5px 0;box-sizing:border-box;border:1px solid #2a3550;border-radius:5px;background:#121a28;color:#e8eef7}button{padding:9px 16px;margin:6px 6px 0 0;background:#2d6cdf;color:#fff;border:0;border-radius:5px;cursor:pointer}#m{margin:14px 0;padding:12px;background:#121a28;border-left:3px solid #2d6cdf;color:#cdd7e6;min-height:1.2em}</style></head><body><h1>Nishi Vault &mdash; private</h1><p>OPAQUE aPAKE (RFC 9807). Same credentials as the gallery. The session rides an HttpOnly cookie, path-scoped to /vault.</p><div id=m>Log in to view the vault.</div><input id=h placeholder=handle autocomplete=username><input id=p type=password placeholder=passphrase autocomplete=current-password><button onclick=login()>Login</button><script>function $(i){return document.getElementById(i)}function M(t){$('m').textContent=t}async function login(){M('Logging in...');try{var r=await fetch('/vault/auth/login',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'handle='+encodeURIComponent($('h').value)+'&pw='+encodeURIComponent($('p').value)});var j=await r.json();if(!r.ok){M('Login failed: '+(j.error||r.status));return}M('Logged in. Opening vault...');location.href='/vault/'}catch(e){M('error: '+e)}}</script></body></html>" as *u8 131 132func vgw_nl(buf: *u8, from: i64, n: i64) -> i64 { var i: i64=from; var r: i64=n; while i<n { if buf[i]==(10 as u8) { r=i; i=n } else { i=i+1 } } return r } 133// append NXR1 field value (by key) to dst at o0; returns new offset (o0 if absent) 134func vgw_field(rec: *u8, rl: i64, key: *u8, kl: i64, dst: *u8, o0: i64) -> i64 { 135 let vp: *i64 = sys_mmap(16) as *i64 136 let vl: *i64 = sys_mmap(16) as *i64 137 if mv_rec_field(rec, rl, key, kl, vp, vl) == 1 { 138 let src: *u8 = vp[0] as *u8 139 var o: i64 = o0; var i: i64 = 0 140 while i < vl[0] { dst[o]=src[i]; o=o+1; i=i+1 } 141 return o 142 } 143 return o0 144} 145// resolve the DSM ref (path) for a cid into out (NUL-term); returns len or 0 146func vgw_ref_of(cid: *u8, out: *u8, cap: i64) -> i64 { 147 let recp: *i64 = sys_mmap(16) as *i64 148 let recl: *i64 = sys_mmap(16) as *i64 149 if mv_store_get(VGW_MVAULT_STORE, cid, recp, recl) != 1 { out[0]=0 as u8; return 0 } 150 let vp: *i64 = sys_mmap(16) as *i64 151 let vl: *i64 = sys_mmap(16) as *i64 152 if mv_rec_field(recp[0] as *u8, recl[0], "ref" as *u8, 3, vp, vl) != 1 { out[0]=0 as u8; return 0 } 153 let src: *u8 = vp[0] as *u8 154 var o: i64 = 0 155 while o < vl[0] { if o < cap-1 { out[o]=src[o] } o=o+1 } 156 out[vl[0]]=0 as u8 157 return vl[0] 158} 159// path-jail: 1 iff p starts with /volume1/vault/ and contains no ".." 160func vgw_jail_ok(p: *u8) -> i64 { 161 let pre: *u8 = "/volume1/vault/" as *u8 162 var i: i64 = 0 163 while pre[i]!=(0 as u8) { if p[i]!=pre[i] { return 0 } i=i+1 } 164 var j: i64 = 0 165 while p[j]!=(0 as u8) { if p[j]==(46 as u8) { if p[j+1]==(46 as u8) { return 0 } } j=j+1 } 166 return 1 167} 168func vgw_ctype_for(p: *u8) -> *u8 { 169 let n: i64 = gw_slen(p) 170 var d: i64 = 0-1; var i: i64 = 0 171 while i < n { if p[i]==(46 as u8) { d=i } i=i+1 } 172 if d < 0 { return "application/octet-stream" as *u8 } 173 let e: *u8 = ((p as i64)+d+1) as *u8 174 let el: i64 = n-d-1 175 if gw_starts(e, el, "jpg" as *u8)==1 { return "image/jpeg" as *u8 } 176 if gw_starts(e, el, "jpeg" as *u8)==1 { return "image/jpeg" as *u8 } 177 if gw_starts(e, el, "png" as *u8)==1 { return "image/png" as *u8 } 178 if gw_starts(e, el, "gif" as *u8)==1 { return "image/gif" as *u8 } 179 if gw_starts(e, el, "webp" as *u8)==1 { return "image/webp" as *u8 } 180 return "application/octet-stream" as *u8 181} 182// GATED media serve: /vault/media?cid=X -> file at record.ref (path-jailed, size-capped) 183func vgw_serve_media(cfd: i64, path: *u8, plen: i64) -> i64 { 184 let qp: i64 = gw_find(path, plen, "cid=" as *u8, 4) 185 if qp < 0 { gw_send(cfd, "400 Bad Request" as *u8, "text/plain" as *u8, "no cid" as *u8, 6); return 0 } 186 let cid: *u8 = sys_mmap(128) 187 var i: i64 = qp+4; var o: i64 = 0 188 while i < plen { let c: u8 = path[i]; if c==(38 as u8) { i=plen } else { if o<127 { cid[o]=c; o=o+1 } i=i+1 } } 189 cid[o]=0 as u8 190 let ref: *u8 = sys_mmap(VGW_MAGIC_2048) 191 if vgw_ref_of(cid, ref, VGW_MAGIC_2048) == 0 { gw_send(cfd, "404 Not Found" as *u8, "text/plain" as *u8, "no rec" as *u8, 6); return 0 } 192 if vgw_jail_ok(ref) == 0 { gw_send(cfd, "403 Forbidden" as *u8, "text/plain" as *u8, "jail" as *u8, 4); return 0 } 193 // remap the logical ref root (/volume1/vault) to the configurable writable media root 194 let realp: *u8 = sys_mmap(VGW_MAGIC_2048) 195 var rro: i64 = gw_cat(realp, 0, VGW_MEDIA_ROOT) 196 var rri: i64 = VGW_REF_ROOT_LEN 197 while ref[rri]!=(0 as u8) { realp[rro]=ref[rri]; rro=rro+1; rri=rri+1 } 198 realp[rro]=0 as u8 199 let fd: i64 = sys_openat_rd(realp) 200 if fd < 0 { gw_send(cfd, "404 Not Found" as *u8, "text/plain" as *u8, "no file" as *u8, 7); return 0 } 201 let sz: i64 = sys_lseek(fd, 0, 2) 202 sys_lseek(fd, 0, 0) 203 if sz > VGW_MEDIA_CAP { sys_close(fd); gw_send(cfd, "413 Payload Too Large" as *u8, "text/plain" as *u8, "too big" as *u8, 7); return 0 } 204 let buf: *u8 = sys_mmap(VGW_MEDIA_CAP) 205 var total: i64 = 0; var go: i64 = 1 206 while go==1 { let r: i64 = sys_read(fd, ((buf as i64)+total) as *u8, VGW_MEDIA_CAP-total); if r<=0 {go=0} else { total=total+r; if total>=VGW_MEDIA_CAP {go=0} } } 207 sys_close(fd) 208 let hdr: *u8 = sys_mmap(512); var ho: i64 = 0 209 ho = gw_cat(hdr, ho, "HTTP/1.1 200 OK\r\nContent-Type: " as *u8); ho = gw_cat(hdr, ho, vgw_ctype_for(ref)) 210 ho = gw_cat(hdr, ho, "\r\nContent-Length: " as *u8); ho = gw_itoa(hdr, ho, total) 211 ho = gw_cat(hdr, ho, "\r\nConnection: close\r\nCache-Control: private, max-age=300\r\n\r\n" as *u8) 212 sys_write(cfd, hdr, ho); sys_write(cfd, buf, total) 213 return 0 214} 215// GATED faceted visual browse: grid of cards (thumbnail + gen/real + type + source badges) + facet filters 216func vgw_serve_catalog(cfd: i64) -> i64 { 217 let store: *u8 = VGW_MVAULT_STORE 218 // ZERO-COPY (2026-07-31): this copied the whole id index into a fixed 8 MiB 219 // mapping. The live index had already passed 10 MiB, so the BROWSE PAGE was 220 // writing ~3 MiB past the end of its own buffer on every request -- it was 221 // past its cliff, not approaching it. Borrowing the store's mapping removes 222 // the failure mode by construction instead of raising the cap and waiting. 223 let vipo: *i64 = sys_mmap(16) as *i64 224 let vilo: *i64 = sys_mmap(16) as *i64 225 var ln: i64 = 0 226 var idxbuf: *u8 = 0 as *u8 227 if reg_index_open(store, "mv:ids" as *u8, vipo, vilo) == 1 { idxbuf = vipo[0] as *u8; ln = vilo[0] } 228 let html: *u8 = sys_mmap(VGW_MAGIC_16777216) 229 var o: i64 = 0 230 o = gw_cat(html, o, "<!doctype html><html><head><meta charset=utf-8><meta name=viewport content=\"width=device-width,initial-scale=1\"><title>Nishi Vault</title><style>body{background:#0b0f14;color:#d4dae3;font:14px system-ui,sans-serif;margin:0;padding:18px}h1{color:#fff;margin:0 0 2px;font-size:20px}.m{color:#8a93a3;font-size:12px}#f{margin:10px 0}#f button{background:#161b22;color:#adbac7;border:1px solid #2a3550;border-radius:14px;padding:3px 11px;margin:2px;font-size:12px;cursor:pointer}#f button.on{background:#2d6cdf;color:#fff;border-color:#2d6cdf}.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px;margin-top:12px}.card{background:#11161d;border:1px solid #1c2230;border-radius:8px;overflow:hidden}.card img{width:100%;height:150px;object-fit:cover;display:block;background:#0d1117}.ph{width:100%;height:150px;display:flex;align-items:center;justify-content:center;font-size:34px;color:#3a4658;background:#0d1117}.meta{padding:5px 7px;font-size:11px}.b{display:inline-block;font-size:10px;padding:1px 6px;border-radius:8px;margin-right:3px}.gen{background:#3a1d5c;color:#c79bff}.real{background:#173a2a;color:#6ee7a8}.src{background:#1c2230;color:#8a93a3}</style></head><body><h1>Nishi Vault</h1>" as *u8) 231 var items: i64 = 0 232 var ci: i64 = 0 233 while ci < ln { if idxbuf[ci]==(10 as u8) { items=items+1 } ci=ci+1 } 234 o = gw_cat(html, o, "<p class=m>" as *u8); o = gw_itoa(html, o, items); o = gw_cat(html, o, " items &middot; gated &middot; faceted (gen/real x type, source-tagged, CID-deduped)</p>" as *u8) 235 o = gw_cat(html, o, "<div id=f><button class=on data-f=all onclick=\"F(this,'all','')\">all</button><button onclick=\"F(this,'c','gen')\">gen</button><button onclick=\"F(this,'c','real')\">real</button><button onclick=\"F(this,'t','image')\">image</button><button onclick=\"F(this,'t','video')\">video</button><button onclick=\"F(this,'t','book')\">book</button></div>" as *u8) 236 // GROUPINGS FACET (site / album / source) -- driven by the live collection axis. 237 // The operator asked to browse by site/album/original-source; the axis existed and 238 // gated 10/10 but no surface ever read it. 239 let cbuf: *u8 = sys_mmap(VGW_COLLBUF) 240 let cn: i64 = mvc_list(cbuf, VGW_COLLBUF) 241 if cn > 0 { 242 o = gw_cat(html, o, "<div id=cf style=\"margin:6px 0 2px\"><span class=m>groupings &middot; </span>" as *u8) 243 var cp: i64 = 0 244 while cp < cn { 245 let ce: i64 = vgw_nl(cbuf, cp, cn) 246 let cl2: i64 = ce - cp 247 if cl2 > 0 { 248 let colid: *u8 = sys_mmap(160) 249 var cq: i64 = 0 250 while cq < cl2 { if cq < 159 { colid[cq] = cbuf[cp+cq] } cq = cq + 1 } 251 colid[cl2] = 0 as u8 252 let gp: *i64 = sys_mmap(16) as *i64 253 let gl: *i64 = sys_mmap(16) as *i64 254 mvc_get(colid, gp, gl) 255 if gl[0] > 0 { 256 let grec: *u8 = gp[0] as *u8 257 let nmb: *u8 = sys_mmap(256) 258 let nmo: i64 = vgw_field(grec, gl[0], "name" as *u8, 4, nmb, 0) 259 let kdb: *u8 = sys_mmap(64) 260 let kdo: i64 = vgw_field(grec, gl[0], "kind" as *u8, 4, kdb, 0) 261 let ccount: i64 = mvc_count(colid) 262 o = gw_cat(html, o, "<span class=\"b src\">" as *u8) 263 o = gw_catb(html, o, nmb, nmo) 264 o = gw_cat(html, o, " &middot; " as *u8) 265 o = gw_catb(html, o, kdb, kdo) 266 o = gw_cat(html, o, " &middot; " as *u8) 267 o = gw_itoa(html, o, ccount) 268 o = gw_cat(html, o, "</span>" as *u8) 269 } 270 } 271 cp = ce + 1 272 } 273 o = gw_cat(html, o, "</div>" as *u8) 274 } 275 o = gw_cat(html, o, "<div class=grid id=g>" as *u8) 276 var i: i64 = 0 277 var shown: i64 = 0 278 while i < ln { 279 let e: i64 = vgw_nl(idxbuf, i, ln) 280 let clen: i64 = e - i 281 if clen > 0 { 282 if shown < VGW_MAX_ITEMS { 283 let cid: *u8 = sys_mmap(128) 284 var k: i64 = 0 285 while k < clen { if k<127 { cid[k]=idxbuf[i+k] } k=k+1 } 286 cid[clen]=0 as u8 287 let recp: *i64 = sys_mmap(16) as *i64 288 let recl: *i64 = sys_mmap(16) as *i64 289 if mv_store_get(store, cid, recp, recl) == 1 { 290 let rec: *u8 = recp[0] as *u8 291 let rl: i64 = recl[0] 292 let cls: *u8 = sys_mmap(32); let clo: i64 = vgw_field(rec, rl, "class" as *u8, 5, cls, 0); cls[clo]=0 as u8 293 let typ: *u8 = sys_mmap(32); let tyo: i64 = vgw_field(rec, rl, "type" as *u8, 4, typ, 0); typ[tyo]=0 as u8 294 o = gw_cat(html, o, "<div class=card data-c=" as *u8); o = gw_catb(html, o, cls, clo); o = gw_cat(html, o, " data-t=" as *u8); o = gw_catb(html, o, typ, tyo); o = gw_cat(html, o, ">" as *u8) 295 if gw_starts(typ, tyo, "image" as *u8)==1 { 296 o = gw_cat(html, o, "<img loading=lazy src=/vault/media?cid=" as *u8); o = gw_catb(html, o, cid, clen); o = gw_cat(html, o, ">" as *u8) 297 } else { if gw_starts(typ, tyo, "gif" as *u8)==1 { 298 o = gw_cat(html, o, "<img loading=lazy src=/vault/media?cid=" as *u8); o = gw_catb(html, o, cid, clen); o = gw_cat(html, o, ">" as *u8) 299 } else { 300 o = gw_cat(html, o, "<div class=ph>" as *u8) 301 if gw_starts(typ, tyo, "video" as *u8)==1 { o = gw_cat(html, o, "&#9654;" as *u8) } else { if gw_starts(typ, tyo, "book" as *u8)==1 { o = gw_cat(html, o, "&#128214;" as *u8) } else { o = gw_cat(html, o, "&#9673;" as *u8) } } 302 o = gw_cat(html, o, "</div>" as *u8) 303 } } 304 o = gw_cat(html, o, "<div class=meta><span class=\"b " as *u8) 305 if gw_starts(cls, clo, "gen" as *u8)==1 { o = gw_cat(html, o, "gen\">gen" as *u8) } else { o = gw_cat(html, o, "real\">real" as *u8) } 306 o = gw_cat(html, o, "</span><span class=\"b src\">" as *u8); o = vgw_field(rec, rl, "source" as *u8, 6, html, o); o = gw_cat(html, o, "</span> " as *u8); o = gw_catb(html, o, typ, tyo) 307 o = gw_cat(html, o, "</div></div>" as *u8) 308 shown = shown + 1 309 } 310 } 311 } 312 i = e + 1 313 } 314 o = gw_cat(html, o, "</div>" as *u8) 315 if items > shown { o = gw_cat(html, o, "<p class=m>(showing " as *u8); o = gw_itoa(html, o, shown); o = gw_cat(html, o, " of " as *u8); o = gw_itoa(html, o, items); o = gw_cat(html, o, ")</p>" as *u8) } 316 o = gw_cat(html, o, "<script>function F(b,k,v){var bs=document.querySelectorAll('#f button');for(var i=0;i<bs.length;i++)bs[i].classList.remove('on');b.classList.add('on');var cs=document.querySelectorAll('.card');for(var j=0;j<cs.length;j++){var c=cs[j];var s=(k=='all')||(k=='c'&&c.dataset.c==v)||(k=='t'&&c.dataset.t==v);c.style.display=s?'':'none'}}</script></body></html>" as *u8) 317 gw_send_big(cfd, html, o) 318 return 0 319} 320 321func main(argc: i64, argv: *i64) -> i64 { 322 if argc < 5 { 323 sys_write(1, "usage: nx_vault_gateway <port> <keys> <store> <budget> [allow_register] [m] [t] [p] [ttl]\n" as *u8, 88) 324 sys_exit(2); return 2 325 } 326 let port: i64 = gw_atoi(argv[1] as *u8) 327 let keys_path: *u8 = argv[2] as *u8 328 let store_path: *u8 = argv[3] as *u8 329 let budget: i64 = gw_atoi(argv[4] as *u8) 330 var allow_reg: i64 = 0 331 if argc > 5 { allow_reg = gw_atoi(argv[5] as *u8) } 332 var m_cost: i64 = VGW_PROD_M 333 if argc > 6 { m_cost = gw_atoi(argv[6] as *u8) } 334 var t_cost: i64 = 3 335 if argc > 7 { t_cost = gw_atoi(argv[7] as *u8) } 336 var p_cost: i64 = 4 337 if argc > 8 { p_cost = gw_atoi(argv[8] as *u8) } 338 var session_ttl: i64 = VGW_SESSION_TTL 339 if argc > 9 { session_ttl = gw_atoi(argv[9] as *u8) } 340 341 let ctx: *NxAuthContext = sys_mmap(256) as *NxAuthContext 342 if olg_ctx_setup_ttl(ctx, keys_path, store_path, "nishi_gallery" as *u8, 13, "Nishi Vault" as *u8, 11, session_ttl, m_cost, t_cost, p_cost) != 0 { 343 sys_write(1, "CTX-INIT-FAIL\n" as *u8, 14); sys_exit(1); return 1 344 } 345 346 let addr: *u8 = sys_mmap(16) 347 addr[0]=2 as u8; addr[1]=0 as u8 348 addr[2]=((port>>8)&0xff) as u8; addr[3]=(port&0xff) as u8 349 addr[4]=0 as u8; addr[5]=0 as u8; addr[6]=0 as u8; addr[7]=0 as u8 350 var zi: i64=8; while zi<16 { addr[zi]=0 as u8; zi=zi+1 } 351 let lfd: i64 = sys_socket(2, 1, 0) 352 if lfd < 0 { sys_write(1, "SOCKET-FAIL\n" as *u8, 12); sys_exit(1); return 1 } 353 let optv: *u8 = sys_mmap(4); optv[0]=1 as u8 354 sys_setsockopt(lfd, 1, 2, optv, 4) 355 if sys_bind(lfd, addr, 16) < 0 { sys_write(1, "BIND-FAIL\n" as *u8, 10); sys_exit(1); return 1 } 356 if sys_listen(lfd, 16) < 0 { sys_write(1, "LISTEN-FAIL\n" as *u8, 12); sys_exit(1); return 1 } 357 sys_write(1, "VAULT-GATEWAY-UP\n" as *u8, 17) 358 359 let st: *i64 = sys_mmap(16) as *i64 360 var served: i64 = 0 361 while served < budget { 362 let cfd: i64 = sys_accept(lfd) 363 if cfd >= 0 { 364 let pid: i64 = sys_fork() 365 if pid == 0 { 366 sys_close(lfd) 367 sys_set_socket_timeout(cfd, 20) 368 let req: *u8 = sys_mmap(VGW_MAGIC_131072) 369 let rn: i64 = gw_read_full(cfd, req, VGW_MAGIC_131071) 370 if rn > 0 { 371 let he: i64 = gw_find(req, rn, "\r\n\r\n" as *u8, 4) 372 var body: *u8 = req; var bn: i64 = 0 373 if he >= 0 { body = ((req as i64) + he + 4) as *u8; bn = rn - he - 4 } 374 let now: i64 = sys_now_realtime_sec() 375 let path: *u8 = sys_mmap(VGW_MAGIC_8192) 376 let plen: i64 = gw_reqpath(req, rn, path, VGW_MAGIC_8192) 377 let resp: *u8 = sys_mmap(VGW_MAGIC_8192) 378 379 if gw_starts(req, rn, "POST /vault/auth/register" as *u8) == 1 { 380 if allow_reg == 1 { 381 let hbuf: *u8 = sys_mmap(128); let hl: *i64 = sys_mmap(16) as *i64 382 let pbuf: *u8 = sys_mmap(320); let pl: *i64 = sys_mmap(16) as *i64 383 nx_http_form_get_field(body, bn, "handle" as *u8, 6, hbuf, 127, hl) 384 nx_http_form_get_field(body, bn, "pw" as *u8, 2, pbuf, 319, pl) 385 let mn: *u8 = sys_mmap(512); let mnn: *i64 = sys_mmap(16) as *i64 386 if olg_register(ctx, hbuf, hl[0], pbuf, pl[0], mn, 512, mnn) == NX_MAUTH_OK { 387 var oo: i64 = gw_cat(resp, 0, "{\"mnemonic\":\"" as *u8); oo = gw_catb(resp, oo, mn, mnn[0]); oo = gw_cat(resp, oo, "\"}" as *u8) 388 gw_send(cfd, "200 OK" as *u8, "application/json" as *u8, resp, oo) 389 } else { 390 let oo: i64 = gw_cat(resp, 0, "{\"error\":\"register failed\"}" as *u8) 391 gw_send(cfd, "400 Bad Request" as *u8, "application/json" as *u8, resp, oo) 392 } 393 } else { 394 let oo: i64 = gw_cat(resp, 0, "{\"error\":\"registration closed\"}" as *u8) 395 gw_send(cfd, "403 Forbidden" as *u8, "application/json" as *u8, resp, oo) 396 } 397 } else { if gw_starts(req, rn, "POST /vault/auth/login" as *u8) == 1 { 398 let hbuf: *u8 = sys_mmap(128); let hl: *i64 = sys_mmap(16) as *i64 399 let pbuf: *u8 = sys_mmap(320); let pl: *i64 = sys_mmap(16) as *i64 400 nx_http_form_get_field(body, bn, "handle" as *u8, 6, hbuf, 127, hl) 401 nx_http_form_get_field(body, bn, "pw" as *u8, 2, pbuf, 319, pl) 402 let b64: *u8 = sys_mmap(512); let b64n: *i64 = sys_mmap(16) as *i64 403 if olg_login(ctx, hbuf, hl[0], pbuf, pl[0], b64, 512, b64n) == NX_MAUTH_OK { 404 var oo: i64 = gw_cat(resp, 0, "{\"token\":\"" as *u8); oo = gw_catb(resp, oo, b64, b64n[0]); oo = gw_cat(resp, oo, "\"}" as *u8) 405 gw_send_ck(cfd, "application/json" as *u8, resp, oo, b64, b64n[0], session_ttl) 406 } else { 407 let oo: i64 = gw_cat(resp, 0, "{\"error\":\"invalid credentials\"}" as *u8) 408 gw_send(cfd, "401 Unauthorized" as *u8, "application/json" as *u8, resp, oo) 409 } 410 } else { if gw_starts(req, rn, "GET /vault/auth/whoami" as *u8) == 1 { 411 let tb: *u8 = sys_mmap(512) 412 var tl: i64 = gw_hdr_val(req, he, "X-Nishi-Session:" as *u8, 16, tb, 512) 413 if tl == 0 { tl = gw_cookie_val(req, he, tb, 512) } 414 let uh: *u8 = sys_mmap(64); let uhn: *i64 = sys_mmap(16) as *i64 415 if olg_whoami(ctx, tb, tl, now, uh, 64, uhn) == NX_MAUTH_OK { 416 let oo: i64 = gw_cat(resp, 0, "{\"ok\":1}" as *u8) 417 gw_send(cfd, "200 OK" as *u8, "application/json" as *u8, resp, oo) 418 } else { gw_401(cfd) } 419 } else { if gw_starts(req, rn, "GET /vault/login" as *u8) == 1 { 420 gw_send(cfd, "200 OK" as *u8, "text/html; charset=utf-8" as *u8, VGW_LOGIN_HTML, gw_slen(VGW_LOGIN_HTML)) 421 } else { if gw_starts(req, rn, "GET /vault/media" as *u8) == 1 { 422 let tb: *u8 = sys_mmap(512) 423 var tl: i64 = gw_hdr_val(req, he, "X-Nishi-Session:" as *u8, 16, tb, 512) 424 if tl == 0 { tl = gw_cookie_val(req, he, tb, 512) } 425 let uh: *u8 = sys_mmap(64); let uhn: *i64 = sys_mmap(16) as *i64 426 if olg_whoami(ctx, tb, tl, now, uh, 64, uhn) == NX_MAUTH_OK { 427 vgw_serve_media(cfd, path, plen) 428 } else { gw_401(cfd) } 429 } else { 430 if gw_starts(path, plen, "/vault" as *u8) == 1 { 431 let tb: *u8 = sys_mmap(512) 432 var tl: i64 = gw_hdr_val(req, he, "X-Nishi-Session:" as *u8, 16, tb, 512) 433 if tl == 0 { tl = gw_cookie_val(req, he, tb, 512) } 434 let uh: *u8 = sys_mmap(64); let uhn: *i64 = sys_mmap(16) as *i64 435 if olg_whoami(ctx, tb, tl, now, uh, 64, uhn) == NX_MAUTH_OK { 436 vgw_serve_catalog(cfd) 437 } else { 438 if gw_is_nav(req, rn) == 1 { gw_302_login(cfd) } else { gw_401(cfd) } 439 } 440 } else { 441 let oo: i64 = gw_cat(resp, 0, "not found" as *u8) 442 gw_send(cfd, "404 Not Found" as *u8, "text/plain" as *u8, resp, oo) 443 } 444 } } } } } 445 } 446 sys_close(cfd) 447 sys_exit(0) 448 } 449 sys_close(cfd) 450 var reaped: i64 = 1 451 while reaped > 0 { reaped = sys_wait4(0 - 1, st, 1) } 452 } 453 served = served + 1 454 } 455 sys_close(lfd); sys_exit(0); return 0 456}