code wiki / _hdl_build / nx_apistack_ratelimit.nx

nx_apistack_ratelimit.nx source

↩ module page · 55 lines · 2507 B

1// nx_apistack_ratelimit.nx -- CAP-API-RATELIMIT: request quotas over /api (429 on exhaustion). Deterministic 2// token-bucket: refill by (elapsed * rate) capped at cap, then take cost -> pure INTEGER math, so the allow/429 3// decision is byte-reproducible (the sovereign exceed over cloud rate meters). Quotas are DATA (ratelimits.conf: 4// key<TAB>cap<TAB>rate_per_sec) so a per-shard/per-actor limit is a row, not code. The bucket STATE (tokens, 5// last_refill) is held by the caller (per-key), keeping this a pure, gateable step. license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "nx_site_lock_lib.nx" 8const K_MAGIC_65536: i64 = 65536 9 10// one token-bucket step. state[0]=tokens, state[1]=last_refill_sec. Returns 1=ALLOW, 0=DENY(429). 11func rl_step(state: *i64, cap: i64, rate: i64, now: i64, cost: i64) -> i64 { 12 let elapsed: i64 = now - state[1] 13 var tok: i64 = state[0] 14 if elapsed > 0 { tok = tok + elapsed * rate } 15 if tok > cap { tok = cap } 16 if tok < 0 { tok = 0 } 17 state[1] = now 18 if tok >= cost { state[0] = tok - cost; return 1 } 19 state[0] = tok 20 return 0 21} 22 23func rl_read_file(path: *u8, out: *u8, cap: i64) -> i64 { 24 let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 } 25 var total: i64 = 0; var go: i64 = 1 26 while go == 1 { 27 let tail: *u8 = (out as i64 + total) as *u8 28 let nr: i64 = sys_read(fd, tail, cap - total) 29 if nr <= 0 { go = 0 } 30 if nr > 0 { total = total + nr } 31 if total >= cap { go = 0 } 32 } 33 sys_close(fd); return total 34} 35 36// per-key quota from data (ratelimits.conf: key<TAB>cap<TAB>rate_per_sec). 1 if found (fills out_cap/out_rate), else 0. 37func rl_config(conf_path: *u8, key: *u8, key_n: i64, out_cap: *i64, out_rate: *i64) -> i64 { 38 let buf: *u8 = sys_mmap(K_MAGIC_65536); let n: i64 = rl_read_file(conf_path, buf, K_MAGIC_65536) 39 let fs: *i64 = sys_mmap(8); let fe: *i64 = sys_mmap(8) 40 var ls: i64 = 0 41 while ls < n { 42 let le: i64 = slk_line_end(buf, n, ls) 43 if le > ls { if buf[ls] != (35 as u8) { 44 if slk_field(buf, ls, le, 0, fs, fe) == 1 { 45 if slk_eq(slk_at(buf, fs[0]), fe[0] - fs[0], key, key_n) == 1 { 46 if slk_field(buf, ls, le, 1, fs, fe) == 1 { out_cap[0] = slk_atoi(buf, fs[0], fe[0]) } 47 if slk_field(buf, ls, le, 2, fs, fe) == 1 { out_rate[0] = slk_atoi(buf, fs[0], fe[0]) } 48 return 1 49 } 50 } 51 } } 52 ls = le + 1 53 } 54 return 0 55}