code wiki / _hdl_build / nx_survey_daemon.nx

nx_survey_daemon.nx

buildroot/runtime/_hdl_build/nx_survey_daemon.nx

12556 B217 linesdepth 13pulls 22 transitivereach 0 importersview sourcekind servicetopic survey
docsdependenciesstructsconstsfunctions

about

nx_survey_daemon.nx -- the DEPLOYABLE Nishi Pulse survey/poll daemon (ops shell around the gated pure core nx_survey_serve). Serves 0.0.0.0:8031; CWD = nishihost on the NAS. Mounted at nishifamily.com/survey via a proxy_routes.conf row (the /office precedent). Store = seg_store prefix knowledge/survey/pulse_ (self-healed on startup: the relate-daemon lesson -- root-owned knowledge/ may lack the subdir). Admin lane reads survey_admin.key from CWD per request (FAIL-CLOSED when absent). Build with --build-only; run deliberately. license_tier: ORIGINAL ---- TRAFFIC-SAFETY ADOPTION 2026-08-21: THIS DAEMON IS THE FIRST ADOPTER OF TS1 AND TS2. ---- /compare/trafficsafety measured the deficit as ADOPTION, not capability: ts_handoff_nodrop was proven on 2026-08-21 and NO serving daemon called it, and rt_sigaction resolved 8 times with ZERO of them a TERM drain. This file closes both for one real, low-blast-radius serving daemon. WHY THIS DAEMON WAS CHOSEN, from the route table rather than from taste. proxy_routes.conf carries exactly ONE row for this backend (nishifamily.com /survey 8031 buffered) against 69 rows total, it owns its binary outright (unlike nx_office_daemon, where one binary serves two registry rows and a deploy restarts both), and it fronts no control-plane or authentication path. It is deliberately NOT sites.elf: that front door is what all 69 rows cross and replacing it is rung TS3's problem, not a first adopter's. (1) TS1 -- KEEP THE SOCKET, REPLACE THE PROCESS. The listener now comes from ts_handoff_nodrop instead of a private bind. When an owner outside this process is publishing on the rendezvous path, the listening socket is INHERITED and never released, so a restart has no instant at which zero processes hold the port and an arrival mid-swap queues instead of being refused. When no owner is publishing it binds normally, so adopting this CANNOT make a cold start fail -- that fallback is the whole reason this is safe to land on a live daemon. IT ALSO REMOVES TWO LATENT HAZARDS THIS FILE CARRIED, because the shared primitive owns them: the old hand-rolled bind set SO_REUSEADDR but NOT FD_CLOEXEC (the port-hostage class that took the mgmt API down for every seat) and never ignored SIGPIPE (a client walking away mid-response takes SIGPIPE's default action, TERMINATE). nx_http_server_listen does all three by construction. (2) ENVOY'S ORDERING RULE, WHICH IS THE DIRECT FIX FOR OUR OWN 8-MINUTE BLACKOUT. Every expensive step -- the three request/response arenas and the seg-store warm, which reads real store files off a shared array -- now happens BEFORE the listener is asked for, while the OUTGOING generation is still serving. The blackout happened because a registry seed holding a process-lifetime lock ran BEFORE the bind, so the incoming instance never reached accept() at all: a handoff placed after a blocking call that the outgoing process gates can never run. Ordering is the entire mitigation and it is free. the ordering is load-bearing, not cosmetic: moving the warm back below the acquire re-creates the exact defect, because the warm's cost is set by the busiest thing on the box and not by this code. (3) TS2 -- COOPERATIVE DRAIN ON TERM. Armed FIRST, before the listener exists, because a daemon that arms the drain only once it is serving has a window in which TERM still kills it outright, and that

dependencies 3 imports · 0 importers

nx_survey_serve.nx nx_http_server.nx nx_ts_drain_lib.nx nx_survey_daemon.nx

imports: nx_survey_serve.nxnx_http_server.nxnx_ts_drain_lib.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main ts_drain_on_term sys_mmap nxa_die sys_write sys_exit nxa_lock_take nxa_lock_addr sys_write ↻ nxa_lock_give nxa_lock_addr ↻ nxa_report_overrun sys_write ↻ nxa_dump_printable sys_write ↻ nxa_dump_sizes sys_write ↻ ts_drain_deadline_s sys_mmap ↻ sys_read_file sys_openat_rd sys_lseek sys_mmap ↻ sys_read sys_munmap sys_close rm_conf rm_field rm_slen sv2_p sd_putn ssc_init sys_mmap ↻ ss_open_cached ssc_init ↻ ssc_sig_of ss_cat sys_fstatat ssc_streq ss_open

structs

none

consts

54const SD_PORT: i64 = 0x1f5f // 8031
55const SD_ADDR_BYTES: i64 = 16
56const SD_MODE_0755: i64 = 493
57const SD_REQ_CAP: i64 = 262144
58const SD_RES_CAP: i64 = 1048576
59const SD_BACKLOG: i64 = 16
60const SD_OUT_BYTES: i64 = 64
61const SD_NUM_SCRATCH: i64 = 24
62const SD_ASCII_0: i64 = 48
63const SD_ASCII_9: i64 = 57
64const SD_B10: i64 = 10
65const SD_ARGC_PORT: i64 = 2
66const SD_ARGC_SOCK: i64 = 3
67const SD_ARGV_PORT: i64 = 1
68const SD_ARGV_SOCK: i64 = 2
72const SD_SA_FAMILY_INET: i64 = 2
73const SD_SA_PORT_OFF: i64 = 2
74const SD_BITS_PER_BYTE: i64 = 8
75const SD_BYTE_MASK: i64 = 0xff
79const SD_HANDOFF_SOCK: *u8 = "knowledge/status/survey_handoff.sock" as *u8

functions

81func sd_addr(out: *u8, port: i64) -> i64
called by 1: main
90func sd_putn(v: i64) -> i64
called by 1: main
102func main(argc: i64, argv: **u8) -> i64