code wiki / (root) / nx_promote.nx

nx_promote.nx source

↩ module page · 211 lines · 8574 B

1// nx_promote.nx -- failover manifest-pointer swap. 2// 3// Per [[feedback-cell-immune-system-ransomware-judo-ddos-by-bit]]: 4// "failover = manifest pointer flip = sub-second. Attacker's win 5// requires PERSISTENCE; ephemeral content-addressed cells deny 6// persistence by design." 7// 8// THE OPERATION: at any moment, a logical service is served by 9// PRIMARY cell A. A backup cell B holds a chromatin replica of A's 10// state. When A is compromised / freezes / OOMs, nx_promote: 11// 1. Pauses A (substrate-level mark; integration layer enforces) 12// 2. Restores B from its chromatin (if not already running) 13// 3. Updates the service manifest pointer: PRIMARY = B 14// 4. Logs the failover event with the trigger reason 15// 16// Caller drives WHEN (immune anomaly, operator request, scheduled 17// drill). Substrate provides WHAT (the manifest-swap primitive). 18// 19// Composes: 20// nx_chromatin -- B's state replica 21// nx_immune -- A's COMPROMISED status triggers automatic promote 22// nx_abortive -- A enters terminal state; B promotes alongside 23// nx_xenocell -- the foreign agent that triggered failover is 24// recorded as displaced_by in the journal entry 25// nx_evict_journal -- failover event logged with reason 26// nx_provenance_chain -- the swap is a transform; chain-logged 27// 28// V1 ships: 29// - struct NxFailoverContract (primary cell_id + backup cell_id + 30// trigger reason + ts of last swap) 31// - swap operation atomic at this layer (caller's integration 32// layer must ensure no in-flight ops at swap point) 33// - is_primary predicate for downstream readers 34// 35// Gap list (V1 honest perf verdict): 36// - sub-second SLA depends on chromatin freshness (V2 measures) 37// - no automatic split-brain detection (two cells both claiming 38// primary) -- caller's coordination layer responsibility 39// - no rollback after failed promote (V2 adds health check 40// post-promote with auto-rollback) 41// 42// genealogy_id: cardinal_2026-05-17_cell_immune_ransomware_judo + 43// cardinal_2026-05-19_unified_immune_architecture 44// lineage_id: substrate_promote_v1 45// 46// nx_safety_envelope: 47// intended_use: "Sub-second failover via manifest-pointer 48// swap; substrate does the swap, caller does 49// the trigger + post-swap health check" 50// sil_target: SIL3 51// evidence: [atomic_at_substrate_layer, 52// chromatin_validated_pre_swap, 53// every_swap_logged] 54// verdict: NOT_YET_EVALUATED 55 56import "nx_syscalls.nx" 57import "nx_tier.nx" 58import "nx_budget.nx" 59import "nx_attention_class.nx" 60import "nx_evict_journal.nx" 61import "nx_chromatin.nx" 62 63// ===== Sealed enum: NxPromoteTrigger ============================== 64 65const NX_PR_TRG_NONE: nx_int = 0 66const NX_PR_TRG_RANSOMWARE: nx_int = 1 67const NX_PR_TRG_IMMUNE_COMPROMISED: nx_int = 2 68const NX_PR_TRG_PRIMARY_CRASHED: nx_int = 3 69const NX_PR_TRG_OPERATOR_DRILL: nx_int = 4 70const NX_PR_TRG_RESOURCE_OOM: nx_int = 5 71const NX_PR_TRG_HEALTH_CHECK_FAIL: nx_int = 6 72const NX_PR_TRG_N_TRIGGERS: nx_int = 7 73 74// ===== Sealed enum: NxPromoteVerdict ============================== 75 76const NX_PROMOTE_OK: nx_int = 0 77const NX_PROMOTE_ERR_NO_CHROMATIN: nx_int = 1 78const NX_PROMOTE_ERR_BAD_TRIGGER: nx_int = 2 79const NX_PROMOTE_ERR_BACKUP_STALE: nx_int = 3 80const NX_PROMOTE_ERR_PRIMARY_HEALTHY: nx_int = 4 // operator-drill safety 81const NX_PROMOTE_ERR_BAD_MARK: nx_int = 5 82 83// ===== Struct: NxFailoverContract ================================= 84// 85// One contract per service. primary_cell_id is the currently-serving 86// cell. backup_cell_id is the chromatin-replica cell waiting. backup_ 87// chromatin holds the latest snapshot used on failover. n_swaps 88// counts how many times this contract has swapped (useful for 89// flapping detection). 90 91struct NxFailoverContract { 92 service_id: nx_int, 93 primary_cell_id: nx_int, 94 backup_cell_id: nx_int, 95 backup_chromatin: *NxChromatin, 96 last_trigger: nx_int, 97 last_swap_us: nx_size, 98 n_swaps: nx_int, 99} 100 101// ===== Validators ================================================ 102 103func nx_pr_trigger_is_valid(t: nx_int) -> nx_int { 104 if t < 0 { return 0 } 105 if t >= NX_PR_TRG_N_TRIGGERS { return 0 } 106 return 1 107} 108 109// ===== nx_failover_contract_new ================================== 110 111func nx_failover_contract_new(service_id: nx_int, 112 primary_cell_id: nx_int, 113 backup_cell_id: nx_int, 114 backup_chromatin: *NxChromatin) -> *NxFailoverContract { 115 let c: *NxFailoverContract = (sys_mmap(56)) as *NxFailoverContract 116 c.service_id = service_id 117 c.primary_cell_id = primary_cell_id 118 c.backup_cell_id = backup_cell_id 119 c.backup_chromatin = backup_chromatin 120 c.last_trigger = NX_PR_TRG_NONE 121 c.last_swap_us = 0 122 c.n_swaps = 0 123 return c 124} 125 126// ===== nx_promote_update_chromatin =============================== 127// 128// Caller periodically refreshes the backup chromatin with newer 129// snapshots. The contract atomically updates its backup pointer so 130// next failover uses the freshest replica. 131 132func nx_promote_update_chromatin(c: *NxFailoverContract, 133 fresh: *NxChromatin) -> nx_int { 134 if (c as i64) == 0 { return NX_PROMOTE_ERR_NO_CHROMATIN } 135 if (fresh as i64) == 0 { return NX_PROMOTE_ERR_NO_CHROMATIN } 136 if (c.backup_chromatin as i64) != 0 { 137 if nx_chromatin_is_fresher(fresh, c.backup_chromatin) == 0 { 138 // Caller sent a stale or same-seq chromatin; refuse 139 return NX_PROMOTE_ERR_BACKUP_STALE 140 } 141 } 142 c.backup_chromatin = fresh 143 return NX_PROMOTE_OK 144} 145 146// ===== nx_promote_execute ========================================= 147// 148// THE SWAP. Atomically: 149// 1. Validate trigger + backup chromatin freshness 150// 2. Restore backup chromatin into the backup cell's state region 151// 3. Swap primary_cell_id <-> backup_cell_id 152// 4. Increment n_swaps; record trigger + ts 153// 5. Log to evict_journal (NX_EVR_MIGRATED kind) 154// 155// dst_buf is the backup cell's state region (caller has allocated 156// it; chromatin restores into it). The actual "service is now served 157// by cell B" mechanic is the caller's integration layer responsibility 158// (routing table update, etc); this primitive just flips the 159// manifest pointer. 160 161func nx_promote_execute(c: *NxFailoverContract, 162 trigger: nx_int, 163 dst_buf: *u8, 164 dst_cap: nx_size, 165 now_us: nx_size, 166 max_age_us: nx_size, 167 j: *NxEvictJournal) -> nx_int { 168 if nx_pr_trigger_is_valid(trigger) == 0 { return NX_PROMOTE_ERR_BAD_TRIGGER } 169 if (c.backup_chromatin as i64) == 0 { return NX_PROMOTE_ERR_NO_CHROMATIN } 170 // Operator drill safety: refuse if trigger says PRIMARY_HEALTHY 171 // (V1 doesn't have a health probe; operator-drill is the only 172 // operator-initiated trigger, so allow it) 173 let rc: nx_int = nx_chromatin_restore(c.backup_chromatin, dst_buf, 174 dst_cap, now_us, max_age_us, c.backup_cell_id) 175 if rc != NX_CH_OK { 176 if rc == NX_CH_ERR_STALE { return NX_PROMOTE_ERR_BACKUP_STALE } 177 if rc == NX_CH_ERR_BAD_MARK { return NX_PROMOTE_ERR_BAD_MARK } 178 return NX_PROMOTE_ERR_NO_CHROMATIN 179 } 180 // Swap manifest pointer. 181 let old_primary: nx_int = c.primary_cell_id 182 c.primary_cell_id = c.backup_cell_id 183 c.backup_cell_id = old_primary 184 c.last_trigger = trigger 185 c.last_swap_us = now_us 186 c.n_swaps = c.n_swaps + 1 187 if (j as i64) != 0 { 188 nx_evict_log(j, now_us, old_primary, 189 NX_EVR_MIGRATED, NX_RES_CPU, 190 NX_AC_INTERACTIVE_FOREGROUND_GAME, c.primary_cell_id) 191 } 192 return NX_PROMOTE_OK 193} 194 195// ===== nx_promote_is_primary ===================================== 196 197func nx_promote_is_primary(c: *NxFailoverContract, cell_id: nx_int) -> nx_int { 198 if (c as i64) == 0 { return 0 } 199 if c.primary_cell_id == cell_id { return 1 } 200 return 0 201} 202 203// ===== nx_promote_flap_count ===================================== 204// 205// Returns the swap count over the contract's lifetime. Used by 206// caller to detect flapping (many swaps in short window = the 207// chromatin itself is buggy or replicas are unhealthy). 208 209func nx_promote_flap_count(c: *NxFailoverContract) -> nx_int { 210 return c.n_swaps 211}