//! `EnteGraph`: estado del fractal vivo en PID 1. //! //! Diseño: //! - Submódulos por concern: lifecycle, topology, shutdown, bus_mediator, //! devices, capabilities. Cada uno extiende `impl EnteGraph` con métodos //! relacionados. //! - Estado plano (no substructs todavía) — la separación es por //! comportamiento, no por compartimentación de datos. //! - Toda mutación pasa por el bucle primordial vía `GraphEvent`. Los //! submódulos se llaman desde `main.rs::primordial_loop`. mod bus_mediator; mod capabilities; mod devices; mod lifecycle; mod shutdown; mod topology; use ente_bus::{BusMessage, BusResponse}; use ente_card::{Capability, EntityCard}; use nix::unistd::Pid; use std::collections::{BTreeMap, BTreeSet, HashMap}; use tokio::sync::{mpsc, oneshot}; use ulid::Ulid; pub use shutdown::SHUTDOWN_GRACE; /// Bit alto encendido en `seq` para invokes server-iniciados — evita choque /// con secuencias allocadas por clientes. pub(in crate::graph) const SERVER_SEQ_FLAG: u64 = 1u64 << 63; pub struct EnteGraph { pub(in crate::graph) seed: EntityCard, /// Entes encarnados como proceso o nodo virtual. id↔pid bidireccional. pub(in crate::graph) incarnated: HashMap, pub(in crate::graph) by_pid: HashMap, /// Quién provee qué capacidad. Resuelve `requires` y `pick_invokable`. pub(in crate::graph) providers: BTreeMap>, /// Tokens de capability emitidos. Revocables al morir el proveedor. pub(in crate::graph) next_token: u64, pub(in crate::graph) grants: HashMap, /// Dispositivos del kernel presentes (devpath → última UEvent). pub(in crate::graph) devices: HashMap, /// Cards genesis pendientes de instanciar (extraídas de la Semilla). pub(in crate::graph) pending_genesis: Vec, /// Hijos directos por lineage. parent → [child, ...]. pub(in crate::graph) children: HashMap>, /// Conexiones del bus indexadas por la identidad anunciada y verificada /// con SO_PEERCRED. El value es el extremo de escritura del writer task. pub(in crate::graph) bus_connections: HashMap>, /// Invokes forwardeados pendientes de respuesta del proveedor. pub(in crate::graph) pending_invokes: HashMap>, pub(in crate::graph) next_invoke_seq: u64, } pub(in crate::graph) struct Incarnated { pub card: EntityCard, pub pid: Option, } pub(in crate::graph) struct GrantedCapability { pub cap: Capability, pub provider: Ulid, pub holder: Ulid, } impl EnteGraph { pub fn new(mut seed: EntityCard) -> Self { // Extraemos genesis antes de almacenar la Semilla — evita duplicación // en `incarnated[seed.id]`. let pending_genesis = std::mem::take(&mut seed.genesis); let mut g = Self { seed: seed.clone(), incarnated: HashMap::new(), by_pid: HashMap::new(), providers: BTreeMap::new(), next_token: 1, grants: HashMap::new(), devices: HashMap::new(), pending_genesis, children: HashMap::new(), bus_connections: HashMap::new(), pending_invokes: HashMap::new(), next_invoke_seq: 0, }; // El Ente #0 se inscribe a sí mismo como proveedor de las capacidades // que su Card declara — sólo así los hijos pueden requerirlas. g.register_provider(&seed); g.incarnated.insert(seed.id, Incarnated { card: seed, pid: None }); g } pub fn lookup_pid(&self, pid: Pid) -> Option { self.by_pid.get(&pid.as_raw()).copied() } /// Acceso read-only a la Card de un Ente vivo. Usado por el cerebro /// para hidratar `SubjectInfo` sin clonar todo el mapa. pub fn peek_card(&self, id: &Ulid) -> Option<&EntityCard> { self.incarnated.get(id).map(|i| &i.card) } /// Identidad de la Semilla. Usado como `requester` para spawns generados /// por reglas auto-cristalizadas (única identidad con Capability::Spawn). pub fn seed_id(&self) -> Ulid { self.seed.id } /// Captura el estado live como snapshot serializable. Excluye la Semilla /// (será re-sintetizada al restore con su seed_id preservado). pub fn snapshot(&self) -> ente_snapshot::FractalSnapshot { let entes: Vec = self.incarnated.iter() .filter(|(id, _)| **id != self.seed.id) .map(|(_, inc)| inc.card.clone()) .collect(); ente_snapshot::FractalSnapshot { version: ente_snapshot::SNAPSHOT_VERSION, timestamp_ms: ente_snapshot::now_ms(), seed_id: self.seed.id, seed_label: self.seed.label.clone(), entes, } } pub(in crate::graph) fn register_provider(&mut self, card: &EntityCard) { for cap in &card.provides { self.providers.entry(cap.clone()).or_default().insert(card.id); } } pub(in crate::graph) fn unregister_provider(&mut self, card: &EntityCard) { for cap in &card.provides { if let Some(set) = self.providers.get_mut(cap) { set.remove(&card.id); } } // Revocar grants emitidos por el Ente fallecido. let revoked: Vec = self.grants.iter() .filter(|(_, g)| g.provider == card.id) .map(|(t, _)| *t) .collect(); for t in revoked { self.grants.remove(&t); } } /// El Ente #0 (semilla) tiene todas sus capacidades declaradas. Otros /// las tienen si su Card las declara o si poseen un grant vivo. pub(in crate::graph) fn holder_has(&self, holder: Ulid, cap: &Capability) -> bool { if holder == self.seed.id { return self.seed.provides.contains(cap); } if let Some(inc) = self.incarnated.get(&holder) { if inc.card.provides.contains(cap) { return true; } } self.grants.values().any(|g| g.holder == holder && &g.cap == cap) } }