nx_site_publish_prepare_t198.nx
buildroot/runtime/nx_site_publish_prepare_t198.nx
about
nx_site_publish_lib.nx -- ARTIFACT-PUBLISH, LIBRARY half (sp_main dispatcher) capability (closes the coverage BUILD-queue gap "artifact-publish";
retires the ssh-cat content lane for pages). Places a NAS-local file into the live sites docroot the
SAFE way, composing the proven IO layer:
publish <src-file> <site> <relpath> -> sites/<site>/<relpath>
- ATOMIC placement via fsx_write (tmp+fsync+rename -- a browser never sees a torn page)
- PRIOR VERSION PRESERVED: an existing target is first copied to <relpath>.prev (rename-not-delete
doctrine; one-deep history -- generated artifacts, git holds sources)
- DENY BY CONSTRUCTION (fsx_write_denied: secrets/device-ns/allowlist) PLUS path-traversal refusal:
any ".." segment or absolute site/relpath is DENIED (an MCP caller must not escape the docroot)
The organ prints the live URL; verification is the SEPARATE browser-grade organ (composition, not a
built-in fallback): run nx_page_verify <url> from a WAN vantage (on-NAS bare-domain fetches hit DSM --
the documented trap). The publish->verify recipe is the contract; this organ does the placement half.
- THE PUBLISH GATEWAY (F763 second half, wired 2026-07-30): for a site listed in
knowledge/pub_sites.conf, a <relpath> absent from that site's publishing REGISTRY -- or
carrying a non-publishable status (withdrawn/redirect/debris) -- is REFUSED. This is the
half that makes "random and arbitrary" impossible instead of merely visible: nx_pub_desk
stated the rule in 2026-07-19 and its own header recorded that enforcement inside the
publish organ was never wired, so anything could be dropped into any docroot and become
site truth. MEASURED before wiring: 54 pages served on nishifamily.com were in NO registry.
Sites NOT in the site table publish UNGATED (with a warning) -- onboarding is a config row,
and a control that refuses everything on day one is a control someone disables by lunchtime.
exit: 0 ok | 2 usage | 3 src absent | 4 io | 5 DENIED license_tier: ORIGINAL
dependencies 6 imports · 1 importers
imports: nx_fsops_lib.nxnx_pub_lib.nxnx_pub_release_lib.nxnx_sha256.nxnx_comparestale_lib.nxnx_json.nx
imported by: nx_site_prepare_gate_t198.nx
structs
| none |
consts
| 30 | const PB_ARG_VERB: i64 = 1 // argv: verb ("publish") |
| 31 | const PB_RC_USAGE: i64 = 2 // exit: usage |
| 32 | const PB_ARG_SRC: i64 = 2 // argv: source file (NAS-local) |
| 33 | const PB_ARG_SITE: i64 = 3 // argv: site dir under sites/ |
| 34 | const PB_ARG_REL: i64 = 4 // argv: page path under the site |
| 35 | const PB_ARGC: i64 = 5 // publish src site relpath |
| 36 | const PB_STAGE_ARGC: i64 = 6 // stage src site releases/<path> sha256 |
| 37 | const PB_GUARDED_ARGC: i64 = PB_STAGE_ARGC + 1 // adds expected live digest |
| 38 | const PB_GUARDED_SIZED_ARGC: i64 = PB_GUARDED_ARGC + 1 // optional exact artifact extent |
| 39 | const PB_PATH: i64 = 1024 // destination path buffer |
| 40 | const PB_CONFCAP: i64 = 65536 // site-table read buffer (the gateway) |
| 41 | const PB_SPANCAP: i64 = 128 // site-table span vector |
| 42 | const PB_DOT: i64 = 46 // '.' |
| 43 | const PB_SLASH: i64 = 47 // '/' |
| 90 | const PB_LOCK_EX_NB: i64 = 6 |
| 91 | const PB_RC_CONFLICT: i64 = 8 |
| 92 | const PB_RC_BUSY: i64 = 9 |
| 93 | const PB_SHA_BYTES: i64 = 32 |
| 94 | const PB_SHA_HEX: i64 = 64 |
| 110 | const PB_SIZED_ARGC: i64 = 7 |
| 111 | const PB_I64_MAX: i64 = 9223372036854775807 |
| 405 | const PE_ARGC: i64=10 |
| 483 | const RM_FIELDS: i64=12 |
| 484 | const RM_VALUES_BYTES: i64=RM_FIELDS*8 |
| 485 | const RM_SCHEMA: *u8="site-release/v1" |
| 486 | const RM_RECEIPT_ROOT: *u8="knowledge/gates/" |
functions
| 46 | func pb_traversal(s: *u8) -> i64 |
| 72 | func pb_app(out: *u8, op: *i64, s: *u8) -> i64 |
| 82 | func pb_same_bytes(a: *u8, an: i64, b: *u8, bn: i64) -> i64 called by 1: rm_bank |
| 95 | func pb_hash(body: *u8, n: i64, hex: *u8) -> i64 |
| 112 | func pb_sized_bytes(s: *u8) -> i64 called by 1: rm_artifact |
| 124 | func pb_read_bounded(path: *u8,b: *u8,cap: i64) -> i64 |
| 138 | func pb_main_edge(argc: i64, argv: *i64, edge_rel: *u8, edge_sha: *u8) -> i64 |
| 188 | func pb_release_path(rel: *u8) -> i64 |
| 197 | func pb_publish_locked_edge(argc: i64, argv: *i64, edge_rel: *u8, edge_sha: *u8) -> i64 |
| 407 | func pe_main(argc: i64,argv: *i64) -> i64 |
| 437 | func sp_main(argc: i64,argv: *i64) -> i64 |
| 448 | func pe_check(body: *u8,n: i64,argv: *i64,rel: *u8,sha: *u8) -> i64 |
| 479 | func pb_publish_locked(argc: i64,argv: *i64) -> i64 {return pb_publish_locked_edge(argc,argv,0 as *u8,0 as *u8)} |
| 487 | func rm_key(k: *u8) -> i64 |
| 497 | func rm_string(t: *NxJsonTok) -> *u8 |
| 506 | func rm_parse_tokens(t: *NxJsonTok,v: *i64) -> i64 |
| 522 | func rm_parse(body: *u8,n: i64,v: *i64) -> i64 |
| 527 | func rm_num(out: *u8,pos: *i64,value: i64) -> i64 |
| 541 | func rm_receipt(path: *u8,digest: *u8,phase: *u8,state: *u8,rc: i64) -> i64 |
| 556 | func rm_finish(receipt: *u8,digest: *u8,phase: *u8,rc: i64) -> i64 |
| 562 | func rm_artifact(src: *u8,size: *u8,hash: *u8,out: *i64) -> i64 |
| 574 | func rm_preflight(v: *i64,prefix: *u8) -> i64 |
| 605 | func rm_execute(v: *i64,prefix: *u8,receipt: *u8,digest: *u8) -> i64 |
| 624 | func rm_main(argc: i64,argv: *i64) -> i64 |
| 644 | func rm_sync(path: *u8) -> i64 |
| 653 | func rm_bank(body: *u8,n: i64,digest: *u8) -> i64 |
| 671 | func rm_cleanup(body: *u8,digest: *u8,v: *i64,receipt: *u8,pos: *i64,prefix: *u8) -> i64 |
| 680 | func pb_prepare_path(site:*u8,rel:*u8)->i64 |