code wiki / (root) / nx_netscope_heal.nx

nx_netscope_heal.nx source

↩ module page · 157 lines · 6857 B

1// nx_netscope_heal.nx -- NX-NETSCOPE rung 3: goal-directed SELF-HEALING 2// connectivity agent (DECISION CORE). The north-star payload: a device 3// given a goal ("reach nishifamily.com") auto-selects the BEST reachable 4// resolver/path, switches/fails over, and re-verifies -- without flapping. 5// 6// This is the PURE, DETERMINISTIC decision core (no I/O), isolated so it 7// is KAT-able offline (fault-inject a synthetic probe matrix -> assert the 8// chosen resolver + heal action). The live MAPE-K loop (monitor tick, 9// EXECUTE set-resolver, backoff+jitter sleep, circuit breaker) wraps this 10// next increment. Design grounded (research workflow wf_ca778575) in: 11// - Happy Eyeballs v2/v3 (RFC 8305/8306): RACE candidates, pick best -- 12// but "best" = lowest RTT among CORRECT answers (a fast WRONG answer, 13// NXDOMAIN/SERVFAIL/timeout, never wins over a slower correct one). 14// - MAPE-K autonomic loop (monitor->analyze->plan->execute->verify). 15// - Hysteresis / RTT-banding (RFC 2439 RFD spirit): don't switch for a 16// marginal gain -> no flapping. 17// 18// Reuses nxv_cell + ProbeCell + NXNS_* from the shipped probe/verdict 19// layers; composes nx_dns_probe_matrix_parallel results (no new deps). 20// 21// license_tier: ORIGINAL 22 23import "nx_netscope_verdict.nx" 24 25// ---- heal actions + reasons (sealed) ---- 26const HEAL_KEEP: i64 = 0 27const HEAL_SWITCH: i64 = 1 28const HEAL_GIVEUP: i64 = 2 29 30const HEAL_R_HEALTHY: i64 = 0 // incumbent still best -> no change 31const HEAL_R_INCUMBENT_FAILED: i64 = 1 // incumbent no longer correct -> fail over 32const HEAL_R_BETTER_FOUND: i64 = 2 // a meaningfully faster correct resolver 33const HEAL_R_NO_CANDIDATE: i64 = 3 // nothing correct reachable -> give up safely 34 35// hysteresis band: only switch for a *better* incumbent if the gain 36// exceeds this (anti-flap). Config key heal.rtt_band_ms (CLAUDE.md ยง11). 37const HEAL_BAND_MS: i64 = 20 38 39// HealDecision: 5 i64 = 40 bytes. 40struct HealDecision { 41 action: i64, // HEAL_KEEP / SWITCH / GIVEUP 42 target_idx: i64, // chosen resolver index (into servers[]), -1 if give-up 43 target_ip: i64, // the resolved answer ip (BE low32), 0 if none 44 target_rtt: i64, // chosen resolver's latency_ms 45 reason: i64, // HEAL_R_* 46} 47const HEAL_DECISION_BYTES: i64 = 40 48 49// A cell is ELIGIBLE iff it returned a CORRECT answer: OK_A, and -- for an 50// owned name that must live on the LAN -- a private (RFC1918) address (a 51// public answer to an owned name is a hairpin = wrong path, demoted). 52func heal_cell_eligible(cell: *ProbeCell, require_private: i64) -> i64 { 53 if cell.verdict != NXNS_OK_A { return 0 } 54 if require_private == 1 { if cell.is_rfc1918 != 1 { return 0 } } 55 return 1 56} 57 58// heal_select_best: the Happy-Eyeballs correctness-gated race winner -- 59// lowest RTT among ELIGIBLE (correct) resolvers. Deterministic: on an RTT 60// tie the lowest index wins (strict `<`). Returns server index or -1. 61func heal_select_best(cells: *ProbeCell, ns: i64, nh: i64, col: i64, require_private: i64) -> i64 { 62 var best: i64 = 0 - 1 63 var best_rtt: i64 = 0 64 var s: i64 = 0 65 while s < ns { 66 let cell: *ProbeCell = nxv_cell(cells, nh, s, col) 67 if heal_cell_eligible(cell, require_private) == 1 { 68 if best == (0 - 1) { 69 best = s 70 best_rtt = cell.latency_ms 71 } else { 72 if cell.latency_ms < best_rtt { best = s; best_rtt = cell.latency_ms } 73 } 74 } 75 s = s + 1 76 } 77 return best 78} 79 80// heal_decide: the PLAN step. Given the frozen probe matrix, the current 81// incumbent resolver, and the goal's privacy requirement, decide KEEP / 82// SWITCH / GIVEUP with a reason. Pure + deterministic -> KAT-able. 83func heal_decide(cells: *ProbeCell, ns: i64, nh: i64, col: i64, 84 require_private: i64, incumbent_idx: i64, band_ms: i64, 85 out: *HealDecision) -> i64 { 86 out.action = HEAL_GIVEUP 87 out.target_idx = 0 - 1 88 out.target_ip = 0 89 out.target_rtt = 0 90 out.reason = HEAL_R_NO_CANDIDATE 91 92 let best: i64 = heal_select_best(cells, ns, nh, col, require_private) 93 if best == (0 - 1) { return HEAL_GIVEUP } // nothing correct -> safe give-up 94 let bcell: *ProbeCell = nxv_cell(cells, nh, best, col) 95 96 // is the incumbent still a CORRECT answer? 97 var inc_ok: i64 = 0 98 if incumbent_idx >= 0 { 99 if incumbent_idx < ns { 100 let icell: *ProbeCell = nxv_cell(cells, nh, incumbent_idx, col) 101 if heal_cell_eligible(icell, require_private) == 1 { inc_ok = 1 } 102 } 103 } 104 105 if inc_ok == 0 { 106 // incumbent failed (or none set) -> fail over to the best correct one 107 out.action = HEAL_SWITCH 108 out.target_idx = best 109 out.target_ip = bcell.ipv4_packed 110 out.target_rtt = bcell.latency_ms 111 out.reason = HEAL_R_INCUMBENT_FAILED 112 return HEAL_SWITCH 113 } 114 115 // incumbent still correct: only switch for a MEANINGFUL gain (hysteresis). 116 let icell2: *ProbeCell = nxv_cell(cells, nh, incumbent_idx, col) 117 if best != incumbent_idx { 118 if (icell2.latency_ms - bcell.latency_ms) > band_ms { 119 out.action = HEAL_SWITCH 120 out.target_idx = best 121 out.target_ip = bcell.ipv4_packed 122 out.target_rtt = bcell.latency_ms 123 out.reason = HEAL_R_BETTER_FOUND 124 return HEAL_SWITCH 125 } 126 } 127 out.action = HEAL_KEEP 128 out.target_idx = incumbent_idx 129 out.target_ip = icell2.ipv4_packed 130 out.target_rtt = icell2.latency_ms 131 out.reason = HEAL_R_HEALTHY 132 return HEAL_KEEP 133} 134 135// heal_render: easy-for-anyone plain-language heal ACTION line. 136func heal_render(out: *HealDecision, servers: *i64, host_name: *u8, host_len: i64) -> i64 { 137 nxv_w("NX-NETSCOPE . SELF-HEAL ", 26); sys_write(1, host_name, host_len); nxv_w("\n", 1) 138 if out.action == HEAL_KEEP { 139 nxv_w(" [OK] HEALTHY -- using ", 24); nxv_ip(servers[out.target_idx]) 140 nxv_w(" (", 2); nxv_dec(out.target_rtt); nxv_w("ms), no change needed.\n", 23) 141 return 0 142 } 143 if out.action == HEAL_SWITCH { 144 if out.reason == HEAL_R_INCUMBENT_FAILED { 145 nxv_w(" [!!] HEALED -- your main resolver was failing; switched to ", 60) 146 nxv_ip(servers[out.target_idx]); nxv_w(" -> ", 4); nxv_ip(out.target_ip) 147 nxv_w(" (", 2); nxv_dec(out.target_rtt); nxv_w("ms). Connection restored.\n", 26) 148 return 0 149 } 150 nxv_w(" [OK] OPTIMIZED -- found a faster correct resolver ", 51) 151 nxv_ip(servers[out.target_idx]); nxv_w(" (", 2); nxv_dec(out.target_rtt); nxv_w("ms).\n", 5) 152 return 0 153 } 154 nxv_w(" [XX] CANNOT HEAL -- no resolver returns a correct answer.\n", 59) 155 nxv_w(" FIX: check this device's link, or add a working resolver.\n", 64) 156 return 0 157}