code wiki / (root) / nx_manga_rating.nx

nx_manga_rating.nx source

↩ module page · 185 lines · 8020 B

1// nx_manga_rating.nx -- THE CONTENT RATING PLANE for the manga lane (mangagen MG24 mr_rate_row, 2// MG32 mr_adult_invariant). A rating is DATA CARRIED ON THE ASSET -- never a property of the 3// account, and never which folder somebody remembered to drop the file in. 4// 5// COMPOSES THE INCUMBENTS AND RE-IMPLEMENTS NEITHER: 6// nx_torrent_access nx_taccess_allow(viewer_level, area) -- the fail-closed VIEWER-LEVEL 7// decision from the operator directive of 2026-06-20. Levels OWNER 3 / 8// FAMILY 1 / ANON 0; areas SFW 0 requires FAMILY, NSFW 1 requires OWNER. 9// This organ NEVER re-decides access; it only says which AREA an asset 10// belongs in, and hands that to the incumbent ruler. 11// nx_wardrobe_state WS_ADULT_AGE -- the adult threshold that already fails closed at 12// construction in the character lane. IMPORTED rather than redeclared: 13// the same constant written twice is two constants to every scanner, and 14// an age threshold that can drift between two organs is a defect generator. 15// 16// THE LOAD-BEARING PROPERTY, and the whole point of the organ: 17// AN UNRATED ASSET IS TREATED AS GATED, NEVER AS SAFE. An absent rating is not evidence of 18// innocence. Every mapping below fails toward the MORE restrictive area, so the failure mode of 19// a missing row is the family viewer cannot see it, never the child can. 20 21import "nx_syscalls.nx" 22import "nx_torrent_access.nx" 23import "nx_wardrobe_state.nx" 24 25// -- THE RATING LADDER. Rows, not opinions: a caller stores the integer, the meaning lives here. 26const MR_RATE_UNRATED: i64 = 0-1 27const MR_RATE_ALLAGES: i64 = 0 28const MR_RATE_TEEN: i64 = 1 29const MR_RATE_MATURE: i64 = 2 30const MR_RATE_ADULT: i64 = 3 31const MR_RATE_MAX: i64 = 3 32 33// -- RETURN CODES. Named so a refusal can say WHICH rule fired rather than just no. 34const MR_OK: i64 = 0 35const MR_E_MINOR: i64 = 0-1 36const MR_E_RATING: i64 = 0-2 37const MR_E_ROW: i64 = 0-3 38 39const MR_ROWCAP: i64 = 512 40const MR_PIPE: i64 = 124 41 42func mr_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 43 44func mr_apps(dst: *u8, o: i64, s: *u8) -> i64 { 45 var i: i64 = 0 46 while s[i] != (0 as u8) { dst[o + i] = s[i]; i = i + 1 } 47 return o + i 48} 49 50func mr_num(dst: *u8, o: i64, v: i64) -> i64 { 51 var w: i64 = o 52 var m: i64 = v 53 if m < 0 { dst[w] = 45 as u8; w = w + 1; m = 0 - m } 54 if m == 0 { dst[w] = 48 as u8; return w + 1 } 55 let t: *u8 = sys_mmap(32) 56 var k: i64 = 0 57 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 58 while k > 0 { k = k - 1; dst[w] = t[k]; w = w + 1 } 59 return w 60} 61 62// mr_rating_valid -- a rating is either UNRATED or inside the ladder. Anything else is a MALFORMED 63// ROW and must be reported as such, never silently coerced into a verdict. 64func mr_rating_valid(r: i64) -> i64 { 65 if r == MR_RATE_UNRATED { return 1 } 66 if r < MR_RATE_ALLAGES { return 0 } 67 if r > MR_RATE_MAX { return 0 } 68 return 1 69} 70 71// mr_area_for_rating -- THE MAPPING, and the direction it fails in IS the design. 72// UNRATED maps to NSFW on purpose: an asset nobody has rated is gated, not safe. 73func mr_area_for_rating(r: i64) -> i64 { 74 if r == MR_RATE_UNRATED { return NX_TAREA_NSFW } 75 if r >= MR_RATE_MATURE { return NX_TAREA_NSFW } 76 return NX_TAREA_SFW 77} 78 79// mr_may_serve -- COMPOSE, never re-decide. The AREA comes from the asset own rating; the 80// allow/deny comes from the incumbent access ruler. An invalid rating denies rather than defaulting. 81func mr_may_serve(viewer_level: i64, r: i64) -> i64 { 82 if mr_rating_valid(r) == 0 { return NX_TACCESS_DENY } 83 let area: i64 = mr_area_for_rating(r) 84 return nx_taccess_allow(viewer_level, area) 85} 86 87// mr_adult_invariant -- MG32, FAIL-CLOSED AT CONSTRUCTION. A subject below the adult age may not 88// carry a MATURE-or-above rating. Returns a NAMED code rather than a boolean so the caller can 89// report which rule fired. WS_ADULT_AGE is the character lane threshold, imported not copied. 90func mr_adult_invariant(subject_age: i64, r: i64) -> i64 { 91 if mr_rating_valid(r) == 0 { return MR_E_RATING } 92 if r < MR_RATE_MATURE { return MR_OK } 93 if subject_age < WS_ADULT_AGE { return MR_E_MINOR } 94 return MR_OK 95} 96 97// mr_rate_row -- MG24. Write the rating row for one asset. The ROW is the artifact: a rating that 98// exists only in a caller variable is a rating nobody can audit and nothing can re-check. 99// rate|<asset-cid>|<rating>|<source>|<epoch> 100func mr_rate_row(cid: *u8, r: i64, src: *u8, epoch: i64, out: *u8, cap: i64) -> i64 { 101 if mr_rating_valid(r) == 0 { return MR_E_RATING } 102 let need: i64 = mr_slen(cid) + mr_slen(src) + 48 103 if need > cap { return MR_E_ROW } 104 var o: i64 = 0 105 o = mr_apps(out, o, "rate" as *u8) 106 out[o] = MR_PIPE as u8; o = o + 1 107 o = mr_apps(out, o, cid) 108 out[o] = MR_PIPE as u8; o = o + 1 109 o = mr_num(out, o, r) 110 out[o] = MR_PIPE as u8; o = o + 1 111 o = mr_apps(out, o, src) 112 out[o] = MR_PIPE as u8; o = o + 1 113 o = mr_num(out, o, epoch) 114 out[o] = 0 as u8 115 return o 116} 117 118func mr_puts(s: *u8) -> i64 { sys_write(1, s, mr_slen(s)); return 0 } 119func mr_putn(v: i64) -> i64 { let b: *u8 = sys_mmap(64); let n: i64 = mr_num(b, 0, v); sys_write(1, b, n); return 0 } 120 121func mr_atoi(s: *u8) -> i64 { 122 var i: i64 = 0 123 var neg: i64 = 0 124 if s[0] == (45 as u8) { neg = 1; i = 1 } 125 var v: i64 = 0 126 while s[i] != (0 as u8) { 127 let c: i64 = s[i] as i64 128 if c < 48 { return v } 129 if c > 57 { return v } 130 v = v * 10 + (c - 48) 131 i = i + 1 132 } 133 if neg == 1 { return 0 - v } 134 return v 135} 136 137func main(argc: i64, argv: *i64) -> i64 { 138 if argc < 2 { 139 mr_puts("usage: nx_manga_rating row <cid> <rating> <source> <epoch>\n" as *u8) 140 mr_puts(" nx_manga_rating serve <viewer_level> <rating>\n" as *u8) 141 mr_puts(" nx_manga_rating adult <subject_age> <rating>\n" as *u8) 142 mr_puts("ratings: -1 UNRATED gated, 0 ALLAGES, 1 TEEN, 2 MATURE, 3 ADULT\n" as *u8) 143 return 3 144 } 145 let verb: *u8 = argv[1] as *u8 146 147 if verb[0] == (114 as u8) { 148 if argc < 6 { mr_puts("usage: row <cid> <rating> <source> <epoch>\n" as *u8); return 3 } 149 let out: *u8 = sys_mmap(MR_ROWCAP) 150 let rc: i64 = mr_rate_row(argv[2] as *u8, mr_atoi(argv[3] as *u8), argv[4] as *u8, mr_atoi(argv[5] as *u8), out, MR_ROWCAP) 151 if rc < 0 { mr_puts("REFUSED rule=" as *u8); mr_putn(rc); mr_puts(" rating outside the ladder, or the row would not fit\n" as *u8); return 1 } 152 mr_puts(out); mr_puts("\n" as *u8) 153 return 0 154 } 155 156 if verb[0] == (115 as u8) { 157 if argc < 4 { mr_puts("usage: serve <viewer_level> <rating>\n" as *u8); return 3 } 158 let vl: i64 = mr_atoi(argv[2] as *u8) 159 let r: i64 = mr_atoi(argv[3] as *u8) 160 let a: i64 = mr_may_serve(vl, r) 161 mr_puts("viewer_level=" as *u8); mr_putn(vl) 162 mr_puts(" rating=" as *u8); mr_putn(r) 163 mr_puts(" area=" as *u8); mr_putn(mr_area_for_rating(r)) 164 if a == NX_TACCESS_ALLOW { mr_puts(" verdict=ALLOW\n" as *u8); return 0 } 165 mr_puts(" verdict=DENY\n" as *u8) 166 return 1 167 } 168 169 if verb[0] == (97 as u8) { 170 if argc < 4 { mr_puts("usage: adult <subject_age> <rating>\n" as *u8); return 3 } 171 let age: i64 = mr_atoi(argv[2] as *u8) 172 let r2: i64 = mr_atoi(argv[3] as *u8) 173 let rc2: i64 = mr_adult_invariant(age, r2) 174 mr_puts("subject_age=" as *u8); mr_putn(age) 175 mr_puts(" rating=" as *u8); mr_putn(r2) 176 mr_puts(" adult_age=" as *u8); mr_putn(WS_ADULT_AGE) 177 if rc2 == MR_OK { mr_puts(" verdict=OK\n" as *u8); return 0 } 178 if rc2 == MR_E_MINOR { mr_puts(" verdict=REFUSED rule=MR_E_MINOR\n" as *u8); return 1 } 179 mr_puts(" verdict=REFUSED rule=MR_E_RATING\n" as *u8) 180 return 1 181 } 182 183 mr_puts("unknown verb\n" as *u8) 184 return 3 185}