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}