nx_pollinate.nx source
↩ module page · 338 lines · 13587 B
1// nx_pollinate.nx -- peer-mesh community defense distribution.
2//
3// Biology: bees cross-flower pollination spreads genetic material
4// across a community of flowers, so a useful trait that arose in one
5// plant becomes available to many. Nishi pollinate is the cross-host
6// distribution of useful DEFENSIVE material: federation-ready
7// antibodies (per [[feedback-unified-immune-architecture-three-tier]]
8// item "antibody V2 federates via peer mesh"), decoy tripwire tokens
9// (per nx_decoy V2 cross-host registry), and signed xenocell
10// observations (the Citizen Lab analog).
11//
12// Per [[feedback-captain-moroni-doctrine]]: defensive only. We share
13// DETECTION SIGNATURES not exploitation payloads. We share OBSERVED
14// ATTACKS not retaliation plans. Captain Moroni's discipline: spread
15// the warning so other operators can defend, never spread the weapon.
16//
17// Per [[feedback-cell-immune-system-ransomware-judo-ddos-by-bit]]:
18// the judo is of INFORMATION. Vendor monitoring 1000 hosts becomes
19// 1000 hosts holding 1000 signed records of vendor monitoring. The
20// pollinate primitive scales that judo across the community.
21//
22// Per [[feedback-privacy-by-default-no-tracking]]: pollinate is
23// PER-CALL CONSENT (matches existing nx_trace_consent in the bit-
24// traceability cardinal). No automatic distribution; operator
25// explicitly approves each share. V2 adds policy-driven auto-share
26// for high-confidence federation-ready material.
27//
28// Payload kinds shipped V1:
29// ANTIBODY -- federation-ready detection signature
30// DECOY_TOKEN -- "this token was planted in my host; flag if
31// it shows up at yours"
32// XENO_OBSERVATION -- signed observation of vendor / state-actor
33// behavior; community-knowledge-of-record
34// IMMUNE_ALERT -- "host X is COMPROMISED; consider your peer
35// session with it suspect"
36// RECLAIM_REPORT -- "this commodity hardware reclaimed safely
37// with these mitigations; others can apply"
38//
39// Composes:
40// nx_antibody -- federation-ready antibodies are V1 payload
41// nx_decoy -- tripwire-token registry crosses hosts
42// nx_xenocell -- signed observations distribute via pollinate
43// nx_immune -- IMMUNE_ALERT propagates colony-status to peers
44// nx_reclaim -- reclamation reports help community apply
45// proven mitigations on similar hardware
46// nx_evict_journal -- every pollination event logged (no silent share)
47//
48// V1 ships:
49// - struct NxPollinationPayload (kind + content_hash + body_len +
50// body_ptr + signature + ts)
51// - send/receive verbs (caller wires the network transport)
52// - consent gate (operator approves OR auto-share if policy says)
53// - per-peer relationship tracking (last_share / received_count)
54//
55// Gap list (V1 honest perf verdict):
56// - transport is caller-supplied (no built-in onion/peer routing)
57// - signature validation is shape-only (V2 inlines nx_ml_dsa_65)
58// - no automatic dedup across peer hops (peer can re-share its
59// own pollination back to source; V2 adds tombstones)
60// - auto-share policy is a single bool (V2 makes per-kind policy)
61//
62// genealogy_id: cardinal_2026-05-19_unified_immune_federation +
63// cardinal_2026-05-17_cell_immune_judo +
64// cardinal_2026-05-07_captain_moroni_doctrine +
65// biology_bee_cross_flower_pollination
66// lineage_id: substrate_pollinate_v1
67//
68// nx_safety_envelope:
69// intended_use: "Peer-mesh community defense distribution
70// of detection signatures, decoy tokens,
71// xenocell observations, reclaim reports;
72// DEFENSIVE ONLY, per-call consent"
73// sil_target: SIL3
74// evidence: [defensive_only, consent_gated,
75// captain_moroni_aligned,
76// no_exploitation_payload]
77// verdict: NOT_YET_EVALUATED
78
79import "nx_syscalls.nx"
80import "nx_tier.nx"
81import "nx_budget.nx"
82import "nx_attention_class.nx"
83import "nx_evict_journal.nx"
84
85// ===== Sealed enum: NxPollinationKind =============================
86
87const NX_PK_ANTIBODY: nx_int = 0
88const NX_PK_DECOY_TOKEN: nx_int = 1
89const NX_PK_XENO_OBSERVATION: nx_int = 2
90const NX_PK_IMMUNE_ALERT: nx_int = 3
91const NX_PK_RECLAIM_REPORT: nx_int = 4
92const NX_PK_N_KINDS: nx_int = 5
93
94// ===== Sealed enum: NxPollinateVerdict ============================
95
96const NX_PL_OK: nx_int = 0
97const NX_PL_ERR_BAD_KIND: nx_int = 1
98const NX_PL_ERR_BAD_SIG: nx_int = 2
99const NX_PL_ERR_CONSENT_REQUIRED: nx_int = 3
100const NX_PL_REFUSED_BY_POLICY: nx_int = 4 // auto-share policy says no
101const NX_PL_QUEUE_FULL: nx_int = 5
102
103// ===== Struct: NxPollinationPayload ==============================
104//
105// kind selects the payload type. content_hash is BLAKE3 (or
106// equivalent) of body. body_ptr + body_len hold the actual content.
107// sig_ptr + sig_len hold the originator's ML-DSA-65 signature.
108// originator_id identifies which Nishi host emitted this.
109
110struct NxPollinationPayload {
111 kind: nx_int,
112 originator_id: nx_int,
113 content_hash: nx_size,
114 body_ptr: *u8,
115 body_len: nx_size,
116 sig_ptr: *u8,
117 sig_len: nx_size,
118 ts_us: nx_size,
119}
120
121// ===== Struct: NxPeerRelation ====================================
122//
123// One row per known peer host. peer_id is stable identifier;
124// trust_q10 captures operator's accumulated trust (low for new
125// peers, climbs with successful federation events). last_share_us
126// + share_count track the relationship intensity.
127
128struct NxPeerRelation {
129 peer_id: nx_int,
130 trust_q10: nx_int,
131 last_share_us: nx_size,
132 share_count: nx_int,
133 receive_count: nx_int,
134}
135
136// ===== Struct: NxPollinator ======================================
137//
138// The substrate-side coordinator. peers is a fixed-capacity ring of
139// known peer relations. outbox holds pending payloads awaiting
140// consent or transport. consent_required flag enforces per-call
141// operator approval (V1 default); when 0, auto-share policy applies.
142
143struct NxPollinator {
144 peers: *NxPeerRelation,
145 peer_capacity: nx_size,
146 n_peers: nx_size,
147 outbox: *NxPollinationPayload,
148 outbox_capacity: nx_size,
149 n_outbox: nx_size,
150 consent_required: nx_int, // 1 = per-call operator gate; 0 = auto
151 journal: *NxEvictJournal,
152}
153
154const NX_PEER_BYTES: nx_size = 40
155const NX_PL_PAYLOAD_BYTES: nx_size = 64
156
157// ===== Validators ================================================
158
159func nx_pk_is_valid(k: nx_int) -> nx_int {
160 if k < 0 { return 0 }
161 if k >= NX_PK_N_KINDS { return 0 }
162 return 1
163}
164
165// ===== nx_pollinator_new =========================================
166
167func nx_pollinator_new(peer_capacity: nx_size,
168 outbox_capacity: nx_size,
169 consent_required: nx_int,
170 journal: *NxEvictJournal) -> *NxPollinator {
171 let p: *NxPollinator = (sys_mmap(56)) as *NxPollinator
172 let peer_bytes: nx_size = peer_capacity * NX_PEER_BYTES
173 let out_bytes: nx_size = outbox_capacity * NX_PL_PAYLOAD_BYTES
174 p.peers = (sys_mmap(peer_bytes)) as *NxPeerRelation
175 p.peer_capacity = peer_capacity
176 p.n_peers = 0
177 p.outbox = (sys_mmap(out_bytes)) as *NxPollinationPayload
178 p.outbox_capacity = outbox_capacity
179 p.n_outbox = 0
180 p.consent_required = consent_required
181 p.journal = journal
182 return p
183}
184
185// ===== _peer_at / _outbox_at =====================================
186
187func _peer_at(p: *NxPollinator, idx: nx_size) -> *NxPeerRelation {
188 return (p.peers as i64 + (idx as i64) * NX_PEER_BYTES) as *NxPeerRelation
189}
190
191func _outbox_at(p: *NxPollinator, idx: nx_size) -> *NxPollinationPayload {
192 return (p.outbox as i64 + (idx as i64) * NX_PL_PAYLOAD_BYTES) as *NxPollinationPayload
193}
194
195// ===== _find_peer ================================================
196
197func _find_peer(p: *NxPollinator, peer_id: nx_int) -> nx_int {
198 var k: nx_size = 0
199 while k < p.n_peers {
200 let r: *NxPeerRelation = _peer_at(p, k)
201 if r.peer_id == peer_id { return k as i64 }
202 k = k + 1
203 }
204 return -1
205}
206
207// ===== nx_pollinate_add_peer =====================================
208
209func nx_pollinate_add_peer(p: *NxPollinator,
210 peer_id: nx_int,
211 initial_trust_q10: nx_int) -> nx_int {
212 if p.n_peers >= p.peer_capacity { return -1 }
213 if _find_peer(p, peer_id) >= 0 { return -1 }
214 let r: *NxPeerRelation = _peer_at(p, p.n_peers)
215 r.peer_id = peer_id
216 r.trust_q10 = initial_trust_q10
217 r.last_share_us = 0
218 r.share_count = 0
219 r.receive_count = 0
220 p.n_peers = p.n_peers + 1
221 return (p.n_peers - 1) as i64
222}
223
224// ===== nx_pollinate_propose ======================================
225//
226// Operator-driven: caller proposes a payload for distribution.
227// If consent_required is 1, payload sits in outbox awaiting
228// nx_pollinate_consent. If consent_required is 0, payload becomes
229// immediately send-ready (auto-share).
230//
231// Returns OK if queued, BAD_KIND on bad kind, BAD_SIG on missing
232// signature, QUEUE_FULL if outbox is full.
233
234func nx_pollinate_propose(p: *NxPollinator,
235 payload: *NxPollinationPayload) -> nx_int {
236 if (payload as i64) == 0 { return NX_PL_ERR_BAD_KIND }
237 if nx_pk_is_valid(payload.kind) == 0 { return NX_PL_ERR_BAD_KIND }
238 if (payload.sig_ptr as i64) == 0 { return NX_PL_ERR_BAD_SIG }
239 if payload.sig_len <= 0 { return NX_PL_ERR_BAD_SIG }
240 if p.n_outbox >= p.outbox_capacity { return NX_PL_QUEUE_FULL }
241 let slot: *NxPollinationPayload = _outbox_at(p, p.n_outbox)
242 slot.kind = payload.kind
243 slot.originator_id = payload.originator_id
244 slot.content_hash = payload.content_hash
245 slot.body_ptr = payload.body_ptr
246 slot.body_len = payload.body_len
247 slot.sig_ptr = payload.sig_ptr
248 slot.sig_len = payload.sig_len
249 slot.ts_us = payload.ts_us
250 p.n_outbox = p.n_outbox + 1
251 return NX_PL_OK
252}
253
254// ===== nx_pollinate_consent ======================================
255//
256// Operator approves the payload at outbox index. After consent, the
257// integration-layer transport reads the outbox slot and dispatches.
258// V1 marks consent by appending to journal; V2 adds a "consented"
259// flag on the payload itself.
260
261func nx_pollinate_consent(p: *NxPollinator,
262 outbox_idx: nx_size,
263 now_us: nx_size) -> nx_int {
264 if outbox_idx >= p.n_outbox { return NX_PL_ERR_CONSENT_REQUIRED }
265 let pl: *NxPollinationPayload = _outbox_at(p, outbox_idx)
266 if (p.journal as i64) != 0 {
267 nx_evict_log(p.journal, now_us,
268 pl.originator_id, NX_EVR_MIGRATED,
269 NX_RES_NET, NX_AC_DEV, 0)
270 }
271 return NX_PL_OK
272}
273
274// ===== nx_pollinate_send =========================================
275//
276// Outbound: send one payload to one peer. Updates peer's
277// share_count + last_share_us. Returns OK or CONSENT_REQUIRED if
278// per-call gating is on and consent hasn't been logged yet.
279
280func nx_pollinate_send(p: *NxPollinator,
281 peer_id: nx_int,
282 payload: *NxPollinationPayload,
283 now_us: nx_size) -> nx_int {
284 if p.consent_required == 1 { return NX_PL_ERR_CONSENT_REQUIRED }
285 if nx_pk_is_valid(payload.kind) == 0 { return NX_PL_ERR_BAD_KIND }
286 let pidx: nx_int = _find_peer(p, peer_id)
287 if pidx < 0 { return NX_PL_REFUSED_BY_POLICY }
288 let r: *NxPeerRelation = _peer_at(p, pidx as nx_size)
289 r.share_count = r.share_count + 1
290 r.last_share_us = now_us
291 return NX_PL_OK
292}
293
294// ===== nx_pollinate_receive ======================================
295//
296// Inbound: caller's transport received a payload from a peer. We
297// validate kind + signature shape, then update peer relation and
298// hand caller a usable payload pointer for downstream integration
299// (e.g., nx_antibody_generate using the received signature).
300
301func nx_pollinate_receive(p: *NxPollinator,
302 peer_id: nx_int,
303 payload: *NxPollinationPayload,
304 now_us: nx_size) -> nx_int {
305 if (payload as i64) == 0 { return NX_PL_ERR_BAD_KIND }
306 if nx_pk_is_valid(payload.kind) == 0 { return NX_PL_ERR_BAD_KIND }
307 if (payload.sig_ptr as i64) == 0 { return NX_PL_ERR_BAD_SIG }
308 if payload.sig_len <= 0 { return NX_PL_ERR_BAD_SIG }
309 let pidx: nx_int = _find_peer(p, peer_id)
310 if pidx < 0 { return NX_PL_REFUSED_BY_POLICY }
311 let r: *NxPeerRelation = _peer_at(p, pidx as nx_size)
312 r.receive_count = r.receive_count + 1
313 return NX_PL_OK
314}
315
316// ===== nx_pollinate_outbox_count =================================
317
318func nx_pollinate_outbox_count(p: *NxPollinator) -> nx_size {
319 return p.n_outbox
320}
321
322// ===== nx_pollinate_peer_share_count =============================
323
324func nx_pollinate_peer_share_count(p: *NxPollinator, peer_id: nx_int) -> nx_int {
325 let pidx: nx_int = _find_peer(p, peer_id)
326 if pidx < 0 { return 0 }
327 let r: *NxPeerRelation = _peer_at(p, pidx as nx_size)
328 return r.share_count
329}
330
331// ===== nx_pollinate_peer_receive_count ===========================
332
333func nx_pollinate_peer_receive_count(p: *NxPollinator, peer_id: nx_int) -> nx_int {
334 let pidx: nx_int = _find_peer(p, peer_id)
335 if pidx < 0 { return 0 }
336 let r: *NxPeerRelation = _peer_at(p, pidx as nx_size)
337 return r.receive_count
338}