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}