code wiki / (root) / nx_site_publish_lib_pre_release_t141.nx

nx_site_publish_lib_pre_release_t141.nx source

↩ module page · 471 lines · 24704 B

1// nx_site_publish_lib.nx -- ARTIFACT-PUBLISH, LIBRARY half (sp_main dispatcher) capability (closes the coverage BUILD-queue gap "artifact-publish"; 2// retires the ssh-cat content lane for pages). Places a NAS-local file into the live sites docroot the 3// SAFE way, composing the proven IO layer: 4// publish <src-file> <site> <relpath> -> sites/<site>/<relpath> 5// - ATOMIC placement via fsx_write (tmp+fsync+rename -- a browser never sees a torn page) 6// - PRIOR VERSION PRESERVED: an existing target is first copied to <relpath>.prev (rename-not-delete 7// doctrine; one-deep history -- generated artifacts, git holds sources) 8// - DENY BY CONSTRUCTION (fsx_write_denied: secrets/device-ns/allowlist) PLUS path-traversal refusal: 9// any ".." segment or absolute site/relpath is DENIED (an MCP caller must not escape the docroot) 10// The organ prints the live URL; verification is the SEPARATE browser-grade organ (composition, not a 11// built-in fallback): run nx_page_verify <url> from a WAN vantage (on-NAS bare-domain fetches hit DSM -- 12// the documented trap). The publish->verify recipe is the contract; this organ does the placement half. 13// - THE PUBLISH GATEWAY (F763 second half, wired 2026-07-30): for a site listed in 14// knowledge/pub_sites.conf, a <relpath> absent from that site's publishing REGISTRY -- or 15// carrying a non-publishable status (withdrawn/redirect/debris) -- is REFUSED. This is the 16// half that makes "random and arbitrary" impossible instead of merely visible: nx_pub_desk 17// stated the rule in 2026-07-19 and its own header recorded that enforcement inside the 18// publish organ was never wired, so anything could be dropped into any docroot and become 19// site truth. MEASURED before wiring: 54 pages served on nishifamily.com were in NO registry. 20// Sites NOT in the site table publish UNGATED (with a warning) -- onboarding is a config row, 21// and a control that refuses everything on day one is a control someone disables by lunchtime. 22// exit: 0 ok | 2 usage | 3 src absent | 4 io | 5 DENIED license_tier: ORIGINAL 23import "nx_fsops_lib.nx" // fsx_write (atomic + write-deny), vw_read, vw_slen, fsx_puts, fsx_putn 24import "nx_pub_lib.nx" // pl_site_lookup + pl_check -- the registry gateway 25import "nx_pub_release_lib.nx" 26import "nx_sha256.nx" 27import "nx_comparestale_lib.nx" 28 29const PB_ARG_VERB: i64 = 1 // argv: verb ("publish") 30const PB_RC_USAGE: i64 = 2 // exit: usage 31const PB_ARG_SRC: i64 = 2 // argv: source file (NAS-local) 32const PB_ARG_SITE: i64 = 3 // argv: site dir under sites/ 33const PB_ARG_REL: i64 = 4 // argv: page path under the site 34const PB_ARGC: i64 = 5 // publish src site relpath 35const PB_STAGE_ARGC: i64 = 6 // stage src site releases/<path> sha256 36const PB_GUARDED_ARGC: i64 = PB_STAGE_ARGC + 1 // adds expected live digest 37const PB_GUARDED_SIZED_ARGC: i64 = PB_GUARDED_ARGC + 1 // optional exact artifact extent 38const PB_PATH: i64 = 1024 // destination path buffer 39const PB_CONFCAP: i64 = 65536 // site-table read buffer (the gateway) 40const PB_SPANCAP: i64 = 128 // site-table span vector 41const PB_DOT: i64 = 46 // '.' 42const PB_SLASH: i64 = 47 // '/' 43 44// any ".." path segment, or a leading '/', or empty -> traversal refusal (1 = refuse) 45func pb_traversal(s: *u8) -> i64 { 46 if s[0] == (0 as u8) { return 1 } 47 if s[0] == (PB_SLASH as u8) { return 1 } 48 var i: i64 = 0 49 while s[i] != (0 as u8) { 50 if s[i] == (PB_DOT as u8) { if s[i+1] == (PB_DOT as u8) { return 1 } } 51 // Reject aliases before deriving the target lock: ./ and // could name the same file 52 // using different lock paths or conceal the reserved release namespace. 53 if s[i] == (PB_SLASH as u8) { 54 if s[i+1] == (PB_SLASH as u8) { return 1 } 55 if s[i+1] == (0 as u8) { return 1 } 56 } 57 var segment_start: i64 = 0 58 if i == 0 { segment_start = 1 } else { 59 if s[i-1] == (PB_SLASH as u8) { segment_start = 1 } 60 } 61 if segment_start == 1 { 62 if s[i] == (PB_DOT as u8) { 63 if (s[i+1] == (PB_SLASH as u8)) || (s[i+1] == (0 as u8)) { return 1 } 64 } 65 } 66 i = i + 1 67 } 68 return 0 69} 70// append NUL-terminated src to out at *op 71func pb_app(out: *u8, op: *i64, s: *u8) -> i64 { 72 var i: i64 = 0 73 var o: i64 = op[0] 74 while s[i] != (0 as u8) { out[o] = s[i]; o = o + 1; i = i + 1 } 75 out[o] = 0 as u8 76 op[0] = o 77 return 0 78} 79 80// Artifact equality is byte-counted: WASM and other binary assets contain NUL bytes. 81func pb_same_bytes(a: *u8, an: i64, b: *u8, bn: i64) -> i64 { 82 if an != bn { return 0 } 83 if an < 0 { return 0 } 84 var i: i64 = 0 85 while i < an { if a[i] != b[i] { return 0 }; i = i + 1 } 86 return 1 87} 88 89const PB_LOCK_EX_NB: i64 = 6 90const PB_RC_CONFLICT: i64 = 8 91const PB_RC_BUSY: i64 = 9 92const PB_SHA_BYTES: i64 = 32 93const PB_SHA_HEX: i64 = 64 94func pb_hash(body: *u8, n: i64, hex: *u8) -> i64 { 95 let raw: *u8 = sys_mmap(PB_SHA_BYTES) 96 sha256_digest(body,n,raw) 97 let digits: *u8 = "0123456789abcdef" as *u8 98 var i: i64 = 0 99 while i < PB_SHA_BYTES { 100 let c: i64 = raw[i] as i64 101 hex[i*2] = digits[c/16]; hex[i*2+1] = digits[c%16] 102 i = i+1 103 } 104 hex[PB_SHA_HEX] = 0 as u8 105 sys_munmap(raw,PB_SHA_BYTES) 106 return 0 107} 108 109const PB_SIZED_ARGC: i64 = 7 110const PB_I64_MAX: i64 = 9223372036854775807 111func pb_sized_bytes(s: *u8) -> i64 { 112 var value: i64=0 113 var i: i64=0 114 while s[i] != (0 as u8) { 115 let c: i64=s[i] as i64 116 if c < 48 || c > 57 { return 0-1 } 117 if value > (PB_I64_MAX/2-1-(c-48))/10 { return 0-1 } 118 value=value*10+c-48; i=i+1 119 } 120 if value <= 0 { return 0-1 } 121 return value 122} 123func pb_read_bounded(path: *u8,b: *u8,cap: i64) -> i64 { 124 let fd: i64=sys_openat_rd(path) 125 if fd < 0 { return fd } 126 var n: i64=0 127 var go: i64=1 128 while n < cap && go == 1 { 129 let got: i64=sys_read(fd,((b as i64)+n) as *u8,cap-n) 130 if got < 0 { sys_close(fd); return got } 131 if got == 0 { go=0 } else { n=n+got } 132 } 133 sys_close(fd) 134 return n 135} 136 137func pb_main_edge(argc: i64, argv: *i64, edge_rel: *u8, edge_sha: *u8) -> i64 { 138 if argc < PB_ARGC { return pb_publish_locked_edge(argc,argv,edge_rel,edge_sha) } 139 let verb: *u8 = argv[PB_ARG_VERB] as *u8 140 if fsx_seq(verb,"publish" as *u8) == 0 { 141 if fsx_seq(verb,"stage" as *u8) == 1 { 142 if argc != PB_STAGE_ARGC { return PB_RC_USAGE } 143 } else { 144 if fsx_seq(verb,"publish-guarded" as *u8) == 0 { if fsx_seq(verb,"stage-sized" as *u8) == 0 { return PB_RC_USAGE } } 145 if argc != PB_GUARDED_ARGC { 146 if fsx_seq(verb,"publish-guarded" as *u8) == 0 || argc != PB_GUARDED_SIZED_ARGC { return PB_RC_USAGE } 147 } 148 } 149 } 150 let site: *u8 = argv[PB_ARG_SITE] as *u8 151 let rel: *u8 = argv[PB_ARG_REL] as *u8 152 var si: i64 = 0 153 while site[si] != (0 as u8) { 154 if site[si] == (PB_SLASH as u8) { return FSX_RC_DENIED } 155 si = si + 1 156 } 157 if fsx_seq(verb,"stage" as *u8) == 1 || fsx_seq(verb,"stage-sized" as *u8) == 1 { 158 if pb_release_path(rel) == 0 { 159 fsx_puts("NX-PUBLISH DENIED stage requires releases/<candidate-path>; live URL is not a staging target\n" as *u8) 160 return FSX_RC_DENIED 161 } 162 } 163 if pb_traversal(site) == 1 { return FSX_RC_DENIED } 164 if pb_traversal(rel) == 1 { return FSX_RC_DENIED } 165 if vw_slen(site)+vw_slen(rel)+vw_slen("sites//.publish-lock" as *u8)+1 >= PB_PATH { return FSX_RC_DENIED } 166 let path: *u8 = sys_mmap(PB_PATH) 167 let pos: *i64 = sys_mmap(8) as *i64 168 pos[0] = 0 169 pb_app(path,pos,"sites/" as *u8);pb_app(path,pos,site) 170 pb_app(path,pos,"/" as *u8);pb_app(path,pos,rel) 171 if fsx_write_denied(path) == 1 { return FSX_RC_DENIED } 172 // Keep one stable inode per target; unlinking a lock lets contenders lock different inodes. 173 pb_app(path,pos,".publish-lock" as *u8) 174 let fd: i64 = sys_openat_wr(path,FSX_MODE_RW) 175 if fd < 0 { fsx_puts("NX-PUBLISH IO lock-parent-unavailable\n" as *u8);return FSX_RC_IO } 176 if sys_flock(fd,PB_LOCK_EX_NB) != 0 { 177 sys_close(fd) 178 fsx_puts("NX-PUBLISH BUSY target-owned retry-after-owner-completes\n" as *u8) 179 return PB_RC_BUSY 180 } 181 let rc: i64 = pb_publish_locked_edge(argc,argv,edge_rel,edge_sha) 182 sys_close(fd) 183 return rc 184} 185// Every publisher verb enforces this namespace, so a legacy publish cannot replace a tested candidate. 186// The site registry still authorizes each release path; staging does not bypass publication policy. 187func pb_release_path(rel: *u8) -> i64 { 188 let prefix: *u8 = "releases/" as *u8 189 var i: i64 = 0 190 while prefix[i] != (0 as u8) { 191 if rel[i] != prefix[i] { return 0 } 192 i = i + 1 193 } 194 return (rel[i] != (0 as u8)) as i64 195} 196func pb_publish_locked_edge(argc: i64, argv: *i64, edge_rel: *u8, edge_sha: *u8) -> i64 { 197 var okverb: i64 = 0 198 if argc >= PB_ARGC { if fsx_seq(argv[PB_ARG_VERB] as *u8, "publish" as *u8) == 1 { okverb = 1 } } 199 if argc == PB_GUARDED_ARGC || argc == PB_GUARDED_SIZED_ARGC { if fsx_seq(argv[PB_ARG_VERB] as *u8,"publish-guarded" as *u8) == 1 { okverb = 1 } } 200 if argc == PB_STAGE_ARGC { if fsx_seq(argv[PB_ARG_VERB] as *u8,"stage" as *u8) == 1 { okverb = 1 } } 201 if argc == PB_SIZED_ARGC { if fsx_seq(argv[PB_ARG_VERB] as *u8,"stage-sized" as *u8) == 1 { okverb = 1 } } 202 if okverb == 0 { 203 fsx_puts("usage: nx_site_publish publish <src-file> <site> <relpath> (-> sites/<site>/<relpath>; then verify: nx_page_verify <url>)\n" as *u8) 204 fsx_puts(" nx_site_publish publish-guarded <src-file> <site> <relpath> <sha256> <expected-live-sha256|absent> [exact-bytes]\n nx_site_publish stage <src-file> <site> releases/<candidate-path> <sha256>\nStaging verifies file identity only; browser and dependency acceptance are separate evidence.\n" as *u8) 205 return PB_RC_USAGE 206 } 207 let site: *u8 = argv[PB_ARG_SITE] as *u8 208 let rel: *u8 = argv[PB_ARG_REL] as *u8 209 if pb_traversal(site) == 1 { 210 fsx_puts("NX-PUBLISH DENIED: site escapes the docroot ('..'/absolute/empty)\n" as *u8) 211 return FSX_RC_DENIED 212 } 213 if pb_traversal(rel) == 1 { 214 fsx_puts("NX-PUBLISH DENIED: relpath escapes the docroot ('..'/absolute/empty)\n" as *u8) 215 return FSX_RC_DENIED 216 } 217 // ---- THE PUBLISH GATEWAY: the registry decides what may exist at this URL, not the caller ---- 218 let gw_conf: *u8 = sys_mmap(PB_CONFCAP) 219 let gw_spans: *i64 = sys_mmap(PB_SPANCAP) as *i64 220 var release_prefix: *u8 = 0 as *u8 221 var registry_checked: i64 = 0 222 if pl_site_lookup("knowledge/pub_sites.conf" as *u8, site, gw_conf, gw_spans) == 1 { 223 registry_checked = 1 224 let gw_pfx: *u8 = sys_mmap(PB_PATH) 225 pl_span_cstr(gw_conf, gw_spans[2], gw_spans[3], gw_pfx) 226 var row_status: i64 = 0 227 if pb_release_path(rel) == 1 { release_prefix = gw_pfx } else { row_status = pl_check(gw_pfx,rel) } 228 if row_status != 0 { 229 fsx_puts("NX-PUBLISH REFUSED: " as *u8); fsx_puts(rel) 230 fsx_puts(" is not a publishable row in this site's registry.\n" as *u8) 231 fsx_puts(" The registry -- not the caller -- decides what may exist at a URL. Either the path is\n" as *u8) 232 fsx_puts(" unregistered, or its status is withdrawn/redirect/debris.\n" as *u8) 233 fsx_puts(" Register it (id/title/owner/status/section/path/note, status=live|asset|draft) in " as *u8) 234 fsx_puts(gw_pfx); fsx_puts(" ,\n" as *u8) 235 fsx_puts(" or run: nx_pub_plane adopt " as *u8); fsx_puts(site) 236 fsx_puts(" to bring existing docroot entries in. Then re-run this publish.\n" as *u8) 237 return FSX_RC_DENIED 238 } 239 } else { 240 fsx_puts("NX-PUBLISH WARN: site '" as *u8); fsx_puts(site) 241 fsx_puts("' is not in knowledge/pub_sites.conf -- publishing UNGATED (add a row to gate it).\n" as *u8) 242 } 243 // stage-sized declares its exact artifact size; the read budget is that size plus one 244 // to distinguish complete bytes from truncation. Legacy verbs retain their existing cap. 245 var readcap: i64 = FSX_READ_CAP 246 var wanted: i64 = 0 247 var size_arg: i64 = 0 248 if fsx_seq(argv[PB_ARG_VERB] as *u8,"stage-sized" as *u8) == 1 { 249 size_arg=6 250 if (release_prefix as i64) == 0 { return FSX_RC_DENIED } 251 } 252 if argc == PB_GUARDED_SIZED_ARGC && fsx_seq(argv[PB_ARG_VERB] as *u8,"publish-guarded" as *u8) == 1 { 253 size_arg=7 254 if registry_checked != 1 { return FSX_RC_DENIED } 255 } 256 if size_arg > 0 { 257 wanted=pb_sized_bytes(argv[size_arg] as *u8) 258 if wanted < 0 { return PB_RC_USAGE } 259 readcap=wanted+1 260 } 261 let body: *u8 = sys_mmap(readcap + 1) 262 let n: i64 = pb_read_bounded(argv[PB_ARG_SRC] as *u8, body, readcap) 263 if wanted > 0 { if n != wanted { fsx_puts("NX-PUBLISH REFUSED source-size-mismatch\n" as *u8); return PB_RC_CONFLICT } } 264 if n <= 0 { 265 fsx_puts("NX-PUBLISH ABSENT: cannot read source " as *u8); fsx_puts(argv[PB_ARG_SRC] as *u8); fsx_puts("\n" as *u8) 266 return FSX_RC_ABSENT 267 } 268 if n == readcap { fsx_puts("NX-PUBLISH REFUSED: source at/over the 1MiB organ cap -- use the mgmt upload lane\n" as *u8); return FSX_RC_IO } 269 // The owned body is checked once here and later passed unchanged to fsx_write. 270 if (edge_rel as i64)>0 { 271 let edge_rc: i64=pe_check(body,n,argv,edge_rel,edge_sha) 272 if edge_rc!=0{return edge_rc} 273 } 274 // Every verb checks the registered content, including legacy publish without a caller hash. 275 if (release_prefix as i64) != 0 { 276 let release_hash: *u8 = sys_mmap(PB_SHA_HEX+1) 277 pb_hash(body,n,release_hash) 278 let registered: i64 = pr_check(release_prefix,rel,release_hash) 279 sys_munmap(release_hash,PB_SHA_HEX+1) 280 if registered != PR_OK { 281 fsx_puts("NX-PUBLISH REFUSED release-registration code=" as *u8); fsx_putn(registered) 282 fsx_puts(" path=" as *u8); fsx_puts(rel) 283 fsx_puts("; verify catalogue integrity and registered digest before retrying\n" as *u8) 284 if registered == PR_IO { return FSX_RC_IO } 285 return PB_RC_CONFLICT 286 } 287 } 288 // build dst = sites/<site>/<relpath> 289 let dst: *u8 = sys_mmap(PB_PATH) 290 let op: *i64 = sys_mmap(8) as *i64 291 op[0] = 0 292 pb_app(dst, op, "sites/" as *u8) 293 pb_app(dst, op, site) 294 pb_app(dst, op, "/" as *u8) 295 pb_app(dst, op, rel) 296 // preserve the prior version (one-deep; rename-not-delete doctrine) 297 // The predecessor has its own extent: a smaller incoming artifact must still retain it whole. 298 var priorcap: i64 = readcap 299 if wanted > 0 { 300 let priorfd: i64 = sys_openat_rd(dst) 301 if priorfd >= 0 { 302 let extent: i64 = sys_lseek(priorfd,0,FSX_SEEK_END) 303 let closed: i64 = sys_close(priorfd) 304 if extent < 0 || extent > PB_I64_MAX/2-1 || closed != 0 { 305 fsx_puts("NX-PUBLISH REFUSED prior-extent-unavailable-or-overflow\n" as *u8) 306 return FSX_RC_IO 307 } 308 if extent >= priorcap { priorcap=extent+1 } 309 } else { 310 if priorfd != (0-2) { fsx_puts("NX-PUBLISH REFUSED prior-open-error\n" as *u8);return FSX_RC_IO } 311 } 312 } 313 let old: *u8 = sys_mmap(priorcap + 1) 314 if (old as i64) <= 0 { fsx_puts("NX-PUBLISH REFUSED prior-allocation-failed\n" as *u8);return FSX_RC_IO } 315 let on: i64 = pb_read_bounded(dst, old, priorcap) 316 // A retry must retain the last DIFFERENT version, not replace it with the current one. 317 if fsx_write_denied(dst) == 1 { 318 fsx_puts("NX-PUBLISH DENIED: destination rejected before retry comparison\n" as *u8) 319 return FSX_RC_DENIED 320 } 321 if on >= priorcap { fsx_puts("NX-PUBLISH REFUSED prior-at-read-cap\n" as *u8);return FSX_RC_IO } 322 let staged: i64 = fsx_seq(argv[PB_ARG_VERB] as *u8,"stage" as *u8) | fsx_seq(argv[PB_ARG_VERB] as *u8,"stage-sized" as *u8) 323 if (fsx_seq(argv[PB_ARG_VERB] as *u8,"publish-guarded" as *u8) == 1) || (staged == 1) { 324 let incoming: *u8 = sys_mmap(PB_SHA_HEX+1) 325 pb_hash(body,n,incoming) 326 if fsx_seq(incoming,argv[5] as *u8) == 0 { 327 fsx_puts("NX-PUBLISH CONFLICT artifact-sha256-mismatch\n" as *u8) 328 return PB_RC_CONFLICT 329 } 330 // A retry with the same result succeeds after its predecessor has changed. 331 if (staged == 0) && (pb_same_bytes(body,n,old,on) == 0) { 332 let current: *u8 = sys_mmap(PB_SHA_HEX+1) 333 if on < 0 { 334 let stat: *u8 = sys_mmap(160) 335 if sys_fstatat(dst,stat) != (0-2) { return FSX_RC_IO } 336 if fsx_seq(argv[6] as *u8,"absent" as *u8) == 0 { return PB_RC_CONFLICT } 337 } else { 338 pb_hash(old,on,current) 339 if fsx_seq(current,argv[6] as *u8) == 0 { 340 fsx_puts("NX-PUBLISH CONFLICT live-sha256-mismatch\n" as *u8) 341 return PB_RC_CONFLICT 342 } 343 } 344 } 345 } 346 if (pb_release_path(rel) == 1) && (pb_same_bytes(body,n,old,on) == 0) { 347 // A failed read is not absence. Only kernel ENOENT permits a new release path. 348 let stat: *u8 = sys_mmap(160) 349 let observed: i64 = sys_fstatat(dst,stat) 350 if observed != (0-2) { 351 if observed == 0 { 352 fsx_puts("NX-PUBLISH CONFLICT immutable-release-path; retain tested bytes and stage a new candidate path\n" as *u8) 353 return PB_RC_CONFLICT 354 } 355 fsx_puts("NX-PUBLISH IO release-target-stat errno=" as *u8);fsx_putn(observed) 356 fsx_puts(" target=" as *u8);fsx_puts(dst);fsx_puts("; resolve target access before retry\n" as *u8) 357 return FSX_RC_IO 358 } 359 } 360 if pb_same_bytes(body, n, old, on) == 1 { 361 fsx_puts("NX-PUBLISH UNCHANGED bytes=" as *u8); fsx_putn(n) 362 fsx_puts(" -> " as *u8); fsx_puts(dst) 363 fsx_puts(" prior_version=retained writes=0\n" as *u8) 364 return 0 365 } 366 if on > 0 { 367 let prev: *u8 = sys_mmap(PB_PATH) 368 let pp: *i64 = sys_mmap(8) as *i64 369 pp[0] = 0 370 pb_app(prev, pp, dst) 371 pb_app(prev, pp, ".prev" as *u8) 372 let pw: i64 = fsx_write(prev, old, on) 373 if pw < 0 { fsx_puts("NX-PUBLISH IO: could not preserve .prev; refusing to overwrite\n" as *u8); return FSX_RC_IO } 374 } 375 // atomic placement through the IO layer (write-deny classes included by construction) 376 let w: i64 = fsx_write(dst, body, n) 377 if w < 0 { 378 // ⚠THE SILENT EXIT, FIXED (debt seq910). Every other failure path in sp_main prints 379 // a diagnostic; this one returned an error code and NOT ONE WORD. Measured 2026-07-25: 380 // `publish <src> nishifamily research/rt004.html` produced EMPTY output and published 381 // nothing, which reads exactly like success to a caller that only sees stdout. 382 // ROOT CAUSE (read, not guessed): fsx_write does NOT create parent directories -- 383 // sys_openat_wr fails and it returns -3 -- so any relpath containing a subdirectory 384 // fails here when sites/<site>/<subdir>/ is absent. Auto-creating docroot directories 385 // is a deliberate NON-goal (deny-by-construction), so the fix is to FAIL LOUD and name 386 // the cause rather than to start creating directories. 387 if w == 0 - (2 as i64) { 388 fsx_puts("NX-PUBLISH DENIED: the IO layer refused this destination -> " as *u8) 389 fsx_puts(dst); fsx_puts("\n" as *u8) 390 return FSX_RC_DENIED 391 } 392 fsx_puts("NX-PUBLISH IO-FAIL: NOTHING WAS PUBLISHED -> " as *u8); fsx_puts(dst); fsx_puts("\n" as *u8) 393 fsx_puts(" likely cause: the parent directory does not exist (this organ never creates docroot dirs).\n" as *u8) 394 fsx_puts(" if <relpath> contains a subdirectory, create it first or publish to a flat relpath.\n" as *u8) 395 return FSX_RC_IO 396 } 397 fsx_puts("NX-PUBLISH OK bytes=" as *u8); fsx_putn(w) 398 fsx_puts(" -> " as *u8); fsx_puts(dst) 399 fsx_puts("\nVERIFY (browser-grade, WAN vantage): nx_page_verify https://<site-domain>/" as *u8); fsx_puts(rel) 400 fsx_puts(" (site->domain map: surfaces.reg)\n" as *u8) 401 return 0 402} 403 404const PE_ARGC: i64=10 405// This lock coordinates only publishers honoring the immutable registered release contract. 406func pe_main(argc: i64,argv: *i64) -> i64 { 407 if argc!=PE_ARGC{return PB_RC_USAGE} 408 // Refuse a missing/nonwritable caller output before open can reuse descriptor1 for a lock. 409 if sys_write(1,"",0)!=0{return FSX_RC_IO} 410 let site: *u8=argv[3] as *u8;let rel: *u8=argv[8] as *u8 411 if pb_traversal(site)==1||pb_traversal(rel)==1||pr_path_ok(rel)==0||pr_hash_ok(argv[9] as *u8)==0{return FSX_RC_DENIED} 412 var i: i64=0;while site[i]!=0{if site[i]==47{return FSX_RC_DENIED};i=i+1} 413 if fsx_seq(rel,argv[4] as *u8)==1{return FSX_RC_DENIED} 414 if vw_slen(site)+vw_slen(rel)+vw_slen("sites//.publish-lock")+1>=PB_PATH{return FSX_RC_DENIED} 415 let lock: *u8=sys_mmap_shared(PB_PATH);let pos: *i64=sys_mmap_shared(8) as *i64 416 let normalized: *i64=sys_mmap_shared((PB_GUARDED_SIZED_ARGC+1)*8) as *i64 417 var rc: i64=FSX_RC_IO 418 if (lock as i64)>0&&(pos as i64)>0&&(normalized as i64)>0 { 419 pos[0]=0;pb_app(lock,pos,"sites/");pb_app(lock,pos,site);pb_app(lock,pos,"/");pb_app(lock,pos,rel) 420 if fsx_write_denied(lock)==0 { 421 pb_app(lock,pos,".publish-lock");let fd: i64=sys_openat_wr(lock,FSX_MODE_RW) 422 if fd>=0 { 423 if sys_flock(fd,PB_LOCK_EX_NB)==0 { 424 i=0;while i<PB_GUARDED_SIZED_ARGC{normalized[i]=argv[i];i=i+1};normalized[i]=0;normalized[1]="publish-guarded" as *u8 as i64 425 rc=pb_main_edge(PB_GUARDED_SIZED_ARGC,normalized,rel,argv[9] as *u8) 426 } else {rc=PB_RC_BUSY;fsx_puts("NX-PUBLISH BUSY selected-dependency-owned\n")} 427 if sys_close(fd)!=0{rc=FSX_RC_IO;fsx_puts("NX-PUBLISH IO dependency-lock-close; inspect placement outcome\n")} 428 } 429 } else {rc=FSX_RC_DENIED} 430 } 431 if (normalized as i64)>0{if sys_munmap(normalized as *u8,(PB_GUARDED_SIZED_ARGC+1)*8)!=0{rc=FSX_RC_IO}} 432 if (pos as i64)>0{if sys_munmap(pos as *u8,8)!=0{rc=FSX_RC_IO}} 433 if (lock as i64)>0{if sys_munmap(lock,PB_PATH)!=0{rc=FSX_RC_IO}} 434 return rc 435} 436func sp_main(argc: i64,argv: *i64) -> i64 { 437 if argc>1{if fsx_seq(argv[1] as *u8,"publish-guarded-edge")==1{return pe_main(argc,argv)}} 438 return pb_main_edge(argc,argv,0 as *u8,0 as *u8) 439} 440func pe_check(body: *u8,n: i64,argv: *i64,rel: *u8,sha: *u8) -> i64 { 441 let conf: *u8=sys_mmap_shared(PB_CONFCAP);let spans: *i64=sys_mmap_shared(PB_SPANCAP) as *i64 442 let prefix: *u8=sys_mmap_shared(PB_PATH);let path: *u8=sys_mmap_shared(PB_PATH);let token: *u8=sys_mmap_shared(PB_PATH) 443 let av: *i64=sys_mmap_shared(8*8) as *i64;let pos: *i64=sys_mmap_shared(8) as *i64 444 var rc: i64=FSX_RC_IO 445 if (conf as i64)>0&&(spans as i64)>0&&(prefix as i64)>0&&(path as i64)>0&&(token as i64)>0&&(av as i64)>0&&(pos as i64)>0 { 446 if pl_site_lookup("knowledge/pub_sites.conf",argv[3] as *u8,conf,spans)==1 { 447 if spans[3]-spans[2]<PB_PATH { 448 pl_span_cstr(conf,spans[2],spans[3],prefix) 449 let registered: i64=pr_check(prefix,rel,sha) 450 if registered==PR_OK { 451 pos[0]=0;pb_app(path,pos,"sites/");pb_app(path,pos,argv[3] as *u8);pb_app(path,pos,"/");pb_app(path,pos,rel) 452 pos[0]=0;pb_app(token,pos,"/");pb_app(token,pos,rel) 453 av[0]="nx_comparestale" as *u8 as i64;av[1]="dependency-check" as *u8 as i64;av[2]=argv[2];av[3]=argv[5];av[4]=path as i64;av[5]=sha as i64;av[6]=token as i64;av[7]=0 454 let observed: i64=cd_buffer_command(7,av,body,n) 455 if observed==0{rc=0}else{if observed==1{rc=PB_RC_CONFLICT}else{rc=FSX_RC_IO}} 456 } else {if registered==PR_IO{rc=FSX_RC_IO}else{rc=PB_RC_CONFLICT};fsx_puts("NX-PUBLISH REFUSED selected-dependency-registration\n")} 457 } 458 } else {rc=FSX_RC_DENIED;fsx_puts("NX-PUBLISH DENIED selected-edge requires registered site\n")} 459 } 460 if (pos as i64)>0{if sys_munmap(pos as *u8,8)!=0{rc=FSX_RC_IO}} 461 if (av as i64)>0{if sys_munmap(av as *u8,64)!=0{rc=FSX_RC_IO}} 462 if (token as i64)>0{if sys_munmap(token,PB_PATH)!=0{rc=FSX_RC_IO}} 463 if (path as i64)>0{if sys_munmap(path,PB_PATH)!=0{rc=FSX_RC_IO}} 464 if (prefix as i64)>0{if sys_munmap(prefix,PB_PATH)!=0{rc=FSX_RC_IO}} 465 if (spans as i64)>0{if sys_munmap(spans as *u8,PB_SPANCAP)!=0{rc=FSX_RC_IO}} 466 if (conf as i64)>0{if sys_munmap(conf,PB_CONFCAP)!=0{rc=FSX_RC_IO}} 467 return rc 468} 469 470// Compatibility entry: callers retain the original two-argument API. 471func pb_publish_locked(argc: i64,argv: *i64) -> i64 {return pb_publish_locked_edge(argc,argv,0 as *u8,0 as *u8)}