code wiki / (root) / nx_toolsapi_bodycap_gate.nx

nx_toolsapi_bodycap_gate.nx source

↩ module page · 342 lines · 18751 B

1// nx_toolsapi_bodycap_gate.nx -- END-TO-END referee for the tools-API request-body fix (2026-08-23). 2// 3// THE DEFECT IT GUARDS: nx_tools_api_serve read a request with ONE sys_read of 64 KiB and handed the partial 4// bytes to the edge; every tools/call above that answered 413, and the laptop wire rendered the 413 as a bare 5// {} -- a whole-file source write read as "dropped" and nobody could say why. Behind it, the argv scratch in 6// ta_mcp_call was a second 64 KiB constant that kept "what fit": a file content above 65535 bytes was written 7// as its prefix with an OK receipt. 8// 9// HOW IT PROVES IT: forks the SUBJECT daemon (default: the STAGED ./nx_tools_api_serve.sov.elf.new; argv[1] 10// `live` = the serving-root binary; any other argv[1] = an explicit elf path) with CWD = /tmp/<gate>/ carrying a 11// fixture cap secret and a one-row allowlist (argecho, the estate's multi-arg witness organ), mints a cap for 12// that secret in-process with the estate's own capt_issue, and drives REAL loopback POST /mcp tools/call 13// requests whose "name" key comes LAST -- after the payload -- so a truncated body can never name the tool. 14// T1 8 KB argv echoed whole (positive control: fits one read on the old and the new binary) 15// T1b the 8 KB reply is BYTE-IDENTICAL to the LIVE daemon's reply on a second port (behaviour control) 16// T2 63 KB argv echoed whole (the measured failing case: the request exceeds 65536 bytes on the wire) 17// T3 120 KB argv echoed whole (over the old window, under TA_CAPTURE_CAP so the echo itself is not cut) 18// T4 neg-control: a request declaring Content-Length above EDGE_REQ_MAX answers 413 naming the limit 19// T5 no reply is the bare {} nor the old "64KiB edge limit" text 20// T6 fixture reached the condition: the T2 request really was > 65536 bytes 21// T7 the subject child is gone after the gate (its port refuses again) -- a probe that leaves a daemon is a second instance 22// A free port is PROVEN free (connect refused) before the subject binds it. 23// license_tier: ORIGINAL expect_exit: 0 GREEN | 1 RED | 3 SKIP 24import "nx_syscalls.nx" 25import "nx_connect.nx" 26import "nx_http_server.nx" 27import "nx_cap_token.nx" 28import "nx_gate_verdict.nx" 29 30const BG_FIX_DIR: *u8 = "/tmp/nx_toolsapi_bodycap_gate" 31const BG_STAGED: *u8 = "/volume1/homes/elderwesto/nishihost/nx_tools_api_serve.sov.elf.new" 32const BG_LIVE: *u8 = "/volume1/homes/elderwesto/nishihost/nx_tools_api_serve.elf" 33const BG_ARGECHO: *u8 = "/volume1/homes/elderwesto/nishihost/nx_tool_argecho.elf" 34const BG_SECRET: *u8 = "bodycap-gate-fixture-secret-not-a-production-key" 35const BG_PORT_SUBJECT_DEFAULT: i64 = 18877 // argv[2] overrides; verified FREE before use; refused-again after the kill 36const BG_PORT_CONTROL_DEFAULT: i64 = 18878 // argv[3] overrides 37const BG_SOCK_TMO_S: i64 = 20 // a reply that does not arrive in 20 s is a hang, reported as one -- never a parked gate 38const BG_KB: i64 = 1024 39const BG_PAYLOAD_SMALL: i64 = 8 * BG_KB 40const BG_PAYLOAD_EDGE: i64 = 64 * BG_KB // the measured failing class: with the envelope the request exceeds 65536 on the wire 41 // (63 KB measured 64,808 B here -- the fixture cap is shorter than a production cap header, so T6 caught it) 42const BG_PAYLOAD_BIG: i64 = 120 * BG_KB // over the old window; under TA_CAPTURE_CAP (163840) so the ECHO is whole 43const BG_OLD_WINDOW: i64 = 65536 44const BG_OVER_LIMIT_DECLARED: i64 = 2 * 1048576 // above EDGE_REQ_MAX (= EDGE_RESP_CAP = 1 MiB) 45const BG_RESP_CAP: i64 = 2097152 // reply reserve: an echo of BG_PAYLOAD_BIG plus envelope, announced if it fills 46const BG_BOOT_TRIES: i64 = 100 // 100 x 100 ms = 10 s for the child to reach listen 47const BG_BOOT_STEP_MS: i64 = 100 48const BG_CONNECT_MS: i64 = 3000 49const BG_SIGKILL: i64 = 9 50const BG_CAP_TTL_S: i64 = 600 51func bg_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 { if c <= 57 { v = v * 10 + (c - 48) } } i = i + 1 } return v } 52 53func bg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 54func bg_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; var p: i64 = o; while s[i] != (0 as u8) { d[p] = s[i]; p = p + 1; i = i + 1 } d[p] = 0 as u8; return p } 55func bg_catn(d: *u8, o: i64, v: i64) -> i64 { 56 var p: i64 = o 57 var m: i64 = v 58 if m < 0 { d[p] = 45 as u8; p = p + 1; m = 0 - m } 59 let t: *u8 = sys_mmap(24) 60 var k: i64 = 0 61 if m == 0 { t[0] = 48 as u8; k = 1 } 62 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 63 var j: i64 = 0 64 while j < k { d[p] = t[k - 1 - j]; p = p + 1; j = j + 1 } 65 d[p] = 0 as u8 66 return p 67} 68func bg_has(hay: *u8, hn: i64, needle: *u8) -> i64 { 69 let nn: i64 = bg_slen(needle) 70 if nn == 0 { return 1 } 71 var i: i64 = 0 72 while i + nn <= hn { 73 var m: i64 = 0 74 var ok: i64 = 1 75 while m < nn { if hay[i + m] != needle[m] { ok = 0; m = nn } else { m = m + 1 } } 76 if ok == 1 { return 1 } 77 i = i + 1 78 } 79 return 0 80} 81// longest run of 'x' in buf[0..n): the echo witness (argecho prints each argv on its own line; 'x' never escapes) 82func bg_xrun(b: *u8, n: i64) -> i64 { 83 var best: i64 = 0 84 var cur: i64 = 0 85 var i: i64 = 0 86 while i < n { 87 if b[i] == (120 as u8) { cur = cur + 1; if cur > best { best = cur } } else { cur = 0 } 88 i = i + 1 89 } 90 return best 91} 92func bg_write_file(path: *u8, data: *u8, n: i64) -> i64 { 93 let fd: i64 = sys_openat_wr(path, MODE_0644) 94 if fd < 0 { return 0 - 1 } 95 var w: i64 = 0 96 while w < n { let r: i64 = sys_write(fd, ((data as i64) + w) as *u8, n - w); if r <= 0 { sys_close(fd); return 0 - 1 } w = w + r } 97 sys_close(fd) 98 return n 99} 100// connect to 127.0.0.1:port; returns fd or -1 101func bg_connect(port: i64) -> i64 { 102 let addr: *u8 = sys_mmap(16) 103 nx_http_server_addr_loopback(addr, port) 104 let fd: i64 = sys_socket(2, 1, 0) 105 if fd < 0 { return 0 - 1 } 106 if nx_connect_bounded(fd, addr, 16, BG_CONNECT_MS) < 0 { sys_close(fd); return 0 - 1 } 107 sys_set_socket_timeout(fd, BG_SOCK_TMO_S) 108 return fd 109} 110// fork+exec the subject daemon with CWD = the fixture dir; returns pid 111func bg_spawn(elf: *u8, port: i64) -> i64 { 112 let pid: i64 = sys_fork() 113 if pid == 0 { 114 // its own session = its own process group: the daemon forks a seed child and a child per request, 115 // and a kill that reaches only the parent leaves them holding the gate's stdout pipe and the port 116 nx_setsid() 117 sys_chdir(BG_FIX_DIR) 118 let ps: *u8 = sys_mmap(24) 119 bg_catn(ps, 0, port) 120 let av: *i64 = sys_mmap(4 * 8) as *i64 121 av[0] = elf as i64 122 av[1] = "serve" as *u8 as i64 123 av[2] = ps as i64 124 av[3] = 0 125 let envp: *i64 = sys_mmap(16) as *i64 126 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64 127 envp[1] = 0 128 sys_execve(elf, av, envp) 129 sys_exit(127) 130 } 131 return pid 132} 133// wait until the port accepts (the child reached listen), bounded 134func bg_wait_listen(port: i64) -> i64 { 135 var t: i64 = 0 136 while t < BG_BOOT_TRIES { 137 let fd: i64 = bg_connect(port) 138 if fd >= 0 { sys_close(fd); return 1 } 139 sys_sleep_ms(BG_BOOT_STEP_MS) 140 t = t + 1 141 } 142 return 0 143} 144// build a tools/call request: payload of `plen` x's as argv[0], cap, and the "name" key LAST 145func bg_build(req: *u8, plen: i64, cap: *u8) -> i64 { 146 let body: *u8 = sys_mmap(plen + 4096) 147 var b: i64 = bg_cat(body, 0, "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\",\"params\":{\"arguments\":{\"argv\":[\"" as *u8) 148 var i: i64 = 0 149 while i < plen { body[b] = 120 as u8; b = b + 1; i = i + 1 } 150 b = bg_cat(body, b, "\"],\"_cap\":\"" as *u8) 151 b = bg_cat(body, b, cap) 152 b = bg_cat(body, b, "\"},\"name\":\"argecho\"}}" as *u8) 153 var o: i64 = bg_cat(req, 0, "POST /mcp HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Type: application/json\r\nConnection: close\r\nContent-Length: " as *u8) 154 o = bg_catn(req, o, b) 155 o = bg_cat(req, o, "\r\n\r\n" as *u8) 156 var k: i64 = 0 157 while k < b { req[o] = body[k]; o = o + 1; k = k + 1 } 158 return o 159} 160// send a whole request, read the whole reply (to EOF) into out; returns reply length (-1 connect failed) 161func bg_roundtrip(port: i64, req: *u8, rn: i64, out: *u8, cap: i64, filled: *i64) -> i64 { 162 let fd: i64 = bg_connect(port) 163 if fd < 0 { return 0 - 1 } 164 var w: i64 = 0 165 while w < rn { let r: i64 = sys_write(fd, ((req as i64) + w) as *u8, rn - w); if r <= 0 { w = rn + 1 } else { w = w + r } } 166 var total: i64 = 0 167 var go: i64 = 1 168 filled[0] = 0 169 while go == 1 { 170 if total >= cap - 1 { filled[0] = 1; go = 0 } else { 171 let r: i64 = sys_read(fd, ((out as i64) + total) as *u8, cap - 1 - total) 172 if r <= 0 { go = 0 } else { total = total + r } 173 } 174 } 175 sys_close(fd) 176 out[total] = 0 as u8 177 return total 178} 179// print the status line of a reply (up to the first CR, at most 72 bytes) 180func bg_head(b: *u8, n: i64) -> i64 { 181 var m: i64 = 0 182 var go: i64 = 1 183 while go == 1 { 184 if m >= n { go = 0 } else { if m >= 72 { go = 0 } else { if b[m] == (13 as u8) { go = 0 } else { m = m + 1 } } } 185 } 186 sys_write(1, b, m) 187 return 0 188} 189func bg_kill(pid: i64) -> i64 { 190 if pid <= 0 { return 0 } 191 nx_kill(0 - pid, BG_SIGKILL) // the whole process group (setsid in the child): daemon, seed, request children 192 nx_kill(pid, BG_SIGKILL) 193 let st: *i64 = sys_mmap(16) as *i64 194 sys_wait4(pid, st, 0) 195 return 0 196} 197 198func main(argc: i64, argv: *i64) -> i64 { 199 gv_head("NX-TOOLSAPI-BODYCAP-GATE: the tools API reads the WHOLE request and never hands a truncated body on" as *u8) 200 let ctr: *i64 = gv_ctr() 201 var subject: *u8 = BG_STAGED 202 if argc > 1 { 203 let a1: *u8 = argv[1] as *u8 204 if a1[0] == (108 as u8) { if a1[1] == (105 as u8) { if a1[2] == (118 as u8) { if a1[3] == (101 as u8) { subject = BG_LIVE } } } } 205 if a1[0] == (47 as u8) { subject = a1 } 206 } 207 var BG_PORT_SUBJECT: i64 = BG_PORT_SUBJECT_DEFAULT 208 var BG_PORT_CONTROL: i64 = BG_PORT_CONTROL_DEFAULT 209 if argc > 2 { let pa: i64 = bg_atoi(argv[2] as *u8); if pa > 0 { BG_PORT_SUBJECT = pa } } 210 if argc > 3 { let pb: i64 = bg_atoi(argv[3] as *u8); if pb > 0 { BG_PORT_CONTROL = pb } } 211 gv_puts(" subject=" as *u8); gv_puts(subject); gv_puts(" control=" as *u8); gv_puts(BG_LIVE) 212 gv_puts(" ports=" as *u8); gv_num(BG_PORT_SUBJECT); gv_puts("/" as *u8); gv_num(BG_PORT_CONTROL); gv_puts("\n" as *u8) 213 // ---- preconditions: the subject elf, the live elf, argecho, and two FREE ports ---- 214 var sfd: i64 = sys_openat_rd(subject) 215 var have_subject: i64 = 0 216 if sfd >= 0 { sys_close(sfd); have_subject = 1 } 217 var lfd: i64 = sys_openat_rd(BG_LIVE) 218 var have_live: i64 = 0 219 if lfd >= 0 { sys_close(lfd); have_live = 1 } 220 var afd: i64 = sys_openat_rd(BG_ARGECHO) 221 var have_echo: i64 = 0 222 if afd >= 0 { sys_close(afd); have_echo = 1 } 223 var p1: i64 = bg_connect(BG_PORT_SUBJECT) 224 var free1: i64 = 1 225 if p1 >= 0 { sys_close(p1); free1 = 0 } 226 var p2: i64 = bg_connect(BG_PORT_CONTROL) 227 var free2: i64 = 1 228 if p2 >= 0 { sys_close(p2); free2 = 0 } 229 var pre: i64 = 1 230 if gv_need("subject elf readable" as *u8, have_subject, ctr) == 0 { pre = 0 } 231 if gv_need("live elf readable (behaviour control)" as *u8, have_live, ctr) == 0 { pre = 0 } 232 if gv_need("argecho witness elf readable" as *u8, have_echo, ctr) == 0 { pre = 0 } 233 if gv_need("subject port is FREE (connect refused) -- a busy port would be a second instance" as *u8, free1, ctr) == 0 { pre = 0 } 234 if gv_need("control port is FREE (connect refused)" as *u8, free2, ctr) == 0 { pre = 0 } 235 if pre == 0 { return gv_verdict("TOOLSAPI-BODYCAP-GATE" as *u8, ctr, "request-body completeness" as *u8) } 236 // ---- fixture: secret + one-row allowlist, in a dir the subject will chdir into ---- 237 sys_mkdir(BG_FIX_DIR, MODE_0755) 238 // the daemon's seed child registers its bootstrap rows under knowledge/ relative to its CWD; give the 239 // fixture one so that child writes a small fixture registry instead of failing every lock open 240 let kd: *u8 = sys_mmap(256) 241 var kdo: i64 = bg_cat(kd, 0, BG_FIX_DIR); kdo = bg_cat(kd, kdo, "/knowledge" as *u8) 242 sys_mkdir(kd, MODE_0755) 243 let kp: *u8 = sys_mmap(256) 244 var ko: i64 = bg_cat(kp, 0, BG_FIX_DIR); ko = bg_cat(kp, ko, "/tools_cap_secret.key" as *u8) 245 bg_write_file(kp, BG_SECRET, bg_slen(BG_SECRET)) 246 let ap: *u8 = sys_mmap(256) 247 var ao: i64 = bg_cat(ap, 0, BG_FIX_DIR); ao = bg_cat(ap, ao, "/tool_allowlist.conf" as *u8) 248 let row: *u8 = sys_mmap(512) 249 var ro: i64 = bg_cat(row, 0, "argecho\t" as *u8); ro = bg_cat(row, ro, BG_ARGECHO); ro = bg_cat(row, ro, "\tGREEN\n" as *u8) 250 bg_write_file(ap, row, ro) 251 // ---- mint a cap for the fixture secret (the estate's own issuer, same bytes the daemon verifies) ---- 252 let cap: *u8 = sys_mmap(1024) 253 let now: i64 = sys_now_realtime_sec() 254 let cl: i64 = capt_issue(BG_SECRET, bg_slen(BG_SECRET), "argecho" as *u8, 7, now + BG_CAP_TTL_S, now, cap, 1024) 255 cap[cl] = 0 as u8 256 var minted: i64 = 0 257 if cl > 8 { minted = 1 } 258 gv_check("fixture cap minted for argecho (capt_issue returned a token)" as *u8, minted, ctr) 259 // ---- spawn subject + control ---- 260 let spid: i64 = bg_spawn(subject, BG_PORT_SUBJECT) 261 let cpid: i64 = bg_spawn(BG_LIVE, BG_PORT_CONTROL) 262 let up_s: i64 = bg_wait_listen(BG_PORT_SUBJECT) 263 let up_c: i64 = bg_wait_listen(BG_PORT_CONTROL) 264 gv_check("subject reached listen within the boot budget" as *u8, up_s, ctr) 265 gv_check("control (live) reached listen within the boot budget" as *u8, up_c, ctr) 266 let out: *u8 = sys_mmap(BG_RESP_CAP) 267 let out2: *u8 = sys_mmap(BG_RESP_CAP) 268 let filled: *i64 = sys_mmap(16) as *i64 269 var bad_shape: i64 = 0 270 if up_s == 1 { 271 // T1 + T1b: 8 KB 272 let r1: *u8 = sys_mmap(BG_PAYLOAD_SMALL + 8192) 273 let n1: i64 = bg_build(r1, BG_PAYLOAD_SMALL, cap) 274 let l1: i64 = bg_roundtrip(BG_PORT_SUBJECT, r1, n1, out, BG_RESP_CAP, filled) 275 let x1: i64 = bg_xrun(out, l1) 276 gv_puts(" T1 request=" as *u8); gv_num(n1); gv_puts("B reply=" as *u8); gv_num(l1); gv_puts("B xrun=" as *u8); gv_num(x1); gv_puts("\n" as *u8) 277 var t1: i64 = 0 278 if x1 == BG_PAYLOAD_SMALL { t1 = 1 } 279 gv_check("T1 8 KB argv echoed whole (positive control)" as *u8, t1, ctr) 280 if bg_has(out, l1, "{}" as *u8) == 1 { bad_shape = bad_shape + 1 } 281 if bg_has(out, l1, "64KiB edge limit" as *u8) == 1 { bad_shape = bad_shape + 1 } 282 if up_c == 1 { 283 let l1c: i64 = bg_roundtrip(BG_PORT_CONTROL, r1, n1, out2, BG_RESP_CAP, filled) 284 var same: i64 = 0 285 if l1c == l1 { same = 1; var q: i64 = 0; while q < l1 { if out[q] != out2[q] { same = 0; q = l1 } q = q + 1 } } 286 gv_puts(" T1b control reply=" as *u8); gv_num(l1c); gv_puts("B identical=" as *u8); gv_num(same); gv_puts("\n" as *u8) 287 gv_check("T1b 8 KB reply BYTE-IDENTICAL to the LIVE daemon (behaviour control: small requests unchanged)" as *u8, same, ctr) 288 } 289 // T2: 63 KB (the measured failing size) 290 let r2: *u8 = sys_mmap(BG_PAYLOAD_EDGE + 8192) 291 let n2: i64 = bg_build(r2, BG_PAYLOAD_EDGE, cap) 292 let l2: i64 = bg_roundtrip(BG_PORT_SUBJECT, r2, n2, out, BG_RESP_CAP, filled) 293 let x2: i64 = bg_xrun(out, l2) 294 gv_puts(" T2 request=" as *u8); gv_num(n2); gv_puts("B reply=" as *u8); gv_num(l2); gv_puts("B xrun=" as *u8); gv_num(x2); gv_puts(" head=" as *u8) 295 bg_head(out, l2) 296 gv_puts("\n" as *u8) 297 var t6: i64 = 0 298 if n2 > BG_OLD_WINDOW { t6 = 1 } 299 gv_check("T6 fixture reached the condition: the T2 request exceeds the old 65536-byte window" as *u8, t6, ctr) 300 var t2: i64 = 0 301 if x2 == BG_PAYLOAD_EDGE { t2 = 1 } 302 gv_check("T2 63 KB argv echoed whole (the measured failing case)" as *u8, t2, ctr) 303 if bg_has(out, l2, "{}" as *u8) == 1 { bad_shape = bad_shape + 1 } 304 if bg_has(out, l2, "64KiB edge limit" as *u8) == 1 { bad_shape = bad_shape + 1 } 305 // T3: 120 KB 306 let r3: *u8 = sys_mmap(BG_PAYLOAD_BIG + 8192) 307 let n3: i64 = bg_build(r3, BG_PAYLOAD_BIG, cap) 308 let l3: i64 = bg_roundtrip(BG_PORT_SUBJECT, r3, n3, out, BG_RESP_CAP, filled) 309 let x3: i64 = bg_xrun(out, l3) 310 gv_puts(" T3 request=" as *u8); gv_num(n3); gv_puts("B reply=" as *u8); gv_num(l3); gv_puts("B xrun=" as *u8); gv_num(x3); gv_puts("\n" as *u8) 311 var t3: i64 = 0 312 if x3 == BG_PAYLOAD_BIG { t3 = 1 } 313 gv_check("T3 120 KB argv echoed whole (over the old window, under the tool-capture reserve)" as *u8, t3, ctr) 314 if bg_has(out, l3, "{}" as *u8) == 1 { bad_shape = bad_shape + 1 } 315 if bg_has(out, l3, "64KiB edge limit" as *u8) == 1 { bad_shape = bad_shape + 1 } 316 // T4: neg-control -- a declared length above the ceiling, no body sent: 413 naming the limit, body unread 317 let r4: *u8 = sys_mmap(1024) 318 var o4: i64 = bg_cat(r4, 0, "POST /mcp HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Type: application/json\r\nConnection: close\r\nContent-Length: " as *u8) 319 o4 = bg_catn(r4, o4, BG_OVER_LIMIT_DECLARED) 320 o4 = bg_cat(r4, o4, "\r\n\r\n{" as *u8) 321 let l4: i64 = bg_roundtrip(BG_PORT_SUBJECT, r4, o4, out, BG_RESP_CAP, filled) 322 gv_puts(" T4 reply=" as *u8); gv_num(l4); gv_puts("B\n" as *u8) 323 var t4: i64 = 0 324 if bg_has(out, l4, "413 Payload Too Large" as *u8) == 1 { if bg_has(out, l4, "1048576" as *u8) == 1 { t4 = 1 } } 325 gv_check("T4 neg-control-over-ceiling: Content-Length above EDGE_REQ_MAX answers 413 naming the 1048576 limit, body unread" as *u8, t4, ctr) 326 var t5: i64 = 0 327 if bad_shape == 0 { t5 = 1 } 328 gv_check("T5 no reply is the bare {} nor the old 64KiB edge limit text" as *u8, t5, ctr) 329 if filled[0] == 1 { gv_puts(" reply reserve FILLED (BG_RESP_CAP) -- announced, not hidden\n" as *u8) } 330 } 331 // ---- teardown: kill both children and PROVE the ports refuse again ---- 332 bg_kill(spid) 333 bg_kill(cpid) 334 sys_sleep_ms(BG_BOOT_STEP_MS) 335 var g1: i64 = bg_connect(BG_PORT_SUBJECT) 336 var gone: i64 = 1 337 if g1 >= 0 { sys_close(g1); gone = 0 } 338 var g2: i64 = bg_connect(BG_PORT_CONTROL) 339 if g2 >= 0 { sys_close(g2); gone = 0 } 340 gv_check("T7 both forked daemons are gone (subject and control ports refuse again)" as *u8, gone, ctr) 341 return gv_verdict("TOOLSAPI-BODYCAP-GATE" as *u8, ctr, "whole-request read, derived ceiling, named refusals, behaviour control" as *u8) 342}