tawasuyu
⎇ ca75ba185f880d431eb118250d3931a91d354b2e crates/ente-zero/src/graph/mod.rs Rust · 184 lines · 7.3 KB History Who touched what raw

Cristales temporales, replay, lease/renew y audit streaming ca75ba185 · Sergio ·

1//! `EnteGraph`: estado del fractal vivo en PID 1.
2//!
3//! Diseño:
4//!   - Submódulos por concern: lifecycle, topology, shutdown, bus_mediator,
5//!     devices, capabilities. Cada uno extiende `impl EnteGraph` con métodos
6//!     relacionados.
7//!   - Estado plano (no substructs todavía) — la separación es por
8//!     comportamiento, no por compartimentación de datos.
9//!   - Toda mutación pasa por el bucle primordial vía `GraphEvent`. Los
10//!     submódulos se llaman desde `main.rs::primordial_loop`.
11
12mod bus_mediator;
13mod capabilities;
14mod devices;
15mod lifecycle;
16mod shutdown;
17mod topology;
18
19use ente_bus::{BusMessage, BusResponse};
20use ente_card::{Capability, EntityCard};
21use nix::unistd::Pid;
22use std::collections::{BTreeMap, BTreeSet, HashMap};
23use tokio::sync::{mpsc, oneshot};
24use ulid::Ulid;
25
26pub use shutdown::SHUTDOWN_GRACE;
27
28/// Bit alto encendido en `seq` para invokes server-iniciados — evita choque
29/// con secuencias allocadas por clientes.
30pub(in crate::graph) const SERVER_SEQ_FLAG: u64 = 1u64 << 63;
31
32pub struct EnteGraph {
33    pub(in crate::graph) seed: EntityCard,
34    /// Entes encarnados como proceso o nodo virtual. id↔pid bidireccional.
35    pub(in crate::graph) incarnated: HashMap<Ulid, Incarnated>,
36    pub(in crate::graph) by_pid: HashMap<i32, Ulid>,
37    /// Quién provee qué capacidad. Resuelve `requires` y `pick_invokable`.
38    pub(in crate::graph) providers: BTreeMap<Capability, BTreeSet<Ulid>>,
39    /// Tokens de capability emitidos. Revocables al morir el proveedor.
40    pub(in crate::graph) next_token: u64,
41    pub(in crate::graph) grants: HashMap<u64, GrantedCapability>,
42    /// Dispositivos del kernel presentes (devpath → última UEvent).
43    pub(in crate::graph) devices: HashMap<String, ente_kernel::UEvent>,
44    /// Cards genesis pendientes de instanciar (extraídas de la Semilla).
45    pub(in crate::graph) pending_genesis: Vec<EntityCard>,
46    /// Hijos directos por lineage. parent → [child, ...].
47    pub(in crate::graph) children: HashMap<Ulid, Vec<Ulid>>,
48    /// Conexiones del bus indexadas por la identidad anunciada y verificada
49    /// con SO_PEERCRED. El value es el extremo de escritura del writer task.
50    pub(in crate::graph) bus_connections: HashMap<Ulid, mpsc::Sender<BusMessage>>,
51    /// Invokes forwardeados pendientes de respuesta del proveedor.
52    pub(in crate::graph) pending_invokes: HashMap<u64, oneshot::Sender<BusResponse>>,
53    pub(in crate::graph) next_invoke_seq: u64,
54}
55
56pub(in crate::graph) struct Incarnated {
57    pub card: EntityCard,
58    pub pid: Option<Pid>,
59    /// Capacidades añadidas en runtime vía BusRequest::UpdateCapabilities.
60    /// La Card original es immutable; la "vista efectiva" del Ente es
61    /// `card.provides ∪ dynamic_provides`.
62    pub dynamic_provides: BTreeSet<Capability>,
63}
64
65pub(in crate::graph) struct GrantedCapability {
66    pub cap: Capability,
67    pub provider: Ulid,
68    pub holder: Ulid,
69    /// Instante en el que el grant deja de ser válido. El garbage collector
70    /// del cerebro purga grants con `Instant::now() > expires_at`.
71    pub expires_at: std::time::Instant,
72}
73
74/// TTL default para nuevos grants. Configurable por bus en el futuro.
75pub const DEFAULT_GRANT_TTL: std::time::Duration = std::time::Duration::from_secs(60);
76
77impl EnteGraph {
78    pub fn new(mut seed: EntityCard) -> Self {
79        // Extraemos genesis antes de almacenar la Semilla — evita duplicación
80        // en `incarnated[seed.id]`.
81        let pending_genesis = std::mem::take(&mut seed.genesis);
82        let mut g = Self {
83            seed: seed.clone(),
84            incarnated: HashMap::new(),
85            by_pid: HashMap::new(),
86            providers: BTreeMap::new(),
87            next_token: 1,
88            grants: HashMap::new(),
89            devices: HashMap::new(),
90            pending_genesis,
91            children: HashMap::new(),
92            bus_connections: HashMap::new(),
93            pending_invokes: HashMap::new(),
94            next_invoke_seq: 0,
95        };
96        // El Ente #0 se inscribe a sí mismo como proveedor de las capacidades
97        // que su Card declara — sólo así los hijos pueden requerirlas.
98        g.register_provider(&seed);
99        g.incarnated.insert(seed.id, Incarnated {
100            card: seed, pid: None,
101            dynamic_provides: BTreeSet::new(),
102        });
103        g
104    }
105
106    pub fn lookup_pid(&self, pid: Pid) -> Option<Ulid> {
107        self.by_pid.get(&pid.as_raw()).copied()
108    }
109
110    /// Acceso read-only a la Card de un Ente vivo. Usado por el cerebro
111    /// para hidratar `SubjectInfo` sin clonar todo el mapa.
112    pub fn peek_card(&self, id: &Ulid) -> Option<&EntityCard> {
113        self.incarnated.get(id).map(|i| &i.card)
114    }
115
116    /// Identidad de la Semilla. Usado como `requester` para spawns generados
117    /// por reglas auto-cristalizadas (única identidad con Capability::Spawn).
118    pub fn seed_id(&self) -> Ulid {
119        self.seed.id
120    }
121
122    /// Captura el estado live como snapshot serializable. Excluye la Semilla
123    /// (será re-sintetizada al restore con su seed_id preservado).
124    pub fn snapshot(&self) -> ente_snapshot::FractalSnapshot {
125        let entes: Vec<EntityCard> = self.incarnated.iter()
126            .filter(|(id, _)| **id != self.seed.id)
127            .map(|(_, inc)| inc.card.clone())
128            .collect();
129        ente_snapshot::FractalSnapshot {
130            version: ente_snapshot::SNAPSHOT_VERSION,
131            timestamp_ms: ente_snapshot::now_ms(),
132            seed_id: self.seed.id,
133            seed_label: self.seed.label.clone(),
134            entes,
135        }
136    }
137
138    pub(in crate::graph) fn register_provider(&mut self, card: &EntityCard) {
139        for cap in &card.provides {
140            self.providers.entry(cap.clone()).or_default().insert(card.id);
141        }
142    }
143
144    pub(in crate::graph) fn unregister_provider(&mut self, card: &EntityCard) {
145        for cap in &card.provides {
146            if let Some(set) = self.providers.get_mut(cap) {
147                set.remove(&card.id);
148            }
149        }
150        // Revocar grants emitidos por el Ente fallecido.
151        let revoked: Vec<u64> = self.grants.iter()
152            .filter(|(_, g)| g.provider == card.id)
153            .map(|(t, _)| *t)
154            .collect();
155        for t in revoked {
156            self.grants.remove(&t);
157        }
158    }
159
160    /// Quita una capacidad dinámica del índice de providers para un Ente
161    /// específico. Usado al recibir UpdateCapabilities con `removes`.
162    pub(in crate::graph) fn unregister_dynamic_cap(&mut self, ente_id: Ulid, cap: &Capability) {
163        if let Some(set) = self.providers.get_mut(cap) {
164            set.remove(&ente_id);
165        }
166    }
167
168    /// Inserta una capacidad dinámica al índice de providers para un Ente.
169    pub(in crate::graph) fn register_dynamic_cap(&mut self, ente_id: Ulid, cap: Capability) {
170        self.providers.entry(cap).or_default().insert(ente_id);
171    }
172
173    /// El Ente #0 (semilla) tiene todas sus capacidades declaradas. Otros
174    /// las tienen si su Card las declara o si poseen un grant vivo.
175    pub(in crate::graph) fn holder_has(&self, holder: Ulid, cap: &Capability) -> bool {
176        if holder == self.seed.id {
177            return self.seed.provides.contains(cap);
178        }
179        if let Some(inc) = self.incarnated.get(&holder) {
180            if inc.card.provides.contains(cap) { return true; }
181        }
182        self.grants.values().any(|g| g.holder == holder && &g.cap == cap)
183    }
184}