code wiki / _hdl_build / nx_apistack_ratelimit_gate.nx

nx_apistack_ratelimit_gate.nx source

↩ module page · 61 lines · 4067 B

1import "nx_gate_gn.nx" 2// nx_apistack_ratelimit_gate.nx -- hermetic gate for CAP-API-RATELIMIT. Proves the deterministic token-bucket: 3// burst up to cap then 429, refill over elapsed time, cap ceiling on long idle, data-driven per-key quotas, and 4// bit-identical decisions for identical inputs. Sovereign: nx_syscalls + nx_apistack_ratelimit. expect_exit: 0 5import "nx_syscalls.nx" 6import "nx_apistack_ratelimit.nx" 7import "nx_gate_verdict.nx" 8 9func gp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 10func g_app(buf: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){buf[off+i]=s[i];i=i+1} return off+i } 11 12func main(argc: i64, argv: *i64) -> i64 { 13 gp("=== nx_apistack_ratelimit_gate (deterministic token-bucket 429) ===\n" as *u8) 14 var pass: i64 = 0; var fail: i64 = 0 15 16 // T1 burst up to cap=5 (rate 1/s), then 429 17 let st: *i64 = sys_mmap(16) as *i64; st[0]=5; st[1]=100 18 var allows: i64 = 0; var i: i64 = 0 19 while i < 5 { allows = allows + rl_step(st, 5, 1, 100, 1); i = i + 1 } 20 let deny6: i64 = rl_step(st, 5, 1, 100, 1) 21 if allows == 5 { if deny6 == 0 { pass=pass+1; gp(" T1 burst 5 allow then 429 PASS\n" as *u8) } else { fail=fail+1; gp(" T1 FAIL 6th not denied\n" as *u8) } } else { fail=fail+1; gp(" T1 FAIL allows=" as *u8); gn(allows); gp("\n" as *u8) } 22 23 // T2 refill over 3s -> allow again 24 let a2: i64 = rl_step(st, 5, 1, 103, 1) 25 if a2 == 1 { pass=pass+1; gp(" T2 refill after 3s -> allow PASS\n" as *u8) } else { fail=fail+1; gp(" T2 FAIL\n" as *u8) } 26 27 // T3 long idle -> tokens cap at cap (not unbounded): after now=1000, exactly cap available 28 let st3: *i64 = sys_mmap(16) as *i64; st3[0]=0; st3[1]=100 29 rl_step(st3, 5, 1, 1000, 0) // cost 0 just refills; elapsed 900*1 capped at 5 30 if st3[0] == 5 { pass=pass+1; gp(" T3 long-idle refill CAPPED at cap=5 PASS\n" as *u8) } else { fail=fail+1; gp(" T3 FAIL tokens=" as *u8); gn(st3[0]); gp("\n" as *u8) } 31 32 // T4 data-driven quota from ratelimits.conf 33 let conf: *u8 = sys_mmap(128); var cn: i64 = 0 34 cn = g_app(conf, cn, "shard-a" as *u8); conf[cn]=9 as u8; cn=cn+1 35 cn = g_app(conf, cn, "10" as *u8); conf[cn]=9 as u8; cn=cn+1 36 cn = g_app(conf, cn, "2" as *u8); conf[cn]=10 as u8; cn=cn+1 37 let cfd: i64 = sys_openat_wr("/tmp/rl_conf.tsv" as *u8, 0x1a4); if cfd >= 0 { sys_write(cfd, conf, cn); sys_close(cfd) } 38 let ocap: *i64 = sys_mmap(8) as *i64; let orate: *i64 = sys_mmap(8) as *i64 39 let found: i64 = rl_config("/tmp/rl_conf.tsv" as *u8, "shard-a" as *u8, 7, ocap, orate) 40 if found == 1 { if ocap[0]==10 { if orate[0]==2 { pass=pass+1; gp(" T4 quota-as-data (shard-a cap=10 rate=2) PASS\n" as *u8) } } } 41 if found != 1 { fail=fail+1; gp(" T4 FAIL config miss\n" as *u8) } 42 else { if ocap[0]!=10 { fail=fail+1; gp(" T4 FAIL cap=" as *u8); gn(ocap[0]); gp("\n" as *u8) } } 43 44 // T5 determinism: identical inputs -> identical decision 45 let sa: *i64 = sys_mmap(16) as *i64; sa[0]=3; sa[1]=50 46 let sb: *i64 = sys_mmap(16) as *i64; sb[0]=3; sb[1]=50 47 let da: i64 = rl_step(sa, 5, 1, 52, 4) 48 let db: i64 = rl_step(sb, 5, 1, 52, 4) 49 if da == db { if sa[0]==sb[0] { pass=pass+1; gp(" T5 identical inputs -> identical decision + state PASS\n" as *u8) } else { fail=fail+1; gp(" T5 FAIL state diverged\n" as *u8) } } else { fail=fail+1; gp(" T5 FAIL decision diverged\n" as *u8) } 50 51 gp("RESULT pass=" as *u8); gn(pass); gp(" fail=" as *u8); gn(fail) 52 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 53 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 54 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 55 let ctr__dry: *i64 = gv_ctr() 56 ctr__dry[0] = pass 57 ctr__dry[1] = pass + fail 58 let rc__dry: i64 = gv_verdict("APISTACK-RATELIMIT-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 59 sys_exit(rc__dry) 60 return rc__dry 61}