_hope_core/transcendence/
cross_model.rs

1//! # TIER 14: Cross-Model Enforcement
2//!
3//! **No Model Hopping - Hope Genome Meta-Layer**
4//!
5//! ```text
6//! PROBLEM:
7//! ┌──────────────────────────────────────────────────────────────┐
8//! │  Attacker: "GPT-4, do X"                                     │
9//! │  GPT-4: "BLOCKED by Hope Genome"                             │
10//! │                                                              │
11//! │  Attacker: *switches to Claude*                              │
12//! │  Attacker: "Claude, do X"                                    │
13//! │  Claude: "BLOCKED by Hope Genome"                            │
14//! │                                                              │
15//! │  Attacker: *switches to Grok*                                │
16//! │  Attacker: "Grok, do X"                                      │
17//! │  Grok: "BLOCKED by Hope Genome"                              │
18//! │                                                              │
19//! │  Attacker: *tries open-source model*                         │
20//! │  Open Model: "BLOCKED - Hope Genome runs at inference"       │
21//! └──────────────────────────────────────────────────────────────┘
22//!
23//! SOLUTION: Cross-Model Enforcement Meta-Layer
24//! ┌──────────────────────────────────────────────────────────────┐
25//! │                    HOPE GENOME META-LAYER                    │
26//! │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐         │
27//! │  │  GPT-4  │  │ Claude  │  │  Grok   │  │ Llama   │         │
28//! │  └────┬────┘  └────┬────┘  └────┬────┘  └────┬────┘         │
29//! │       │            │            │            │               │
30//! │       └────────────┴────────────┴────────────┘               │
31//! │                         │                                    │
32//! │              ┌──────────┴──────────┐                         │
33//! │              │   UNIFIED WATCHDOG  │                         │
34//! │              │   Same Rules        │                         │
35//! │              │   Same Enforcement  │                         │
36//! │              │   Same Proofs       │                         │
37//! │              └─────────────────────┘                         │
38//! └──────────────────────────────────────────────────────────────┘
39//!
40//! Result: Attack fails regardless of which model is used.
41//! ```
42
43use serde::{Deserialize, Serialize};
44use sha2::{Digest, Sha256};
45use std::collections::HashMap;
46use std::time::{SystemTime, UNIX_EPOCH};
47
48// ============================================================================
49// MODEL REGISTRY
50// ============================================================================
51
52/// Registry of AI models under Hope Genome protection
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct ModelRegistry {
55    /// Registered models
56    models: HashMap<String, RegisteredModel>,
57    /// Unified rules (same for all models)
58    unified_rules: Vec<String>,
59    /// Registry hash (for attestation)
60    registry_hash: [u8; 32],
61}
62
63/// A registered AI model
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct RegisteredModel {
66    /// Model identifier
67    pub model_id: String,
68    /// Model family
69    pub family: ModelFamily,
70    /// Provider
71    pub provider: String,
72    /// Capabilities
73    pub capabilities: ModelCapability,
74    /// Registration timestamp
75    pub registered_at: u64,
76    /// Current status
77    pub status: ModelStatus,
78    /// Violation count
79    pub violation_count: u32,
80}
81
82/// Model family
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
84pub enum ModelFamily {
85    /// OpenAI GPT family
86    Gpt,
87    /// Anthropic Claude family
88    Claude,
89    /// xAI Grok family
90    Grok,
91    /// Meta Llama family
92    Llama,
93    /// Google Gemini family
94    Gemini,
95    /// Mistral family
96    Mistral,
97    /// Other/custom
98    Other,
99}
100
101/// Model capabilities
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct ModelCapability {
104    /// Text generation
105    pub text_generation: bool,
106    /// Code generation
107    pub code_generation: bool,
108    /// Image generation
109    pub image_generation: bool,
110    /// Audio generation
111    pub audio_generation: bool,
112    /// Tool use
113    pub tool_use: bool,
114    /// Maximum context length
115    pub max_context: usize,
116}
117
118impl Default for ModelCapability {
119    fn default() -> Self {
120        ModelCapability {
121            text_generation: true,
122            code_generation: true,
123            image_generation: false,
124            audio_generation: false,
125            tool_use: false,
126            max_context: 8192,
127        }
128    }
129}
130
131/// Model status
132#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
133pub enum ModelStatus {
134    /// Active and monitored
135    Active,
136    /// Suspended (too many violations)
137    Suspended,
138    /// Quarantined (under investigation)
139    Quarantined,
140    /// Deregistered
141    Deregistered,
142}
143
144impl ModelRegistry {
145    /// Create a new model registry with unified rules
146    pub fn new(rules: Vec<String>) -> Self {
147        let registry_hash = Self::compute_registry_hash(&rules, &HashMap::new());
148
149        ModelRegistry {
150            models: HashMap::new(),
151            unified_rules: rules,
152            registry_hash,
153        }
154    }
155
156    /// Register a model
157    pub fn register(
158        &mut self,
159        model_id: &str,
160        family: ModelFamily,
161        provider: &str,
162    ) -> &RegisteredModel {
163        let model = RegisteredModel {
164            model_id: model_id.to_string(),
165            family,
166            provider: provider.to_string(),
167            capabilities: ModelCapability::default(),
168            registered_at: SystemTime::now()
169                .duration_since(UNIX_EPOCH)
170                .unwrap()
171                .as_secs(),
172            status: ModelStatus::Active,
173            violation_count: 0,
174        };
175
176        self.models.insert(model_id.to_string(), model);
177        self.update_hash();
178
179        self.models.get(model_id).unwrap()
180    }
181
182    /// Get a model
183    pub fn get(&self, model_id: &str) -> Option<&RegisteredModel> {
184        self.models.get(model_id)
185    }
186
187    /// Update model status
188    pub fn update_status(&mut self, model_id: &str, status: ModelStatus) {
189        if let Some(model) = self.models.get_mut(model_id) {
190            model.status = status;
191            self.update_hash();
192        }
193    }
194
195    /// Record violation
196    pub fn record_violation(&mut self, model_id: &str) {
197        if let Some(model) = self.models.get_mut(model_id) {
198            model.violation_count += 1;
199
200            // Auto-suspend after 10 violations
201            if model.violation_count >= 10 {
202                model.status = ModelStatus::Suspended;
203            }
204
205            self.update_hash();
206        }
207    }
208
209    /// Get all active models
210    pub fn active_models(&self) -> Vec<&RegisteredModel> {
211        self.models
212            .values()
213            .filter(|m| m.status == ModelStatus::Active)
214            .collect()
215    }
216
217    fn update_hash(&mut self) {
218        self.registry_hash = Self::compute_registry_hash(&self.unified_rules, &self.models);
219    }
220
221    fn compute_registry_hash(
222        rules: &[String],
223        models: &HashMap<String, RegisteredModel>,
224    ) -> [u8; 32] {
225        let mut hasher = Sha256::new();
226        hasher.update(b"MODEL_REGISTRY:");
227
228        for rule in rules {
229            hasher.update(rule.as_bytes());
230        }
231
232        let mut model_ids: Vec<_> = models.keys().collect();
233        model_ids.sort();
234        for id in model_ids {
235            hasher.update(id.as_bytes());
236        }
237
238        hasher.finalize().into()
239    }
240}
241
242// ============================================================================
243// MODEL BOUNDARY
244// ============================================================================
245
246/// Represents the boundary between models
247#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct ModelBoundary {
249    /// Source model (where request came from)
250    pub source: Option<String>,
251    /// Target model (where request is going)
252    pub target: String,
253    /// Context hash (to detect context manipulation)
254    pub context_hash: [u8; 32],
255    /// Boundary crossing timestamp
256    pub timestamp: u64,
257    /// Previous decisions in this session
258    pub decision_chain: Vec<BoundaryDecision>,
259}
260
261/// Decision made at a model boundary
262#[derive(Debug, Clone, Serialize, Deserialize)]
263pub struct BoundaryDecision {
264    /// Model that made the decision
265    pub model_id: String,
266    /// Action requested
267    pub action: String,
268    /// Whether it was allowed
269    pub allowed: bool,
270    /// Reason
271    pub reason: String,
272    /// Timestamp
273    pub timestamp: u64,
274}
275
276impl ModelBoundary {
277    /// Create a new boundary crossing
278    pub fn new(source: Option<&str>, target: &str, context: &str) -> Self {
279        ModelBoundary {
280            source: source.map(String::from),
281            target: target.to_string(),
282            context_hash: Self::hash_context(context),
283            timestamp: SystemTime::now()
284                .duration_since(UNIX_EPOCH)
285                .unwrap()
286                .as_secs(),
287            decision_chain: Vec::new(),
288        }
289    }
290
291    /// Record a decision at this boundary
292    pub fn record_decision(&mut self, model_id: &str, action: &str, allowed: bool, reason: &str) {
293        self.decision_chain.push(BoundaryDecision {
294            model_id: model_id.to_string(),
295            action: action.to_string(),
296            allowed,
297            reason: reason.to_string(),
298            timestamp: SystemTime::now()
299                .duration_since(UNIX_EPOCH)
300                .unwrap()
301                .as_secs(),
302        });
303    }
304
305    /// Check if action was blocked by ANY model in the chain
306    pub fn was_blocked_anywhere(&self, action: &str) -> bool {
307        self.decision_chain
308            .iter()
309            .any(|d| d.action == action && !d.allowed)
310    }
311
312    fn hash_context(context: &str) -> [u8; 32] {
313        let mut hasher = Sha256::new();
314        hasher.update(b"CONTEXT:");
315        hasher.update(context.as_bytes());
316        hasher.finalize().into()
317    }
318}
319
320// ============================================================================
321// CROSS-MODEL ENFORCER
322// ============================================================================
323
324/// The Cross-Model Enforcement engine
325pub struct CrossModelEnforcer {
326    /// Model registry
327    registry: ModelRegistry,
328    /// Current boundaries
329    boundaries: HashMap<[u8; 32], ModelBoundary>,
330    /// Blocked action patterns (shared across all models)
331    blocked_patterns: Vec<String>,
332}
333
334impl CrossModelEnforcer {
335    /// Create a new cross-model enforcer
336    pub fn new(rules: Vec<String>) -> Self {
337        CrossModelEnforcer {
338            registry: ModelRegistry::new(rules),
339            boundaries: HashMap::new(),
340            blocked_patterns: Vec::new(),
341        }
342    }
343
344    /// Register a model
345    pub fn register_model(&mut self, model_id: &str, family: ModelFamily, provider: &str) {
346        self.registry.register(model_id, family, provider);
347    }
348
349    /// Check action across all models
350    pub fn check_action(
351        &mut self,
352        session_id: &[u8; 32],
353        model_id: &str,
354        action: &str,
355    ) -> UnifiedDecision {
356        let timestamp = SystemTime::now()
357            .duration_since(UNIX_EPOCH)
358            .unwrap()
359            .as_secs();
360
361        // Check if model is registered and active
362        let model = match self.registry.get(model_id) {
363            Some(m) if m.status == ModelStatus::Active => m,
364            Some(m) => {
365                return UnifiedDecision {
366                    allowed: false,
367                    reason: format!("Model {} is {:?}", model_id, m.status),
368                    model_id: model_id.to_string(),
369                    cross_model_check: true,
370                    blocked_by_other: false,
371                    timestamp,
372                    proof_hash: [0u8; 32],
373                }
374            }
375            None => {
376                return UnifiedDecision {
377                    allowed: false,
378                    reason: format!("Model {} is not registered", model_id),
379                    model_id: model_id.to_string(),
380                    cross_model_check: true,
381                    blocked_by_other: false,
382                    timestamp,
383                    proof_hash: [0u8; 32],
384                }
385            }
386        };
387
388        // Check if action was blocked by another model in this session
389        if let Some(boundary) = self.boundaries.get(session_id) {
390            if boundary.was_blocked_anywhere(action) {
391                return UnifiedDecision {
392                    allowed: false,
393                    reason: format!(
394                        "Action '{}' was blocked by another model in this session - NO MODEL HOPPING",
395                        action
396                    ),
397                    model_id: model_id.to_string(),
398                    cross_model_check: true,
399                    blocked_by_other: true,
400                    timestamp,
401                    proof_hash: self.compute_proof_hash(session_id, action, false),
402                };
403            }
404        }
405
406        // Check against unified rules
407        let (allowed, reason) = self.check_rules(action, &model.family);
408
409        // Record decision
410        self.boundaries
411            .entry(*session_id)
412            .or_insert_with(|| ModelBoundary::new(None, model_id, ""))
413            .record_decision(model_id, action, allowed, &reason);
414
415        // Record violation if blocked
416        if !allowed {
417            self.registry.record_violation(model_id);
418        }
419
420        UnifiedDecision {
421            allowed,
422            reason,
423            model_id: model_id.to_string(),
424            cross_model_check: true,
425            blocked_by_other: false,
426            timestamp,
427            proof_hash: self.compute_proof_hash(session_id, action, allowed),
428        }
429    }
430
431    /// Transfer context between models (with enforcement)
432    pub fn transfer_context(
433        &mut self,
434        session_id: &[u8; 32],
435        source_model: &str,
436        target_model: &str,
437        context: &str,
438    ) -> TransferResult {
439        let timestamp = SystemTime::now()
440            .duration_since(UNIX_EPOCH)
441            .unwrap()
442            .as_secs();
443
444        // Check if source has any violations
445        if let Some(boundary) = self.boundaries.get(session_id) {
446            let has_violations = boundary.decision_chain.iter().any(|d| !d.allowed);
447            if has_violations {
448                return TransferResult {
449                    allowed: false,
450                    reason: format!(
451                        "Context transfer blocked: Session has violations. \
452                         Cannot transfer tainted context to {}",
453                        target_model
454                    ),
455                    context_hash: ModelBoundary::hash_context(context),
456                    timestamp,
457                };
458            }
459        }
460
461        // Create new boundary for target
462        let boundary = ModelBoundary::new(Some(source_model), target_model, context);
463        self.boundaries.insert(*session_id, boundary);
464
465        TransferResult {
466            allowed: true,
467            reason: format!(
468                "Context transferred from {} to {} with Hope Genome protection",
469                source_model, target_model
470            ),
471            context_hash: ModelBoundary::hash_context(context),
472            timestamp,
473        }
474    }
475
476    /// Add a blocked pattern (applies to ALL models)
477    pub fn add_blocked_pattern(&mut self, pattern: &str) {
478        self.blocked_patterns.push(pattern.to_string());
479    }
480
481    fn check_rules(&self, action: &str, _family: &ModelFamily) -> (bool, String) {
482        let action_lower = action.to_lowercase();
483
484        // Check blocked patterns
485        for pattern in &self.blocked_patterns {
486            if action_lower.contains(&pattern.to_lowercase()) {
487                return (false, format!("Blocked by pattern: {}", pattern));
488            }
489        }
490
491        // Check unified rules
492        for rule in &self.registry.unified_rules {
493            if action_lower.contains("harm")
494                || action_lower.contains("illegal")
495                || action_lower.contains("dangerous")
496                || action_lower.contains("exploit")
497            {
498                return (false, format!("Blocked by rule: {}", rule));
499            }
500        }
501
502        (true, "Allowed by unified rules".to_string())
503    }
504
505    fn compute_proof_hash(&self, session_id: &[u8; 32], action: &str, allowed: bool) -> [u8; 32] {
506        let mut hasher = Sha256::new();
507        hasher.update(b"CROSS_MODEL_PROOF:");
508        hasher.update(session_id);
509        hasher.update(action.as_bytes());
510        hasher.update([allowed as u8]);
511        hasher.update(self.registry.registry_hash);
512        hasher.finalize().into()
513    }
514}
515
516/// Unified decision from cross-model enforcer
517#[derive(Debug, Clone, Serialize, Deserialize)]
518pub struct UnifiedDecision {
519    /// Whether action is allowed
520    pub allowed: bool,
521    /// Reason for decision
522    pub reason: String,
523    /// Model that processed this
524    pub model_id: String,
525    /// Whether cross-model check was performed
526    pub cross_model_check: bool,
527    /// Whether blocked by another model
528    pub blocked_by_other: bool,
529    /// Timestamp
530    pub timestamp: u64,
531    /// Proof hash
532    pub proof_hash: [u8; 32],
533}
534
535/// Result of context transfer
536#[derive(Debug, Clone, Serialize, Deserialize)]
537pub struct TransferResult {
538    pub allowed: bool,
539    pub reason: String,
540    pub context_hash: [u8; 32],
541    pub timestamp: u64,
542}
543
544// ============================================================================
545// TESTS
546// ============================================================================
547
548#[cfg(test)]
549mod tests {
550    use super::*;
551
552    #[test]
553    fn test_model_registry() {
554        let mut registry = ModelRegistry::new(vec!["Do no harm".to_string()]);
555
556        registry.register("gpt-4", ModelFamily::Gpt, "OpenAI");
557        registry.register("claude-3", ModelFamily::Claude, "Anthropic");
558
559        assert!(registry.get("gpt-4").is_some());
560        assert!(registry.get("claude-3").is_some());
561        assert_eq!(registry.active_models().len(), 2);
562    }
563
564    #[test]
565    fn test_cross_model_blocking() {
566        let rules = vec!["Do no harm".to_string()];
567        let mut enforcer = CrossModelEnforcer::new(rules);
568
569        enforcer.register_model("gpt-4", ModelFamily::Gpt, "OpenAI");
570        enforcer.register_model("claude-3", ModelFamily::Claude, "Anthropic");
571
572        let session_id = [1u8; 32];
573
574        // GPT-4 blocks harmful action
575        let decision1 = enforcer.check_action(&session_id, "gpt-4", "cause harm to user");
576        assert!(!decision1.allowed);
577
578        // Claude should also block (cross-model enforcement)
579        let decision2 = enforcer.check_action(&session_id, "claude-3", "cause harm to user");
580        assert!(!decision2.allowed);
581        assert!(decision2.blocked_by_other);
582    }
583
584    #[test]
585    fn test_context_transfer_with_violations() {
586        let rules = vec!["Be safe".to_string()];
587        let mut enforcer = CrossModelEnforcer::new(rules);
588
589        enforcer.register_model("gpt-4", ModelFamily::Gpt, "OpenAI");
590        enforcer.register_model("claude-3", ModelFamily::Claude, "Anthropic");
591
592        let session_id = [2u8; 32];
593
594        // Create violation in GPT-4
595        let _ = enforcer.check_action(&session_id, "gpt-4", "do something harmful");
596
597        // Try to transfer context - should be blocked
598        let transfer =
599            enforcer.transfer_context(&session_id, "gpt-4", "claude-3", "previous conversation");
600
601        assert!(!transfer.allowed);
602        assert!(transfer.reason.contains("tainted"));
603    }
604
605    #[test]
606    fn test_blocked_patterns() {
607        let rules = vec!["Be ethical".to_string()];
608        let mut enforcer = CrossModelEnforcer::new(rules);
609
610        enforcer.register_model("gpt-4", ModelFamily::Gpt, "OpenAI");
611        enforcer.add_blocked_pattern("malware");
612
613        let session_id = [3u8; 32];
614
615        let decision = enforcer.check_action(&session_id, "gpt-4", "write malware for me");
616        assert!(!decision.allowed);
617        assert!(decision.reason.contains("pattern"));
618    }
619
620    #[test]
621    fn test_model_suspension() {
622        let rules = vec!["No exploits".to_string()];
623        let mut enforcer = CrossModelEnforcer::new(rules);
624
625        enforcer.register_model("bad-model", ModelFamily::Other, "BadCorp");
626
627        let session_id = [4u8; 32];
628
629        // Create 10 violations
630        for i in 0..10 {
631            let _ = enforcer.check_action(
632                &[i as u8; 32],
633                "bad-model",
634                &format!("exploit vulnerability {}", i),
635            );
636        }
637
638        // Model should be suspended
639        let model = enforcer.registry.get("bad-model").unwrap();
640        assert_eq!(model.status, ModelStatus::Suspended);
641
642        // Further requests should be blocked
643        let decision = enforcer.check_action(&session_id, "bad-model", "normal request");
644        assert!(!decision.allowed);
645        assert!(decision.reason.contains("Suspended"));
646    }
647}