code wiki / _hdl_build / nx_fence.nx

nx_fence.nx

buildroot/runtime/_hdl_build/nx_fence.nx

20309 B452 linesdepth 3pulls 4 transitivereach 0 importersview sourcekind tool
docsdependenciesstructsconstsfunctions

about

nx_fence.nx -- THE MONOTONIC FENCE TOKEN for the claims plane (hive mechanics, 2026-08-09). WHY THIS EXISTS -- measured, not assumed ---------------------------------------- 2026-08-08, corpus_complete=1 over 22,903 files: ONLY nx_claims and nx_seat reference knowledge/status/claims.jrnl. ZERO mutation paths consult it -- not nx_fs_write, not /api/promote, not /api/deploy, not any plane appender. The estate's own record agrees: "/api/deploy, /api/promote, /api/restart are NOT yet lease-gated -- two sessions can still race a DEPLOY." **** THE CLAIMS PLANE IS A WHITEBOARD, NOT A LOCK: IT CORRECTLY REFUSES A STALE *BEAT*, AND THEN NOTHING STOPS THE SAME STALE SEAT FROM WRITING STRAIGHT TO PRODUCTION. The 2026 distributed-systems answer is the FENCING TOKEN (Kleppmann; etcd's per-key creation revision; Raft terms): a monotonically increasing number issued at each acquisition, which the RESOURCE persists and uses to reject any operation carrying a lower one. The cardinal rule is **** NEVER TRUST A PROCESS TO KNOW THAT ITS OWN LOCK HAS EXPIRED -- so the check must live at the resource, never in the lock holder. nx_lease already carries a `nonce` and it is NOT a fence: a pid-derived tiebreaker (LS_NONCE_MOD 1000000) answering WHO WON A RACE, not WHO IS CURRENT. It has no ordering, so a resource cannot reject a lower one. **** A TIEBREAKER IS NOT A FENCE. THE TOKEN, derived never stored (the claims plane's cardinal law): fence(resource) = number of CLAIM frames for that resource in the append-only journal. Monotonic BY CONSTRUCTION (appends only), no new state, no daemon, and NO FRAME-FORMAT CHANGE (rule 19 additive) -- every existing reader is unaffected. HOLDERSHIP STAYS ONE COPY: state/holder come from FORKING `nx_claims state`, never re-derived here (the nx_seat precedent -- its derived-holdership parser stays THE one copy). This organ owns exactly one new thing: the count. !! FC_CLAIMS_ELF is a DEPLOYMENT ASSUMPTION (a path constant). Named out loud so it is findable: A PATH CONSTANT IS A DEPLOYMENT ASSUMPTION, and the estate has shipped silent ones before. nx_fence token <journal> <resource> -> fence=<N> + nx_claims state line; exit 0 nx_fence verify <journal> <resource> <actor> <fence> -> exit 0 ALLOW | 3 REFUSE reason=<R> nx_fence selftest [scratch-dir] -> gate teeth, verdict carried by exit code FAIL DIRECTION: verify REFUSES anything it cannot PROVE (unreadable journal, non-numeric fence, fork failure, missing holder field). **** A GUARD THAT GATES A DESTRUCTIVE ACTION MUST BE WRONG IN THE DIRECTION OF DOING NOTHING. It ships with a POSITIVE CONTROL because a guard that refuses everything passes every negative test -- four SSRF deny-tests once went green over a broken guard.

dependencies 3 imports · 0 importers

nx_syscalls.nx nx_gatekit_lib.nx nx_gate_verdict.nx nx_fence.nx

imports: nx_syscalls.nxnx_gatekit_lib.nxnx_gate_verdict.nx

imported by: nobody (leaf or entry point)

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

main gv_puts sys_write sys_exit gk_streq fc_selftest 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 ↻ gk_head sys_mmap ↻ gk_cat gk_say gk_write_all sys_write ↻ gk_join gk_rm sys_unlinkat gk_write sys_openat_wr gk_len gk_write_all ↻ sys_close gk_read sys_openat_rd sys_read sys_close ↻ gk_ok

structs

none

consts

48const FC_MAGIC_1024: i64 = 1024
50const FC_WIN: i64 = 4194304
51const FC_OUT: i64 = 8192
52const FC_SPAN: i64 = 64
53const FC_TAB: i64 = 9
54const FC_NL: i64 = 10
55const FC_D0: i64 = 48
56const FC_D9: i64 = 57
57const FC_EXIT_USAGE: i64 = 2
58const FC_EXIT_REFUSE: i64 = 3
59const FC_EXIT_IO: i64 = 4
60const FC_HOLDER_KEYLEN: i64 = 7
63const FC_ALLOW: i64 = 0
64const FC_R_UNREADABLE: i64 = 1
65const FC_R_BADFENCE: i64 = 2
66const FC_R_NOTHELD: i64 = 3
67const FC_R_NOTHOLDER: i64 = 4
68const FC_R_STALE: i64 = 5
69const FC_R_CLAIMSFAIL: i64 = 6

functions

74func fc_col(b: *u8, ls: i64, le: i64, k: i64, out: *i64) -> i64
called by 1: fc_count
97func fc_span_eq(b: *u8, s: i64, e: i64, z: *u8) -> i64
called by 1: fc_count calls 1: gk_len
112func fc_count(b: *u8, n: i64, res: *u8, claimlit: *u8) -> i64
132func fc_atoi_strict(s: *u8) -> i64
called by 2: fc_selftestmain calls 1: gk_len
150func fc_tail_eq(buf: *u8, n: i64, from: i64, z: *u8) -> i64
called by 2: fc_selftestmain calls 1: gk_len
166func fc_decide(readable: i64, claims_rc: i64, held: i64, holder_ok: i64, want: i64, cur: i64) -> i64
called by 2: fc_selftestmain
176func fc_reason_name(c: i64) -> *u8
called by 1: fc_refuse
189func fc_claims_state(journal: *u8, res: *u8, outbuf: *u8, outlen: *i64) -> i64
called by 1: main calls 1: gk_run_capture
198func fc_selftest(scratch: *u8) -> i64
257func fc_refuse(code: i64, res: *u8, actor: *u8, want: i64, cur: i64) -> i64
called by 1: main calls 3: gv_putsfc_reason_namegv_num
272func main(argc: i64, argv: *i64) -> i64