code wiki / _hdl_build / nx_adnet_selfserve.nx
nx_adnet_selfserve.nx source
↩ module page · 106 lines · 4151 B
1// nx_adnet_selfserve.nx -- LIB: self-serve advertiser intake for the sovereign ad network. An advertiser
2// submits a full inventory row (id/advertiser/img/clickurl/section/weight -- the exact nx_adnet contract);
3// the row is VALIDATED with the same fail-closed rules the serving slot enforces (first-party img only,
4// http(s)/path click only, clean id) and then appended to a STAGED submissions journal. STAGED IS NOT
5// LIVE: nothing an advertiser submits ever serves until the operator approves it into the live inventory
6// conf (ass_approve appends a validated staged row there -- an operator action, never automatic).
7// B2B only; the schema HAS NO VISITOR FIELD -- no visitor identity can transit this path by construction.
8// license_tier: ORIGINAL
9import "nx_syscalls.nx"
10import "_hdl_build/nx_adnet_slot.nx"
11
12// validate a submitted inventory row against the serving rules (same validators the slot uses).
13func ass_valid_row(row: *u8, rlen: i64) -> i64 {
14 if rlen <= 0 { return 0 }
15 if rlen > 1024 { return 0 }
16 if row[0] == (64 as u8) { return 0 }
17 let id: *u8 = sys_mmap(128)
18 let img: *u8 = sys_mmap(1024)
19 let clk: *u8 = sys_mmap(1024)
20 let sec: *u8 = sys_mmap(128)
21 aslot_field_b(row, rlen, 0, id, 128)
22 aslot_field_b(row, rlen, 2, img, 1024)
23 aslot_field_b(row, rlen, 3, clk, 1024)
24 aslot_field_b(row, rlen, 4, sec, 128)
25 if aslot_id_ok(id) == 0 { return 0 }
26 if aslot_img_ok(img) == 0 { return 0 }
27 if aslot_click_ok(clk) == 0 { return 0 }
28 var i: i64 = 0
29 while sec[i] != (0 as u8) {
30 let c: u8 = sec[i]
31 var ok: i64 = 0
32 if c >= (97 as u8) { if c <= (122 as u8) { ok = 1 } }
33 if c >= (48 as u8) { if c <= (57 as u8) { ok = 1 } }
34 if ok == 0 { return 0 }
35 i = i + 1
36 if i > 64 { return 0 }
37 }
38 if i == 0 { return 0 }
39 return 1
40}
41
42// append one validated row to the staged submissions journal (append-only; NEVER the live inventory).
43// Returns 1 on validated+persisted (read-back verified), 0 = REFUSED or io failure.
44func ass_submit(subs_path: *u8, row: *u8, rlen: i64) -> i64 {
45 if ass_valid_row(row, rlen) == 0 { return 0 }
46 let fd: i64 = sys_openat_append(subs_path, 420)
47 if fd < 0 { return 0 }
48 let line: *u8 = sys_mmap(1152)
49 var o: i64 = 0
50 var i: i64 = 0
51 while i < rlen { line[o] = row[i]; o = o + 1; i = i + 1 }
52 line[o] = 10 as u8
53 o = o + 1
54 let wr: i64 = sys_write(fd, line, o)
55 sys_close(fd)
56 if wr != o { return 0 }
57 let box: *i64 = (sys_mmap(8)) as *i64
58 box[0] = 0
59 let back: *u8 = sys_read_file(subs_path, box)
60 if (back as i64) == 0 { return 0 }
61 let bn: i64 = box[0]
62 var j: i64 = 0
63 while j + rlen <= bn {
64 var k: i64 = 0
65 var eq: i64 = 1
66 while k < rlen { if back[j + k] != row[k] { eq = 0; break } k = k + 1 }
67 if eq == 1 { return 1 }
68 j = j + 1
69 }
70 return 0
71}
72
73// anti-snoop scanner: 1 if a visitor-identity-looking token is present in the journal bytes.
74// (The schema has no visitor field; this detector exists so the gate can PROVE it would catch a leak.)
75func ass_snoop_scan(buf: *u8, n: i64) -> i64 {
76 let m: *u8 = "VISITOR-TOKEN" as *u8
77 var i: i64 = 0
78 while i + 13 <= n {
79 if buf[i] == (86 as u8) {
80 var k: i64 = 0
81 var eq: i64 = 1
82 while k < 13 { if buf[i + k] != m[k] { eq = 0; break } k = k + 1 }
83 if eq == 1 { return 1 }
84 }
85 i = i + 1
86 }
87 return 0
88}
89
90// OPERATOR APPROVE: re-validate a staged row and append it to the LIVE inventory conf. This is the
91// only staged->live bridge and it is invoked by the operator lane, never by the submit path.
92func ass_approve(inv_path: *u8, row: *u8, rlen: i64) -> i64 {
93 if ass_valid_row(row, rlen) == 0 { return 0 }
94 let fd: i64 = sys_openat_append(inv_path, 420)
95 if fd < 0 { return 0 }
96 let line: *u8 = sys_mmap(1152)
97 var o: i64 = 0
98 var i: i64 = 0
99 while i < rlen { line[o] = row[i]; o = o + 1; i = i + 1 }
100 line[o] = 10 as u8
101 o = o + 1
102 let wr: i64 = sys_write(fd, line, o)
103 sys_close(fd)
104 if wr != o { return 0 }
105 return 1
106}