code wiki / _hdl_build / nx_fence.nx
nx_fence.nx
buildroot/runtime/_hdl_build/nx_fence.nx
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
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
structs
| none |
consts
| 48 | const FC_MAGIC_1024: i64 = 1024 |
| 50 | const FC_WIN: i64 = 4194304 |
| 51 | const FC_OUT: i64 = 8192 |
| 52 | const FC_SPAN: i64 = 64 |
| 53 | const FC_TAB: i64 = 9 |
| 54 | const FC_NL: i64 = 10 |
| 55 | const FC_D0: i64 = 48 |
| 56 | const FC_D9: i64 = 57 |
| 57 | const FC_EXIT_USAGE: i64 = 2 |
| 58 | const FC_EXIT_REFUSE: i64 = 3 |
| 59 | const FC_EXIT_IO: i64 = 4 |
| 60 | const FC_HOLDER_KEYLEN: i64 = 7 |
| 63 | const FC_ALLOW: i64 = 0 |
| 64 | const FC_R_UNREADABLE: i64 = 1 |
| 65 | const FC_R_BADFENCE: i64 = 2 |
| 66 | const FC_R_NOTHELD: i64 = 3 |
| 67 | const FC_R_NOTHOLDER: i64 = 4 |
| 68 | const FC_R_STALE: i64 = 5 |
| 69 | const FC_R_CLAIMSFAIL: i64 = 6 |
functions
| 74 | func fc_col(b: *u8, ls: i64, le: i64, k: i64, out: *i64) -> i64 called by 1: fc_count |
| 97 | func fc_span_eq(b: *u8, s: i64, e: i64, z: *u8) -> i64 |
| 112 | func fc_count(b: *u8, n: i64, res: *u8, claimlit: *u8) -> i64 |
| 132 | func fc_atoi_strict(s: *u8) -> i64 |
| 150 | func fc_tail_eq(buf: *u8, n: i64, from: i64, z: *u8) -> i64 |
| 166 | func fc_decide(readable: i64, claims_rc: i64, held: i64, holder_ok: i64, want: i64, cur: i64) -> i64 |
| 176 | func fc_reason_name(c: i64) -> *u8 called by 1: fc_refuse |
| 189 | func fc_claims_state(journal: *u8, res: *u8, outbuf: *u8, outlen: *i64) -> i64 |
| 198 | func fc_selftest(scratch: *u8) -> i64 |
| 257 | func fc_refuse(code: i64, res: *u8, actor: *u8, want: i64, cur: i64) -> i64 |
| 272 | func main(argc: i64, argv: *i64) -> i64 |